/*
 * Copyright 2010 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import java.util.jar.Attributes
import org.gradle.api.artifacts.repositories.WebdavResolver
import org.gradle.api.internal.artifacts.dependencies.DefaultSelfResolvingDependency

import org.gradle.build.samples.WrapperProjectCreator
import org.gradle.build.Version
import org.gradle.build.Install
import org.gradle.build.Git

/**
 * 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: codehausUserName, codehausUserPassword
 * Uploading the userguide and the javadoc to Gradle's website: websiteFtpUserName, websiteFtpUserPassword
 * Using the build to create a new distribution and install it on the local machine: gradle_installPath
 */

version = new Version(project)

defaultTasks 'assemble'

apply plugin: 'java-base'

startScriptsDir = new File("$buildDir/startScripts")

archivesBaseName = 'gradle'

libraries = [
        ant: 'org.apache.ant:ant:1.8.1@jar',
        ant_junit: 'org.apache.ant:ant-junit:1.8.1@jar',
        ant_launcher: 'org.apache.ant:ant-launcher:1.8.1@jar',
        ant_nodeps: 'org.apache.ant:ant-nodeps:1.8.1@jar',
        ant_antlr: 'org.apache.ant:ant-antlr:1.8.1@jar',
        antlr: 'antlr:antlr:2.7.7@jar',
        asm_all: 'asm:asm-all:3.2@jar',
        commons_cli: 'commons-cli:commons-cli:1.2@jar',
        commons_io: 'commons-io:commons-io:1.4@jar',
        commons_lang: 'commons-lang:commons-lang:2.5@jar',
        dom4j: 'dom4j:dom4j:1.6.1@jar',
        google_collections: 'com.google.collections:google-collections:1.0@jar',
        groovy: 'org.codehaus.groovy:groovy-all:1.7.5@jar',
        ivy: 'org.apache.ivy:ivy:2.2.0@jar',
        jaxen: 'jaxen:jaxen:1.1@jar',
        slf4j_api: 'org.slf4j:slf4j-api:1.6.1@jar',
        jcl_to_slf4j: 'org.slf4j:jcl-over-slf4j:1.6.1@jar',
        jul_to_slf4j: 'org.slf4j:jul-to-slf4j:1.6.1@jar',
        log4j_to_slf4j: 'org.slf4j:log4j-over-slf4j:1.6.1@jar',
        logback_classic: 'ch.qos.logback:logback-classic:0.9.24@jar',
        logback_core: 'ch.qos.logback:logback-core:0.9.24@jar',
        junit: 'junit:junit:4.8.1',
        xmlunit: 'xmlunit:xmlunit:1.3',
]

libraries.spock = ['org.spockframework:spock-core:0.4-groovy-1.7@jar', libraries.groovy, 'cglib:cglib-nodep:2.2', 'org.objenesis:objenesis:1.2']
libraries.groovy_depends = [libraries.groovy, libraries.commons_cli]
libraries.jetty_depends = ["org.mortbay.jetty:jetty:6.1.25@jar", "org.mortbay.jetty:jetty-util:6.1.25@jar",
        "org.mortbay.jetty:servlet-api:2.5-20081211@jar"]

allprojects {
    group = 'org.gradle'

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

    repositories {
        mavenRepo(urls: 'http://gradle.artifactoryonline.com/gradle/libs')
    }

    version = this.version
}

def FIRST_LEVEL_JMOCK = ['org.hamcrest:hamcrest-core:1.1@jar', 'org.hamcrest:hamcrest-library:1.1@jar', 'org.jmock:jmock-junit4:2.5.1@jar']

configure(groovyProjects()) {
    apply plugin: 'groovy'
    apply plugin: 'eclipse'

    archivesBaseName = "gradle-${name.replaceAll("\\p{Upper}") { "-${it.toLowerCase()}" } }"
    dependencies {
        testCompile libraries.junit, FIRST_LEVEL_JMOCK, libraries.spock
        testCompile module("org.jmock:jmock:2.5.1") {
            dependencies('org.jmock:jmock-legacy:2.5.1@jar', 'org.objenesis:objenesis:1.0', 'cglib:cglib-nodep:2.1_3')
            dependencies(FIRST_LEVEL_JMOCK as Object[])
        }
        // todo - these should not be here
        testCompile 'org.jmock:jmock-legacy:2.5.1@jar', 'org.objenesis:objenesis:1.0', 'cglib:cglib-nodep:2.1_3'
    }

    test {
        maxParallelForks = guessMaxForks()
    }

    tasks.withType(Jar).each { jar ->
        jar.manifest.mainAttributes([
                (Attributes.Name.IMPLEMENTATION_TITLE.toString()): 'Gradle',
                (Attributes.Name.IMPLEMENTATION_VERSION.toString()): version,
        ])
    }
}

