Index: src/main/groovy/org/gradle/api/plugins/scala/ScalaPluginConvention.groovy
===================================================================
--- src/main/groovy/org/gradle/api/plugins/scala/ScalaPluginConvention.groovy	Tue Nov 04 21:23:02 EST 2008
+++ src/main/groovy/org/gradle/api/plugins/scala/ScalaPluginConvention.groovy	Tue Nov 04 21:23:02 EST 2008
@@ -0,0 +1,39 @@
+package org.gradle.api.plugins.scala
+
+import org.gradle.api.Project
+import org.gradle.api.internal.plugins.PluginUtil
+import org.gradle.api.plugins.JavaPluginConvention
+
+class ScalaPluginConvention {
+    final Project project
+    List scalaSrcDirNames = []
+    List scalaTestSrcDirNames = []
+    List floatingScalaSrcDirs = []
+    List floatingScalaTestSrcDirs = []
+    String scalaDocDirName
+
+    ScalaPluginConvention(Project project, Map customValues) {
+        this.project = project
+        scalaSrcDirNames << 'main/scala'
+        scalaTestSrcDirNames << 'test/scala'
+        scalaDocDirName = 'scaladoc'
+        PluginUtil.applyCustomValues(project.convention, this, customValues)
+    }
+
+    List getScalaSrcDirs() {
+        scalaSrcDirNames.collect {new File(javaConvention.srcRoot, it)} + floatingScalaSrcDirs
+    }
+
+    List getScalaTestSrcDirs() {
+        scalaTestSrcDirNames.collect {new File(javaConvention.srcRoot, it)} + floatingScalaTestSrcDirs
+    }
+
+    File getScalaDocDir() {
+        return new File(javaConvention.docsDir, scalaDocDirName)
+    }
+
+    private JavaPluginConvention getJavaConvention() {
+        project.convention.plugins.java
+    }
+
+}
Index: src/main/groovy/org/gradle/api/plugins/scala/ScalaCompileOptions.groovy
===================================================================
--- src/main/groovy/org/gradle/api/plugins/scala/ScalaCompileOptions.groovy	Tue Nov 04 21:23:02 EST 2008
+++ src/main/groovy/org/gradle/api/plugins/scala/ScalaCompileOptions.groovy	Tue Nov 04 21:23:02 EST 2008
@@ -0,0 +1,124 @@
+package org.gradle.api.plugins.scala
+
+import org.gradle.api.tasks.compile.AbstractOptions
+
+class ScalaCompileOptions extends AbstractOptions {
+    /**
+     * Whether to use the fsc compile daemon.
+     */
+    boolean useCompileDaemon = false
+
+    // 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.
+     */
+    String daemonServer;
+
+    /**
+     * Fail the build on compilation errors.
+     */
+    boolean failOnError = true
+
+    /**
+     * Generate deprecation information.
+     */
+    boolean deprecation = true
+
+    /**
+     * Generate unchecked information.
+     */
+    boolean unchecked = true
+
+    /**
+     * Generate debugging information.
+     * Legal values: none, source, line, vars, notailcalls
+     */
+    String debugLevel
+
+    /**
+     * Run optimisations.
+     */
+    boolean optimize = false
+
+    /**
+     * Encoding of source files.
+     */
+    String encoding = null
+
+    /**
+     * 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)
+     */
+    String force = "never"
+
+    /**
+     * Specifies which backend to use.
+     * Legal values: 1.4, 1.5
+     */
+    String targetCompatibility = '1.5'
+
+    /**
+     * Additional parameters passed to the compiler.
+     * Each parameter must start with '-'.
+     */
+    List<String> additionalParameters
+
+    /**
+     * List files to be compiled.
+     */
+    boolean listFiles
+
+    /**
+     * Specifies the amount of logging.
+     * Legal values:  none, verbose, debug
+     */
+    String loggingLevel
+
+    /**
+     * Phases of the compiler to log.
+     * Legal values: namer, typer, pickler, uncurry, tailcalls, transmatch, explicitouter, erasure,
+     *               lambdalift, flatten, constructors, mixin, icode, jvm, terminal.
+     */
+    List<String> loggingPhases
+
+
+    Map fieldName2AntMap() {
+        [
+                failOnError: 'failonerror',
+                loggingLevel: 'logging',
+                loggingPhases: 'logPhase',
+                targetCompatibility: 'target',
+                optimize: 'optimise',
+                daemonServer: 'server',
+                listFiles: 'scalacDebugging',
+                debugLevel: 'debugInfo',
+                additionalParameters: 'addParams'
+        ]
+    }
+
+    Map fieldValue2AntMap() {
+        [
+                deprecation: {toBooleanString(deprecation)},
+                unchecked: {toBooleanString(unchecked)},
+                optimize: {toBooleanString(optimize)},
+                targetCompatibility: {"jvm-${targetCompatibility}"},
+                loggingPhases: {loggingPhases.isEmpty() ? ' ' : loggingPhases.join(',')},
+                additionalParameters: {additionalParameters.isEmpty() ? ' ' : additionalParameters.join(' ')},
+        ]
+    }
+
+    List excludedFieldsFromOptionMap() {
+        ['useCompileDaemon']
+    }
+
+    private String toBooleanString(value) {
+        return value ? 'on' : 'off'
+    }
+
+}
Index: src/toplevel/plugin.properties
===================================================================
--- src/toplevel/plugin.properties	(revision 1083)
+++ src/toplevel/plugin.properties	Tue Nov 04 18:55:35 EST 2008
@@ -1,3 +1,4 @@
 java=org.gradle.api.plugins.JavaPlugin
 groovy=org.gradle.api.plugins.GroovyPlugin
