/*
 * Copyright 2010 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT 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.build.Version
import org.gradle.build.Install
import org.gradle.build.Git
import org.gradle.build.Releases

/**
 * For building Gradle you usually don't need to specify any properties. Only certain functionality of the Gradle requires
 * setting certain properties. Those properties can be set in the gradle.properties file in the the gradle user home. The
 * following properties can be set:
 *
 * Uploading distributions to Gradle's release and snapshot repository at codehaus: artifactoryUserName, artifactoryUserPassword
 * Uploading the userguide and the javadoc to Gradle's website: websiteScpUserName, websiteScpUserPassword
 * Using the build to create a new distribution and install it on the local machine: gradle_installPath
 */

releases = new Releases(project(':core').file('src/releases.xml'), project)
version = new Version(project)

defaultTasks 'assemble'

apply plugin: 'java-base'

archivesBaseName = 'gradle'

libraries = [
    ant: dependencies.module('org.apache.ant:ant:1.8.2') {
        dependency 'org.apache.ant:ant-launcher:1.8.2@jar'
    },
    asm: 'asm:asm-all:3.3.1@jar',
    commons_io: 'commons-io:commons-io:1.4@jar',
    commons_lang: 'commons-lang:commons-lang:2.6@jar',
    commons_collections: 'commons-collections:commons-collections:3.2.1@jar',
    ivy: 'org.apache.ivy:ivy:2.2.0@jar',
    jcip: "net.jcip:jcip-annotations:1.0@jar",
]

// Logging
libraries.slf4j_api = 'org.slf4j:slf4j-api:1.6.2@jar'
libraries.jcl_to_slf4j = dependencies.module('org.slf4j:jcl-over-slf4j:1.6.2') {
    dependency libraries.slf4j_api
}
libraries.jul_to_slf4j = dependencies.module('org.slf4j:jul-to-slf4j:1.6.2') {
    dependency libraries.slf4j_api
}
libraries.log4j_to_slf4j = dependencies.module('org.slf4j:log4j-over-slf4j:1.6.2') {
    dependency libraries.slf4j_api
}
libraries.logback_core = 'ch.qos.logback:logback-core:0.9.29@jar'
libraries.logback_classic = dependencies.module('ch.qos.logback:logback-classic:0.9.29') {
    dependency libraries.logback_core
    dependency libraries.slf4j_api
}

// Jetty
libraries.servlet_api = "org.mortbay.jetty:servlet-api:2.5-20081211@jar"
libraries.jetty_util = dependencies.module("org.mortbay.jetty:jetty-util:6.1.25") {
    dependency libraries.slf4j_api
    dependency libraries.servlet_api
}
libraries.jetty = dependencies.module("org.mortbay.jetty:jetty:6.1.25") {
    dependency libraries.jetty_util
    dependency libraries.servlet_api
}

libraries.commons_httpclient = dependencies.module('commons-httpclient:commons-httpclient:3.1') {
    dependency libraries.jcl_to_slf4j
    dependency "commons-codec:commons-codec:1.2@jar"
}

libraries.maven_ant_tasks = dependencies.module("org.apache.maven:maven-ant-tasks:2.1.3") {
    libraries.ant
}

libraries += [
        ant_junit: 'org.apache.ant:ant-junit:1.8.2@jar',
        ant_antlr: 'org.apache.ant:ant-antlr:1.8.2@jar',
        antlr: 'antlr:antlr:2.7.7@jar',
        commons_cli: 'commons-cli:commons-cli:1.2@jar',
        dom4j: 'dom4j:dom4j:1.6.1@jar',
        guava: 'com.google.guava:guava:r09@jar',
        groovy: 'org.codehaus.groovy:groovy-all:1.8.4@jar',
        jaxen: 'jaxen:jaxen:1.1@jar',
        jcip: "net.jcip:jcip-annotations:1.0",
        jna: 'net.java.dev.jna:jna:3.2.2@jar',
        junit: 'junit:junit:4.8.2',
        xmlunit: 'xmlunit:xmlunit:1.3',
]

libraries.spock = ['org.spockframework:spock-core:0.6-groovy-1.8-SNAPSHOT@jar',
        libraries.groovy,
        'org.objenesis:objenesis:1.2',
        'cglib:cglib-nodep:2.2']