allprojects {
    apply plugin: 'code-quality'

    checkstyleConfigDir = "$rootDir/config/checkstyle"
    checkstyleConfigFileName = new File(checkstyleConfigDir, "checkstyle.xml")
    codeNarcConfigFileName = "$rootDir/config/codenarc.xml"
    checkstyleProperties.checkstyleConfigDir = checkstyleConfigDir
}
configure(groovyProjects()) {
    sourceSets.allObjects { sourceSet ->
        task "${sourceSet.getTaskName('checkstyle', 'groovy')}"(type: Checkstyle) {
            configFile = new File(checkstyleConfigDir, "checkstyle-groovy.xml")
            source sourceSet.allGroovy
            classpath = sourceSet.compileClasspath
            resultFile = new File(checkstyleResultsDir, "${sourceSet.name}-groovy.xml")
        }
    }
}

allprojects {
    apply plugin: 'idea'
    apply plugin: 'eclipse'
    def config = configurations.findByName('testRuntime')
    if (!config) {
        return
    }
    ideDir = file('ide')
    task ide(type: Sync) {
        into "$ideDir/lib"
        from { config.copyRecursive {dep -> !(dep instanceof ProjectDependency)}.files }
    }
    ideaModule {
        gradleCacheVariable = 'GRADLE_CACHE'
        outputDir = "$rootProject.projectDir/intellij/out" as File
        testOutputDir = "$rootProject.projectDir/intellij/testOut" as File
    }
    eclipseClasspath {
        variables = [:]
    }
}

ideaModule {
    excludeDirs += file('intTestHomeDir')
    excludeDirs += file('buildSrc/build')
    excludeDirs += file('buildSrc/.gradle')
    excludeDirs += file('performanceTest/.gradle')
    excludeDirs += file('performanceTest/build')
    excludeDirs += file('website/.gradle')
    excludeDirs += file('website/build')
}

ideaProject {
    wildcards += ['?*.gradle']
    javaVersion = '1.6'
    withXml { provider ->
		def node = provider.asNode()
		// 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])
    }

    whenConfigured { project ->
        project.jdk.languageLevel = 'JDK_1_5'
    }
}

// Exclude resource directories from compilation and add them back in as classpath resources

ideaProject {
    withXml { provider ->
		def node = provider.asNode()
        def compilerConfig = node.component.find { it.'@name' == 'CompilerConfiguration' }
        def exclude = compilerConfig.appendNode('excludeFromCompile')
        Collection resourceDirs = groovyProjects().collect { project -> project.sourceSets*.resources*.srcDirs }.flatten()
        resourceDirs.each {
            exclude.appendNode('directory', [url: "file://\$PROJECT_DIR\$/${rootProject.relativePath(it)}", includeSubdirectories: true])
        }
    }
}

configure(groovyProjects()) {
    ideaModule {
        scopes.RUNTIME.plus.add(configurations.detachedConfiguration(new DefaultSelfResolvingDependency(files { sourceSets.main.resources.srcDirs })))
        scopes.TEST.plus.add(configurations.detachedConfiguration(new DefaultSelfResolvingDependency(files { sourceSets*.resources*.srcDirs })))
    }
}

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

dependencies {
    runtime runtimeProjects()
    plugins pluginProjects()
}

evaluationDependsOn(':docs')
evaluationDependsOn(':core')

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

task check(overwrite: true, dependsOn: groovyProjects()*.check)
check.dependsOn ':docs:checkstyleApi'
task test(overwrite: true, dependsOn: groovyProjects()*.test)

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

binDistImage = copySpec {
    from 'src/toplevel'
    from project(':docs').distDocs.destFile
    into('bin') {
        from startScriptsDir
        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').docsDir
    }
    into('samples') {
        from project(':docs').samplesDir
    }
}

task allZip(type: Zip, dependsOn: ['launcher:startScripts', 'docs:docs', 'docs:samples']) {
    classifier = 'all'
    into(zipRootFolder) {
        with allDistImage
    }
}

task binZip(type: Zip, dependsOn: ['launcher:startScripts', 'docs:distDocs']) {
    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 'gradle.properties'
            include 'wrapper/'
            include 'gradlew.bat'
        }
    }
}

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