-war=org.gradle.api.plugins.WarPlugin
\ No newline at end of file
+war=org.gradle.api.plugins.WarPlugin
+scala=org.gradle.api.plugins.scala.ScalaPlugin
Index: src/test/groovy/org/gradle/api/plugins/scala/ScalaPluginTest.groovy
===================================================================
--- src/test/groovy/org/gradle/api/plugins/scala/ScalaPluginTest.groovy	Tue Nov 04 21:46:13 EST 2008
+++ src/test/groovy/org/gradle/api/plugins/scala/ScalaPluginTest.groovy	Tue Nov 04 21:46:13 EST 2008
@@ -0,0 +1,44 @@
+package org.gradle.api.plugins.scala
+
+import org.gradle.api.Project
+import org.gradle.api.internal.project.PluginRegistry
+import org.gradle.api.plugins.JavaPlugin
+import org.gradle.util.HelperUtil
+import static org.gradle.util.WrapUtil.toSet
+import static org.hamcrest.Matchers.*
+import static org.junit.Assert.assertFalse
+import static org.junit.Assert.assertThat
+import org.junit.Test
+
+public class ScalaPluginTest {
+
+    @Test
+    public void testApply() {
+        Project project = HelperUtil.createRootProject(new File('path', 'root'))
+        ScalaPlugin scalaPlugin = new ScalaPlugin()
+        scalaPlugin.apply(project, new PluginRegistry())
+
+        def configuration = project.dependencies.configurations[JavaPlugin.COMPILE]
+        assertFalse(configuration.visible)
+        assertFalse(configuration.transitive)
+
+        configuration = project.dependencies.configurations[ScalaPlugin.SCALA_TOOLS]
+        assertThat(configuration.extendsFrom, equalTo(toSet()))
+        assertFalse(configuration.visible)
+        assertFalse(configuration.transitive)
+
+        def task = project.tasks[JavaPlugin.COMPILE]
+        assertThat(task, instanceOf(ScalaCompile.class))
+        assertThat(task.srcDirs, hasItems(project.convention.plugins.java.srcDirs as Object[]))
+        assertThat(task.scalaSourceDirs, hasItems(project.convention.plugins.scala.scalaSrcDirs as Object[]))
+
+        task = project.tasks[JavaPlugin.TEST_COMPILE]
+        assertThat(task, instanceOf(ScalaCompile.class))
+        assertThat(task.srcDirs, hasItems(project.convention.plugins.java.testSrcDirs as Object[]))
+        assertThat(task.scalaSourceDirs, hasItems(project.convention.plugins.scala.scalaTestSrcDirs as Object[]))
+
+        task = project.tasks[ScalaPlugin.SCALA_DOC_TASK]
+        assertThat(task, instanceOf(ScalaDoc.class))
+        assertThat(task.destinationDir, equalTo(project.convention.plugins.scala.scalaDocDir))
+    }
+}
Index: src/samples/scalaproject/settings.gradle
===================================================================
--- src/samples/scalaproject/settings.gradle	Tue Nov 04 18:53:02 EST 2008
+++ src/samples/scalaproject/settings.gradle	Tue Nov 04 18:53:02 EST 2008
@@ -0,0 +1,1 @@
+include "api"
Index: src/main/groovy/org/gradle/api/plugins/scala/ScalaDefine.java
===================================================================
--- src/main/groovy/org/gradle/api/plugins/scala/ScalaDefine.java	Tue Nov 04 20:20:11 EST 2008
+++ src/main/groovy/org/gradle/api/plugins/scala/ScalaDefine.java	Tue Nov 04 20:20:11 EST 2008
@@ -0,0 +1,69 @@
+package org.gradle.api.plugins.scala;
+
+import org.gradle.api.DependencyManager;
+import org.gradle.api.Project;
+import org.gradle.api.Task;
+import org.gradle.api.TaskAction;
+import org.gradle.api.internal.ConventionTask;
+
+import java.io.File;
+import java.util.List;
+
+public class ScalaDefine extends ConventionTask {
+
+    private DependencyManager dependencyManager;
+
+    private AntScalaDefine antScalaDefine;
+
+    public ScalaDefine(Project project, String name) {
+        super(project, name);
+        doFirst(new TaskAction() {
+            public void execute(Task task) {
+                defineAntTasks(task);
+            }
+        });
+    }
+
+    public AntScalaDefine getAntScalaDefine() {
+        if (antScalaDefine == null) {
+            antScalaDefine = new AntScalaDefine(getAnt(), getClasspath());
+        }
+        return antScalaDefine;
+    }
+
+    public void setAntScalaDefine(AntScalaDefine antScalaDefine) {
+        this.antScalaDefine = antScalaDefine;
+    }
+
+    /**
+     * <p>Returns the classpath used to define the Scala ant tasks.</p>
+     *
+     * @return The list of files containing scala tools libraries.
+     */
+    public List<File> getClasspath() {
+        return getDependencyManager().resolveTask(getName());
+    }
+
+    /**
+     * <p>Returns the {@link DependencyManager} which this task uses to resolve the classpath
+     * to define the scala ant tasks.</p>
+     *
+     * @return The dependency manager.
+     */
+    public DependencyManager getDependencyManager() {
+        return (DependencyManager) conv(dependencyManager, "dependencyManager");
+    }
+
+    /**
+     * <p>Sets the {@link DependencyManager} which this task uses to resolve the classpath
+     * to define the scala ant tasks.</p>
+     */
+    public void setDependencyManager(DependencyManager dependencyManager) {
+        this.dependencyManager = dependencyManager;
+    }
+
+    protected void defineAntTasks(Task task) {
+        getAntScalaDefine().execute();
+    }
+
+}
Index: src/test/groovy/org/gradle/api/plugins/scala/ScalaPluginConventionTest.groovy
===================================================================
--- src/test/groovy/org/gradle/api/plugins/scala/ScalaPluginConventionTest.groovy	Tue Nov 04 21:46:13 EST 2008
+++ src/test/groovy/org/gradle/api/plugins/scala/ScalaPluginConventionTest.groovy	Tue Nov 04 21:46:13 EST 2008
@@ -0,0 +1,65 @@
+package org.gradle.api.plugins.scala
+
+import org.gradle.api.plugins.AbstractPluginConventionTest
+import org.gradle.api.plugins.JavaPluginConvention
+import static org.hamcrest.Matchers.equalTo
+import static org.junit.Assert.assertEquals
+import static org.junit.Assert.assertThat
+import org.junit.Before
+import org.junit.Test
+
+public class ScalaPluginConventionTest extends AbstractPluginConventionTest {
+    private ScalaPluginConvention scalaConvention
+    private JavaPluginConvention javaConvention
+
+    Class getType() {
+        ScalaPluginConvention
+    }
+
+    Map getCustomValues() {
+        [scalaSrcDirNames: ['newSourceRootName']]
+    }
+
+    @Before public void setUp() {
+        super.setUp()
+        javaConvention = new JavaPluginConvention(project, [:])
+        project.convention.plugins.java = javaConvention
+        scalaConvention = new ScalaPluginConvention(project, [:])
+    }
+
+    @Test public void testScalaConvention() {
+        assertEquals(['main/scala'], scalaConvention.scalaSrcDirNames)
+        assertEquals(['test/scala'], scalaConvention.scalaTestSrcDirNames)
+        assertEquals([], scalaConvention.floatingScalaSrcDirs)
+        assertEquals([], scalaConvention.floatingScalaTestSrcDirs)
+    }
+
+    @Test
+    public void testScalaDefaultDirs() {
+        checkScalaDirs(project.srcRootName)
+    }
+
+    @Test
+    public void testScalaDynamicDirs() {
+        project.srcRootName = 'mysrc'
+        project.buildDirName = 'mybuild'
+        checkScalaDirs(project.srcRootName)
+    }
+
+    @Test
+    public void testScalaDocDirUsesJavaConventionToDetermineDocsDir() {
+        assertThat(scalaConvention.scalaDocDir, equalTo(new File(javaConvention.docsDir, "scaladoc")))
+
+        scalaConvention.scalaDocDirName = "other-dir"
+        assertThat(scalaConvention.scalaDocDir, equalTo(new File(javaConvention.docsDir, "other-dir")))
+    }
+
+    private void checkScalaDirs(String srcRootName) {
+        scalaConvention.floatingScalaSrcDirs << 'someScalaSrcDir' as File
+        scalaConvention.floatingScalaTestSrcDirs << 'someScalaTestSrcDir' as File
+        assertEquals([new File(project.srcRoot, scalaConvention.scalaSrcDirNames[0])] + scalaConvention.floatingScalaSrcDirs,
+                scalaConvention.scalaSrcDirs)
+        assertEquals([new File(project.srcRoot, scalaConvention.scalaTestSrcDirNames[0])] + scalaConvention.floatingScalaTestSrcDirs,
+                scalaConvention.scalaTestSrcDirs)
+    }
+}
Index: src/main/groovy/org/gradle/api/plugins/scala/AntScalaDefine.groovy
===================================================================
--- src/main/groovy/org/gradle/api/plugins/scala/AntScalaDefine.groovy	Tue Nov 04 21:23:02 EST 2008
+++ src/main/groovy/org/gradle/api/plugins/scala/AntScalaDefine.groovy	Tue Nov 04 21:23:02 EST 2008
@@ -0,0 +1,29 @@
+package org.gradle.api.plugins.scala
+
+import org.slf4j.Logger
+import org.slf4j.LoggerFactory
+
+public class AntScalaDefine {
+    private static Logger logger = LoggerFactory.getLogger(AntScalaDefine)
+
+    private final AntBuilder ant
+    private final List<File> scalaToolsClasspath
+
+    def AntScalaDefine(AntBuilder ant, List<File> scalaToolsClasspath) {
+        this.ant = ant
+        this.scalaToolsClasspath = scalaToolsClasspath
+    }
+
+    void execute() {
+        if (logger.isInfoEnabled()) {
+            logger.info("Defining Scala ant tasks with classpath ${scalaToolsClasspath}\n")
+        }
+
+        ant.taskdef(resource: 'scala/tools/ant/antlib.xml', loaderRef: 'scala') {
+            scalaToolsClasspath.each {file ->
+                classpath(location: file)
+            }
+        }
+    }
+
+}
Index: src/main/groovy/org/gradle/api/plugins/scala/AntScalaDoc.groovy
===================================================================
--- src/main/groovy/org/gradle/api/plugins/scala/AntScalaDoc.groovy	Tue Nov 04 21:23:02 EST 2008
+++ src/main/groovy/org/gradle/api/plugins/scala/AntScalaDoc.groovy	Tue Nov 04 21:23:02 EST 2008
@@ -0,0 +1,73 @@
+package org.gradle.api.plugins.scala
+
+import org.slf4j.Logger
+import org.slf4j.LoggerFactory
+
+class AntScalaDoc {
+    private static Logger logger = LoggerFactory.getLogger(AntScalaDoc)
+
+    private final AntBuilder ant
+    private final List<File> bootclasspathFiles
+    private final List<File> extensionDirs
+
+    def AntScalaDoc(AntBuilder ant) {
+        this.ant = ant
+        this.bootclasspathFiles = []
+        this.extensionDirs = []
+    }
+
+    def AntScalaDoc(AntBuilder ant, List<File> bootclasspathFiles, List<File> extensionDirs) {
+        this.ant = ant
+        this.bootclasspathFiles = bootclasspathFiles
+        this.extensionDirs = extensionDirs
+    }
+
+    void execute(List<File> sourceDirs, List<String> includes, List<String> excludes, File targetDir,
+                 List<File> classpathFiles, ScalaDocOptions docOptions) {
+
+        ant.mkdir(dir: targetDir.absolutePath)
+
+        Map options = ['destDir': targetDir] + docOptions.optionMap()
+        if (logger.isInfoEnabled()) {
+            StringBuilder builder = new StringBuilder()
+            builder.append("Generating Scaladoc from source ${sourceDirs}\n")
+            builder.append("    options = ${options}\n")
+            builder.append("    classpath = ${classpathFiles}\n")
+            if (includes) {
+                builder.append("    includes = ${includes}\n")
+            }
+            if (excludes) {
+                builder.append("    excludes = ${excludes}\n")
+            }
+            if (bootclasspathFiles) {
+                builder.append("    bootclasspath = ${bootclasspathFiles}\n")
+            }
+            if (extensionDirs) {
+                builder.append("    extDirs = ${extensionDirs}\n")
+            }
+            logger.info(builder.toString())
+        }
+
+        ant.scaladoc(options) {
+            sourceDirs.each {dir ->
+                src(location: dir)
+            }
+            bootclasspathFiles.each {file ->
+                bootclasspath(location: file)
+            }
+            extensionDirs.each {dir ->
+                extdirs(location: dir)
+            }
+            classpathFiles.each {file ->
+                classpath(location: file)
+            }
+            includes.each {pattern ->
+                include(name: pattern)
+            }
+            excludes.each {pattern ->
+                exclude(name: pattern)
+            }
+        }
+    }
+
+}
Index: src/main/groovy/org/gradle/api/plugins/scala/AntScalaCompile.groovy
===================================================================
--- src/main/groovy/org/gradle/api/plugins/scala/AntScalaCompile.groovy	Tue Nov 04 21:23:02 EST 2008
+++ src/main/groovy/org/gradle/api/plugins/scala/AntScalaCompile.groovy	Tue Nov 04 21:23:02 EST 2008
@@ -0,0 +1,74 @@
+package org.gradle.api.plugins.scala
+
+import org.slf4j.Logger
+import org.slf4j.LoggerFactory
+
+class AntScalaCompile {
+    private static Logger logger = LoggerFactory.getLogger(AntScalaCompile)
+
+    private final AntBuilder ant
+    private final List<File> bootclasspathFiles
+    private final List<File> extensionDirs
+
+    def AntScalaCompile(AntBuilder ant) {
+        this.ant = ant
+        this.bootclasspathFiles = []
+        this.extensionDirs = []
+    }
+
+    def AntScalaCompile(AntBuilder ant, List<File> bootclasspathFiles, List<File> extensionDirs) {
+        this.ant = ant
+        this.bootclasspathFiles = bootclasspathFiles
+        this.extensionDirs = extensionDirs
+    }
+
+    void execute(List<File> sourceDirs, List<String> includes, List<String> excludes, File targetDir,
+                 List<File> classpathFiles, ScalaCompileOptions compileOptions) {
+
+        ant.mkdir(dir: targetDir.absolutePath)
+
+        Map options = ['destDir': targetDir] + compileOptions.optionMap()
+        String taskName = compileOptions.useCompileDaemon ? 'fsc' : 'scalac'
+        if (logger.isInfoEnabled()) {
+            StringBuilder builder = new StringBuilder()
+            builder.append("Compiling Scala with ${taskName} from source ${sourceDirs}\n")
+            builder.append("    options = ${options}\n")
+            builder.append("    classpath = ${classpathFiles}\n")
+            if (includes) {
+                builder.append("    includes = ${includes}\n")
+            }
+            if (excludes) {
+                builder.append("    excludes = ${excludes}\n")
+            }
+            if (bootclasspathFiles) {
+                builder.append("    bootclasspath = ${bootclasspathFiles}\n")
+            }
+            if (extensionDirs) {
+                builder.append("    extDirs = ${extensionDirs}\n")
+            }
+            logger.info(builder.toString())
+        }
+
+        ant."${taskName}"(options) {
+            sourceDirs.each {dir ->
+                src(location: dir)
+            }
+            bootclasspathFiles.each {file ->
+                bootclasspath(location: file)
+            }
+            extensionDirs.each {dir ->
+                extdirs(location: dir)
+            }
+            classpathFiles.each {file ->
+                classpath(location: file)
+            }
+            includes.each {pattern ->
+                include(name: pattern)
+            }
+            excludes.each {pattern ->
+                exclude(name: pattern)
+            }
+        }
+    }
+
+}
Index: src/samples/scalaproject/build.gradle
===================================================================
--- src/samples/scalaproject/build.gradle	Tue Nov 04 21:52:56 EST 2008
+++ src/samples/scalaproject/build.gradle	Tue Nov 04 21:52:56 EST 2008
@@ -0,0 +1,8 @@
+project.group = 'org.gradle'
+project.version = '1.0'
+
+subprojects {
+    dependencies {
+        addFlatDirResolver('lib', "$rootDir/lib")
+    }
+}
Index: src/main/groovy/org/gradle/api/plugins/scala/ScalaCompile.java
===================================================================
--- src/main/groovy/org/gradle/api/plugins/scala/ScalaCompile.java	Tue Nov 04 21:23:02 EST 2008
+++ src/main/groovy/org/gradle/api/plugins/scala/ScalaCompile.java	Tue Nov 04 21:23:02 EST 2008
@@ -0,0 +1,107 @@
+package org.gradle.api.plugins.scala;
+
+import org.gradle.api.InvalidUserDataException;
+import org.gradle.api.Project;
+import org.gradle.api.Task;
+import org.gradle.api.TaskAction;
+import org.gradle.api.tasks.compile.Compile;
+import org.gradle.util.GUtil;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Task to perform scala compilation.
+ */
+public class ScalaCompile extends Compile {
+
+    /**
+     * Directories containing scala source files.
+     */
+    private List<File> scalaSourceDirs;
+
+    /**
+     * Include pattern for which scala files should be compiled (e.g. '**&#2F;org/gradle/package1/')).
+     * This pattern is added as an nested include the scalac task.
+     */
+    private List<String> scalaIncludes;
+
+    /**
+     * Exclude pattern for which files should be compiled (e.g. '**&#2F;org/gradle/package2/A*.java').
+     * This pattern is added as an nested exclude the scalac task.
+     */
+    private List<String> scalaExcludes;
+
+    private AntScalaCompile antScalaCompile;
+
+    private ScalaCompileOptions scalaCompileOptions = new ScalaCompileOptions();
+
+    public ScalaCompile(Project project, String name) {
+        super(project, name);
+        setActions(new ArrayList<TaskAction>());
+        doFirst(new TaskAction() {
+            public void execute(Task task) {
+                compile(task);
+            }
+        });
+    }
+
+    public AntScalaCompile getAntScalaCompile() {
+        if (antScalaCompile == null) {
+            antScalaCompile = new AntScalaCompile(getAnt());
+        }
+        return antScalaCompile;
+    }
+
+    public void setAntScalaCompile(AntScalaCompile antScalaCompile) {
+        this.antScalaCompile = antScalaCompile;
+    }
+
+    public List getScalaSourceDirs() {
+        return (List) conv(scalaSourceDirs, "scalaSourceDirs");
+    }
+
+    public void setScalaSourceDirs(List<File> scalaSourceDirs) {
+        this.scalaSourceDirs = scalaSourceDirs;
+    }
+
+    public List<String> getScalaIncludes() {
+        return scalaIncludes;
+    }
+
+    public void setScalaIncludes(List<String> scalaIncludes) {
+        this.scalaIncludes = scalaIncludes;
+    }
+
+    public List<String> getScalaExcludes() {
+        return scalaExcludes;
+    }
+
+    public void setScalaExcludes(List<String> scalaExcludes) {
+        this.scalaExcludes = scalaExcludes;
+    }
+
+    public ScalaCompileOptions getScalaCompileOptions() {
+        return scalaCompileOptions;
+    }
+
+    public void setScalaCompileOptions(ScalaCompileOptions scalaCompileOptions) {
+        this.scalaCompileOptions = scalaCompileOptions;
+    }
+
+    @Override
+    protected void compile(Task task) {
+
+        if (!GUtil.isTrue(getTargetCompatibility())) {
+            throw new InvalidUserDataException("The targetCompatibility must be set!");
+        }
+
+        List<File> existingSourceDirs = existentDirsFilter.checkDestDirAndFindExistingDirsAndThrowStopActionIfNone(
+                getDestinationDir(), getScalaSourceDirs());
+
+        getAntScalaCompile().execute(existingSourceDirs, getScalaIncludes(), getScalaExcludes(), getDestinationDir(),
+                getClasspath(), getScalaCompileOptions());
+    }
+
+}
Index: src/samples/scalaproject/api/src/test/scala/org/gradle/sample/impl/PersonImplTest.scala
===================================================================
--- src/samples/scalaproject/api/src/test/scala/org/gradle/sample/impl/PersonImplTest.scala	Tue Nov 04 21:54:00 EST 2008
+++ src/samples/scalaproject/api/src/test/scala/org/gradle/sample/impl/PersonImplTest.scala	Tue Nov 04 21:54:00 EST 2008
@@ -0,0 +1,14 @@
+package org.gradle.sample.impl
+
+import _root_.junit.framework.TestCase
+import _root_.org.gradle.sample.api.Person
+
+class PersonImplTest extends TestCase {
+
+  // FIXME: use a Scala test framework to run a test
+  def testCanCreatePersonImpl(): Unit = {
+    def person: Person = new PersonImpl(List("bob", "smith"))
+    person
+  }
+
+}
Index: src/samples/scalaproject/api/src/main/scala/org/gradle/sample/api/Person.scala
===================================================================
--- src/samples/scalaproject/api/src/main/scala/org/gradle/sample/api/Person.scala	Tue Nov 04 18:50:02 EST 2008
+++ src/samples/scalaproject/api/src/main/scala/org/gradle/sample/api/Person.scala	Tue Nov 04 18:50:02 EST 2008
@@ -0,0 +1,9 @@
+package org.gradle.sample.api
+
+/**
+ * Defines the interface for a person.
+ */
+abstract trait Person
+{
+  def names: List[String]
+}
Index: src/samples/scalaproject/api/src/main/scala/org/gradle/sample/impl/PersonImpl.scala
===================================================================
--- src/samples/scalaproject/api/src/main/scala/org/gradle/sample/impl/PersonImpl.scala	Tue Nov 04 18:51:00 EST 2008
+++ src/samples/scalaproject/api/src/main/scala/org/gradle/sample/impl/PersonImpl.scala	Tue Nov 04 18:51:00 EST 2008
@@ -0,0 +1,11 @@
+package org.gradle.sample.impl
+
+import org.gradle.sample.api.Person
+
+/**
+ * Immutable implementation of  { @link Person }.
+ */
+class PersonImpl(val names: List[String]) extends Person
+{
+
+}
Index: src/main/groovy/org/gradle/api/plugins/scala/ScalaDoc.java
===================================================================
--- src/main/groovy/org/gradle/api/plugins/scala/ScalaDoc.java	Tue Nov 04 21:25:29 EST 2008
+++ src/main/groovy/org/gradle/api/plugins/scala/ScalaDoc.java	Tue Nov 04 21:25:29 EST 2008
@@ -0,0 +1,144 @@
+package org.gradle.api.plugins.scala;
+
+import org.gradle.api.DependencyManager;
+import org.gradle.api.Project;
+import org.gradle.api.Task;
+import org.gradle.api.TaskAction;
+import org.gradle.api.internal.ConventionTask;
+import org.gradle.api.tasks.util.ExistingDirsFilter;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Task to perform scala compilation.
+ */
+public class ScalaDoc extends ConventionTask {
+
+    /**
+     * The directory to put the generated documentation.
+     */
+    private File destinationDir;
+
+    /**
+     * Directories containing input scala source files.
+     */
+    private List<File> scalaSourceDirs;
+
+    /**
+     * Include pattern for which scala files should be compiled (e.g. '**&#2F;org/gradle/package1/')).
+     */
+    private List<String> scalaIncludes;
+
+    /**
+     * Exclude pattern for which scala files should be compiled (e.g. '**&#2F;org/gradle/package2/A*.java').
+     */
+    private List<String> scalaExcludes;
+
+    private DependencyManager dependencyManager;
+
+    private AntScalaDoc antScalaDoc;
+
+    private ScalaDocOptions scalaDocOptions = new ScalaDocOptions();
+
+    private ExistingDirsFilter existentDirsFilter = new ExistingDirsFilter();
+
+    public ScalaDoc(Project project, String name) {
+        super(project, name);
+        setActions(new ArrayList<TaskAction>());
+        doFirst(new TaskAction() {
+            public void execute(Task task) {
+                generate(task);
+            }
+        });
+    }
+
+    public AntScalaDoc getAntScalaDoc() {
+        if (antScalaDoc == null) {
+            antScalaDoc = new AntScalaDoc(getAnt());
+        }
+        return antScalaDoc;
+    }
+
+    public void setAntScalaDoc(AntScalaDoc antScalaDoc) {
+        this.antScalaDoc = antScalaDoc;
+    }
+
+    public File getDestinationDir() {
+        return (File) conv(destinationDir, "destinationDir");
+    }
+
+    public void setDestinationDir(File destinationDir) {
+        this.destinationDir = destinationDir;
+    }
+
+    public List<File> getScalaSourceDirs() {
+        return (List<File>) conv(scalaSourceDirs, "scalaSourceDirs");
+    }
+
+    public void setScalaSourceDirs(List<File> scalaSourceDirs) {
+        this.scalaSourceDirs = scalaSourceDirs;
+    }
+
+    /**
+     * <p>Returns the classpath to use to locate classes referenced by the documented source.</p>
+     *
+     * @return The classpath.
+     */
+    public List<File> getClasspath() {
+        return getDependencyManager().resolveTask(getName());
+    }
+
+    /**
+     * <p>Returns the {@link DependencyManager} which this task uses to resolve the classpath to use when generating the
+     * documentation.</p>
+     *
+     * @return The dependency manager.
+     */
+    public DependencyManager getDependencyManager() {
+        return (DependencyManager) conv(dependencyManager, "dependencyManager");
+    }
+
+    /**
+     * <p>Sets the {@link DependencyManager} which this task uses to resolve the classpath to use when generating the
+     * documentation.</p>
+     */
+    public void setDependencyManager(DependencyManager dependencyManager) {
+        this.dependencyManager = dependencyManager;
+    }
+
+    public List<String> getScalaIncludes() {
+        return (List<String>) conv(scalaIncludes, "scalaIncludes");
+    }
+
+    public void setScalaIncludes(List<String> scalaIncludes) {
+        this.scalaIncludes = scalaIncludes;
+    }
+
+    public List<String> getScalaExcludes() {
+        return (List<String>) conv(scalaExcludes, "scalaExcludes");
+    }
+
+    public void setScalaExcludes(List<String> scalaExcludes) {
+        this.scalaExcludes = scalaExcludes;
+    }
+
+    public ScalaDocOptions getScalaDocOptions() {
+        return scalaDocOptions;
+    }
+
+    public void setScalaDocOptions(ScalaDocOptions scalaDocOptions) {
+        this.scalaDocOptions = scalaDocOptions;
+    }
+
+    protected void generate(Task task) {
+
+        List<File> existingSourceDirs = existentDirsFilter.checkDestDirAndFindExistingDirsAndThrowStopActionIfNone(
+                getDestinationDir(), getScalaSourceDirs());
+
+        getAntScalaDoc().execute(existingSourceDirs, getScalaIncludes(), getScalaExcludes(), getDestinationDir(),
+                getClasspath(), getScalaDocOptions());
+    }
+
+}
Index: src/main/groovy/org/gradle/api/plugins/scala/ScalaDocOptions.groovy
===================================================================
--- src/main/groovy/org/gradle/api/plugins/scala/ScalaDocOptions.groovy	Tue Nov 04 21:23:02 EST 2008
+++ src/main/groovy/org/gradle/api/plugins/scala/ScalaDocOptions.groovy	Tue Nov 04 21:23:02 EST 2008
@@ -0,0 +1,77 @@
+package org.gradle.api.plugins.scala
+
+import org.gradle.api.tasks.compile.AbstractOptions
+
+public class ScalaDocOptions extends AbstractOptions {
+
+    /**
+     * Generate deprecation information.
+     */
+    boolean deprecation = true
+
+    /**
+     * Generate unchecked information.
+     */
+    boolean unchecked = true
+
+    /**
+     * Text to appear in the window title.
+     */
+    String windowTitle
+
+    /**
+     * Html text to appear in the main frame title.
+     */
+    String docTitle
+
+    /**
+     * Html text to appear in the header for each page.
+     */
+    String header
+
+    /**
+     * Html text to appear in the footer for each page.
+     */
+    String footer
+
+    /**
+     * Html text to appear in the top text for each page.
+     */
+    String top
+
+    /**
+     * Html text to appear in the bottom text for each page.
+     */
+    String bottom
+
+    /**
+     * Style sheet to override default style.
+     */
+    File styleSheet
+
+    /**
+     * Additional parameters passed to the compiler.
+     * Each parameter must start with '-'.
+     */
+    List<String> additionalParameters
+
+
+    Map fieldName2AntMap() {
+        [
+                additionalParameters: 'addParams'
+        ]
+    }
+
+    Map fieldValue2AntMap() {
+        [
+                deprecation: {toBooleanString(deprecation)},
+                unchecked: {toBooleanString(unchecked)},
+                additionalParameters: {additionalParameters.isEmpty() ? ' ' : additionalParameters.join(' ')},
+        ]
+    }
+
+    private String toBooleanString(value) {
+        return value ? 'on' : 'off'
+    }
+
+}
Index: src/samples/scalaproject/api/build.gradle
===================================================================
--- src/samples/scalaproject/api/build.gradle	Tue Nov 04 21:50:37 EST 2008
+++ src/samples/scalaproject/api/build.gradle	Tue Nov 04 21:50:37 EST 2008
@@ -0,0 +1,14 @@
+usePlugin('scala')
+
+targetCompatibility = 1.5
+
+dependencies {
+    addMavenStyleRepo('scala-releases', 'http://scala-tools.org/repo-releases/')
+
+    // Libraries needed to run the scala tools
+    scalaTools 'org.scala-lang:scala-compiler:2.7.1'
+    scalaTools 'org.scala-lang:scala-library:2.7.1'
+
+    compile 'org.scala-lang:scala-library:2.7.1'
+    testCompile ":junit:4.4"
+}
Index: src/main/groovy/org/gradle/api/plugins/scala/ScalaPlugin.java
===================================================================
--- src/main/groovy/org/gradle/api/plugins/scala/ScalaPlugin.java	Tue Nov 04 21:33:24 EST 2008
+++ src/main/groovy/org/gradle/api/plugins/scala/ScalaPlugin.java	Tue Nov 04 21:33:24 EST 2008
@@ -0,0 +1,110 @@
+package org.gradle.api.plugins.scala;
+
+import org.gradle.api.Project;
+import org.gradle.api.Task;
+import org.gradle.api.internal.project.PluginRegistry;
+import org.gradle.api.plugins.Convention;
+import org.gradle.api.plugins.DefaultConventionsToPropertiesMapping;
+import org.gradle.api.plugins.JavaPlugin;
+import org.gradle.api.tasks.ConventionValue;
+import org.gradle.api.tasks.compile.Compile;
+import org.gradle.api.tasks.testing.ForkMode;
+import org.gradle.api.tasks.testing.Test;
+import org.gradle.util.GUtil;
+
+import java.util.Arrays;
+import java.util.Map;
+
+public class ScalaPlugin extends JavaPlugin {
+    // public configurations
+    public static final String SCALA_TOOLS = "scalaTools";
+
+    // tasks
+    private static final String SCALA_DOC_TASK = "scalaDoc";
+    private static final String SCALA_DEFINE_TASK = "defineScalaAnt";
+
+    @Override
+    public void apply(Project project, PluginRegistry pluginRegistry, Map customValues) {
+        pluginRegistry.apply(JavaPlugin.class, project, pluginRegistry, customValues);
+        ScalaPluginConvention scalaPluginConvention = new ScalaPluginConvention(project, customValues);
+        project.getConvention().getPlugins().put("scala", scalaPluginConvention);
+
+        configureDefine(project);
+        configureCompile(project);
+        configureTestCompile(project);
+
+        ((Test) project.task(TEST)).getOptions().getForkOptions().setForkMode(ForkMode.ONCE);
+
+        configureScaladoc(project);
+
+        project.getDependencies().addConfiguration(SCALA_TOOLS).setVisible(false).setTransitive(false);
+        project.getDependencies().linkConfWithTask(SCALA_TOOLS, SCALA_DEFINE_TASK);
+        project.getDependencies().linkConfWithTask(COMPILE, SCALA_DOC_TASK);
+    }
+
+    private void configureDefine(Project project) {
+        ScalaDefine defineTask = (ScalaDefine) project.createTask(
+                GUtil.map("type", ScalaDefine.class, "dependsOn", RESOURCES, "overwrite", true), SCALA_DEFINE_TASK);
+        defineTask.conventionMapping(GUtil.map(
+                "dependencyManager", new ConventionValue() {
+                    public Object getValue(Convention convention, Task task) {
+                        return task.getProject().getDependencies();
+                    }
+                }
+        ));
+    }
+
+    private void configureScaladoc(Project project) {
+        ScalaDoc docTask = (ScalaDoc) project.createTask(
+                GUtil.map("type", ScalaDoc.class, "dependsOn", SCALA_DEFINE_TASK), SCALA_DOC_TASK);
+        docTask.conventionMapping(GUtil.map(
+                "dependencyManager", new ConventionValue() {
+                    public Object getValue(Convention convention, Task task) {
+                        return task.getProject().getDependencies();
+                    }
+                },
+                "destinationDir", new ConventionValue() {
+                    public Object getValue(Convention convention, Task task) {
+                        return scala(convention).getScalaDocDir();
+                    }
+                },
+                "scalaSourceDirs", new ConventionValue() {
+                    public Object getValue(Convention convention, Task task) {
+                        return scala(convention).getScalaSrcDirs();
+                    }
+                }
+        ));
+    }
+
+    private void configureCompile(Project project) {
+        ScalaCompile compileTask = (ScalaCompile) project.createTask(
+                GUtil.map("type", ScalaCompile.class, "dependsOn", Arrays.asList(SCALA_DEFINE_TASK, RESOURCES), "overwrite", true), COMPILE);
+        configureCompile(compileTask, DefaultConventionsToPropertiesMapping.COMPILE);
+        compileTask.conventionMapping(GUtil.map(
+                "scalaSourceDirs", new ConventionValue() {
+                    public Object getValue(Convention convention, Task task) {
+                        return scala(convention).getScalaSrcDirs();
+                    }
+                }
+        ));
+    }
+
+    private void configureTestCompile(Project project) {
+        ScalaCompile testCompileTask = (ScalaCompile) project.createTask(
+                GUtil.map("type", ScalaCompile.class, "dependsOn", Arrays.asList(SCALA_DEFINE_TASK, TEST_RESOURCES), "overwrite", true), TEST_COMPILE);
+        configureTestCompile(testCompileTask, (Compile) project.task(COMPILE),
+                DefaultConventionsToPropertiesMapping.TEST_COMPILE);
+        testCompileTask.conventionMapping(GUtil.map(
+                "scalaSourceDirs", new ConventionValue() {
+                    public Object getValue(Convention convention, Task task) {
+                        return scala(convention).getScalaTestSrcDirs();
+                    }
+                }
+        ));
+    }
+
+    private ScalaPluginConvention scala(Convention convention) {
+        return (ScalaPluginConvention) convention.getPlugins().get("scala");
+    }
+
+}