libraries.jmock = ['org.jmock:jmock:2.5.1',
        'org.hamcrest:hamcrest-core:1.1',
        'org.hamcrest:hamcrest-library:1.1',
        'org.jmock:jmock-junit4:2.5.1',
        'org.jmock:jmock-legacy:2.5.1',
        'org.objenesis:objenesis:1.2',
        'cglib:cglib-nodep:2.2']

allprojects {
    group = 'org.gradle'

    plugins.withType(JavaPlugin) {
        sourceCompatibility = 1.5
        targetCompatibility = 1.5
    }

    repositories {
        mavenRepo(url: 'http://repo.gradle.org/gradle/libs')
        mavenRepo(url: 'http://repository.codehaus.org/')
        mavenRepo(url: 'http://m2repo.spockframework.org/snapshots/')
        ivy {
            artifactPattern 'http://repo.jfrog.org/artifactory/gradle-plugins-snapshots/[organisation]/[module]/[revision]/[artifact]-[revision](-[classifier]).[ext]'
            ivyPattern 'http://repo.jfrog.org/artifactory/gradle-ivy-local/[organisation]/[module]/ivy-[revision].xml'
        }
    }

    version = this.version

    apply from: "$rootDir/gradle/conventions-dsl.gradle"
}

configure(groovyProjects()) {
    apply from: "$rootDir/gradle/groovyProject.gradle"
}

configure(publishedProjects()) {
    apply from: "$rootDir/gradle/publish.gradle"
}

allprojects {
    apply from: "$rootDir/gradle/codeQuality.gradle"
    apply from: "$rootDir/gradle/fakeOsInTests.gradle"
}

apply from: "gradle/idea.gradle"
apply from: "gradle/eclipse.gradle"

configurations {
    dists
    runtime {
        visible = false
    }
    plugins {
        visible = false
    }
    testRuntime {
        extendsFrom runtime
        extendsFrom plugins
    }
}

dependencies {
    runtime project(':launcher')
    plugins pluginProjects()
    plugins project(':coreImpl')
}

evaluationDependsOn(':docs')
evaluationDependsOn(':integTest')

clean.dependsOn subprojects.collect { "$it.path:clean" }

task check(overwrite: true, dependsOn: groovyProjects()*.check)
check.dependsOn ':docs:checkstyleApi'
configure(groovyProjects()) {
    check.dependsOn ":docs:checkstyleApi"
}

task test(overwrite: true, dependsOn: groovyProjects()*.test)
task uploadArchives(dependsOn: publishedProjects()*.uploadArchives)
task publishLocalArchives(dependsOn: publishedProjects()*.publishLocalArchives)

task aggregateTestReports(type: TestReportAggregator) {
    testReportDir = file("${reportsDir}/tests")
    testResultsDir = file("${buildDir}/test-results")
    projects = subprojects
}

zipRootFolder = "$archivesBaseName-${-> version}"

binDistImage = copySpec {
    from('src/toplevel') {
        expand(version: version)
    }
    from project(':docs').outputs.distDocs
    into('bin') {
        from { project(':launcher').startScripts.outputs.files }
        fileMode = 0755
    }
    into('lib') {
        from configurations.runtime
        into('plugins') {
            from configurations.plugins - configurations.runtime
        }
    }
}

allDistImage = copySpec {
    with binDistImage
    into('src') {
        from groovyProjects().collect {project -> project.sourceSets.main.allSource }
    }
    into('docs') {
        from project(':docs').outputs.docs
    }
    into('samples') {
        from project(':docs').outputs.samples
    }
}

task allZip(type: Zip) {
    classifier = 'all'
    into(zipRootFolder) {
        with allDistImage
    }
}

task binZip(type: Zip) {
    classifier = 'bin'
    into(zipRootFolder) {
        with binDistImage
    }
}

task srcZip(type: Zip) {
    classifier = 'src'
    into(zipRootFolder) {
        from('gradlew') {
            fileMode = 0755
        }
        from(projectDir) {
            def spec = delegate
            ['buildSrc', 'subprojects/*'].each {
                spec.include "$it/*.gradle"
                spec.include "$it/src/"
            }
            include 'config/'
            include 'gradle/'
            include 'src/'
            include '*.gradle'
            include 'wrapper/'
            include 'gradlew.bat'
        }
    }
}

artifacts {
    tasks.withType(Zip).each {
        dists it
    }
}

task intTestImage(type: Sync) {
    dependsOn "launcher:startScripts"
    with binDistImage
    integTestGradleHome = file("$buildDir/integ test")
    into integTestGradleHome
    doLast { task ->
        ant.chmod(dir: "$integTestGradleHome/bin", perm: "ugo+rx", includes: "**/*")
    }
}