task intTestImage(type: Sync) {
    dependsOn allZip.taskDependencies
    with allDistImage
    integTestGradleHome = file("$buildDir/integ test")
    into integTestGradleHome
    doLast { task ->
        ant.chmod(dir: "$integTestGradleHome/bin", perm: "ugo+rx", includes: "**/*")
        WrapperProjectCreator.createProject(new File(integTestGradleHome, 'samples'), binZip.archivePath.parentFile, version.toString())
    }
}

tasks.withType(Test).allTasks { task ->
    task.configure {
        dependsOn intTestImage, binZip, allZip, srcZip, ':docs:userguideDocbook'
        integTestUserDir = file('intTestHomeDir')
        systemProperties['integTest.userGuideInfoDir'] = project(':docs').docbookSrc
        systemProperties['integTest.userGuideOutputDir'] = new File(project(':docs').samplesSrcDir, "userguideOutput").absolutePath
        systemProperties['integTest.gradleUserHomeDir'] = integTestUserDir.absolutePath
        forkEvery = 15
        maxParallelForks = guessMaxForks()

        testClassesDir = project(':core').sourceSets.integTest.classesDir
        classpath = project(':core').sourceSets.integTest.runtimeClasspath + configurations.testRuntime
        testResultsDir = file("build/test-results/$name")
        testReportDir = file("build/reports/tests/$name")
        testSrcDirs = []

        systemProperties['integTest.gradleHomeDir'] = intTestImage.integTestGradleHome.absolutePath
        jvmArgs '-Xmx512m', '-XX:+HeapDumpOnOutOfMemoryError'

        doFirst {
            if (isDevBuild()) {
                exclude 'org/gradle/integtests/DistributionIntegrationTest.*'
            }
        }
    }
}

task integTest(type: Test) {
}

task embeddedIntegTest(type: Test) {
    systemProperties['org.gradle.integtest.executer'] = 'embedded'
    jvmArgs '-Xmx512m', '-XX:MaxPermSize=256m', '-XX:+HeapDumpOnOutOfMemoryError'
}

task daemonIntegTest(type: Test) {
    systemProperties['org.gradle.integtest.executer'] = 'daemon'
}

private def isDevBuild() {
    gradle.taskGraph.hasTask(':developerBuild')
}

gradle.taskGraph.whenReady { graph ->
    if (isDevBuild()) {
        if (OperatingSystem.current().isWindows()) {
            embeddedIntegTest.enabled = false
        } else {
            integTest.enabled = false
        }
    }
    if (graph.hasTask(':ciBuild')) {
        embeddedIntegTest.enabled = false
    }
}

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

task testedDists(dependsOn: [assemble, check, integTest, embeddedIntegTest, 'openApi:integTest', ':ui:integTest'])

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 {
        it.repositories.add(new WebdavResolver()) {
            name = 'gradleReleases'
            user = codehausUserName
            userPassword = codehausUserPassword
            addArtifactPattern("${version.distributionUrl}/[artifact]-[revision](-[classifier]).[ext]" as String)
        }
    }
}

gradle.taskGraph.whenReady {graph ->
    if (graph.hasTask(uploadDists)) {
        // check properties defined and fail early
        codehausUserName
        codehausUserPassword
    }
}

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

task ciBuild {
    description = 'Build performed by the CI server'
    dependsOn clean, 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

task tag {
    doLast {
        def git = new Git(project)
        git.checkNoModifications()
        git.tag("REL_$version", "Release $version")
        git.branch("RB_$version")
    }
}

task releaseArtifacts {
    description = 'Builds the release artifacts'
    group = 'release'
    dependsOn releaseVersion, assemble, ':docs:websiteDocs'
}

task release {
    description = 'Builds, tests and uploads the release artifacts'
    group = 'release'
    dependsOn releaseVersion, tag, releaseArtifacts, testedDists, uploadDists, ':docs:uploadDocs'
}

task wrapper(type: Wrapper, dependsOn: binZip)
wrapper.doFirst {task ->
    task.configure {
        gradleVersion = version
        jarPath = 'wrapper'
    }
}

def groovyProjects() {
    subprojects.findAll { project -> project.name != 'docs' }
}

def runtimeProjects() {
    groovyProjects() - pluginProjects()
}

def pluginProjects() {
    ['plugins', 'codeQuality', 'jetty', 'antlr', 'wrapper', 'osgi', 'maven', 'eclipse', 'idea', 'announce', 'scala'].collect {
        project(it)
    }
}