def isDevBuild() {
    gradle.taskGraph.hasTask(developerBuild)
}

def isCIBuild() {
    gradle.taskGraph.hasTask(ciBuild)
}

def isCommitBuild() {
    gradle.taskGraph.hasTask(commitBuild)
}

def guessMaxForks() {
    int processors = Runtime.runtime.availableProcessors()
    return Math.max(2, (int) (processors / 2))
}

task testedDists(dependsOn: [assemble, check])

task nightlyBuild(dependsOn: [clean, testedDists, ':docs:uploadDocs'])

task install(type: Install) {
    description = 'Installs the minimal distribution into directory $gradle_installPath'
    group = 'build'
    dependsOn binZip.taskDependencies
    with binDistImage
    installDirProperyName = 'gradle_installPath'
}

task installAll(type: Install) {
    description = 'Installs the full distribution into directory $gradle_installPath'
    group = 'build'
    dependsOn allZip.taskDependencies
    with allDistImage
    installDirProperyName = 'gradle_installPath'
}

uploadDists {
    dependsOn testedDists
    uploadDescriptor = false
    doFirst {
        repositories {
            ivy {
                artifactPattern "${version.distributionUrl}/[artifact]-[revision](-[classifier]).[ext]"
                userName = artifactoryUserName
                password = artifactoryUserPassword
            }
        }
    }
}

gradle.taskGraph.whenReady {graph ->
    if (graph.hasTask(uploadDists) || graph.hasTask(uploadArchives)) {
        // check properties defined and fail early
        artifactoryUserName
        artifactoryUserPassword
    }
}

task developerBuild {
    description = 'Builds distributions and runs pre-checkin checks'
    group = 'build'
    dependsOn testedDists
}

task ciBuild {
    description = 'Full build performed by the CI server'
    dependsOn clean, testedDists
}

task commitBuild {
    description = 'Commit build performed by the CI server'
    dependsOn testedDists
}

gradle.taskGraph.whenReady {graph ->
    if (graph.hasTask(ciBuild)) {
        subprojects { reportsDirName = "$rootProject.reportsDir/${path.replaceFirst(':', '').replaceAll(':', '.')}" }
    }
}

// A marker task which causes the release version to be used when it is present in the task graph
task releaseVersion
//TODO SF - this task name is inconsistent because other releaseXxx tasks actually upload some content somewhere. Should be called something like 'markReleaseVersion'

task tag(type: Tag)

task testedTag(type: Tag, dependsOn: testedDists)

task releaseArtifacts {
    description = 'Builds the release artifacts'
    //TODO SF - this task name is inconsistent because other releaseXxx tasks actually upload some content somewhere. Should be called something like 'buildReleaseArtifacts'
    group = 'release'
    dependsOn releaseVersion, assemble, ':docs:websiteDocs'
}

task checkJavaVersion << {
    assert Jvm.current().java6Compatible : "Must use a Java 6 compatible JVM to perform the release. Current JVM is ${Jvm.current()}"
}

task release {
    description = 'Builds, tests and uploads the release artifacts'
    group = 'release'
    dependsOn releaseVersion, checkJavaVersion, testedTag, releaseArtifacts, testedDists, 'uploadAll'
    doLast {
        releases.incrementNextVersion()
    }
}

task incrementNextVersion << {
    releases.incrementNextVersion()
}

task uploadAll {
    description = 'Uploads binaries, sources and documentation. Does not upload the website! Useful when release procedure breaks at upload and only upload tasks should executed again'
    dependsOn uploadArchives, uploadDists, ':docs:uploadDocs'
}

task wrapper(type: Wrapper) {
    gradleVersion = '1.0-milestone-4-20110508123756+0200'
}

def groovyProjects() {
    subprojects.findAll { it.name != 'docs' }
}

def publishedProjects() {
    [project(':core'), project(':toolingApi'), project(':wrapper')]
}

def pluginProjects() {
    ['plugins', 'codeQuality', 'jetty', 'antlr', 'wrapper', 'osgi', 'maven', 'ide', 'announce', 'scala', 'sonar', 'signing', 'cpp', 'ear'].collect {
        project(it)
    }
}

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
    }

}

class Tag extends DefaultTask {
    @TaskAction
    def tagNow() {
        def version = project.version
        def git = new Git(project)
        git.checkNoModifications()
        git.tag("REL_$version", "Release $version")
        git.branch("RB_$version")
    }
}


