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

/**
 * 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: 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.2@jar',
        ant_junit: 'org.apache.ant:ant-junit:1.8.2@jar',
        ant_launcher: 'org.apache.ant:ant-launcher:1.8.2@jar',
        ant_antlr: 'org.apache.ant:ant-antlr:1.8.2@jar',
        antlr: 'antlr:antlr:2.7.7@jar',
        asm_all: 'asm:asm-all:3.3.1@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.6@jar',
        dom4j: 'dom4j:dom4j:1.6.1@jar',
        guava: 'com.google.guava:guava:r08@jar',
        groovy: 'org.codehaus.groovy:groovy-all:1.7.10@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.28@jar',
        logback_core: 'ch.qos.logback:logback-core:0.9.28@jar',
        junit: 'junit:junit:4.8.2',
        xmlunit: 'xmlunit:xmlunit:1.3',
]

libraries.spock = ['org.spockframework:spock-core:0.5-groovy-1.7@jar',
        libraries.groovy,
        'org.hamcrest:hamcrest-core:1.1@jar',
        'cglib:cglib-nodep:2.2',
        'org.objenesis:objenesis:1.2']
libraries.jmock = ['org.jmock:jmock:2.5.1@jar',
        'org.hamcrest:hamcrest-core:1.1@jar',
        'org.hamcrest:hamcrest-library:1.1@jar',
        'org.jmock:jmock-junit4:2.5.1@jar',
        'org.jmock:jmock-legacy:2.5.1@jar',
        'org.objenesis:objenesis:1.2',
        'cglib:cglib-nodep:2.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://repo.gradle.org/gradle/libs')
    }

    version = this.version
}

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

    archivesBaseName = "gradle-${name.replaceAll("\\p{Upper}") { "-${it.toLowerCase()}" } }"
    dependencies {
        testCompile libraries.junit, libraries.jmock, libraries.spock
    }

    apply from: "$rootDir/gradle/compile.gradle"

    test {
        maxParallelForks = guessMaxForks()
    }

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

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

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

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 {
    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])

        // Code formatting options
        def codeFormatSettings = new XmlParser().parseText('''
  <component name="CodeStyleSettingsManager">
    <option name="PER_PROJECT_SETTINGS">
      <value>
        <option name="USE_SAME_INDENTS" value="true" />
        <option name="RIGHT_MARGIN" value="200" />
        <option name="JD_ALIGN_PARAM_COMMENTS" value="false" />
        <option name="JD_ALIGN_EXCEPTION_COMMENTS" value="false" />
        <option name="JD_P_AT_EMPTY_LINES" value="false" />
        <option name="JD_KEEP_EMPTY_PARAMETER" value="false" />
        <option name="JD_KEEP_EMPTY_EXCEPTION" value="false" />
        <option name="JD_KEEP_EMPTY_RETURN" value="false" />
        <option name="WRAP_COMMENTS" value="true" />
        <option name="IF_BRACE_FORCE" value="3" />
        <option name="DOWHILE_BRACE_FORCE" value="3" />
        <option name="WHILE_BRACE_FORCE" value="3" />
        <option name="FOR_BRACE_FORCE" value="3" />
        <ADDITIONAL_INDENT_OPTIONS fileType="groovy">
          <option name="INDENT_SIZE" value="2" />
          <option name="CONTINUATION_INDENT_SIZE" value="8" />
          <option name="TAB_SIZE" value="4" />
          <option name="USE_TAB_CHARACTER" value="false" />
          <option name="SMART_TABS" value="false" />
          <option name="LABEL_INDENT_SIZE" value="0" />
          <option name="LABEL_INDENT_ABSOLUTE" value="false" />
          <option name="USE_RELATIVE_INDENTS" value="false" />
        </ADDITIONAL_INDENT_OPTIONS>
        <ADDITIONAL_INDENT_OPTIONS fileType="java">
          <option name="INDENT_SIZE" value="4" />
          <option name="CONTINUATION_INDENT_SIZE" value="8" />
          <option name="TAB_SIZE" value="4" />
          <option name="USE_TAB_CHARACTER" value="false" />
          <option name="SMART_TABS" value="false" />
          <option name="LABEL_INDENT_SIZE" value="0" />
          <option name="LABEL_INDENT_ABSOLUTE" value="false" />
          <option name="USE_RELATIVE_INDENTS" value="false" />
        </ADDITIONAL_INDENT_OPTIONS>
        <ADDITIONAL_INDENT_OPTIONS fileType="js">
          <option name="INDENT_SIZE" value="4" />
          <option name="CONTINUATION_INDENT_SIZE" value="8" />
          <option name="TAB_SIZE" value="4" />
          <option name="USE_TAB_CHARACTER" value="false" />
          <option name="SMART_TABS" value="false" />
          <option name="LABEL_INDENT_SIZE" value="0" />
          <option name="LABEL_INDENT_ABSOLUTE" value="false" />
          <option name="USE_RELATIVE_INDENTS" value="false" />
        </ADDITIONAL_INDENT_OPTIONS>
        <ADDITIONAL_INDENT_OPTIONS fileType="jsp">
          <option name="INDENT_SIZE" value="4" />
          <option name="CONTINUATION_INDENT_SIZE" value="8" />
          <option name="TAB_SIZE" value="4" />
          <option name="USE_TAB_CHARACTER" value="false" />
          <option name="SMART_TABS" value="false" />
          <option name="LABEL_INDENT_SIZE" value="0" />
          <option name="LABEL_INDENT_ABSOLUTE" value="false" />
          <option name="USE_RELATIVE_INDENTS" value="false" />
        </ADDITIONAL_INDENT_OPTIONS>
        <ADDITIONAL_INDENT_OPTIONS fileType="php">
          <option name="INDENT_SIZE" value="4" />
          <option name="CONTINUATION_INDENT_SIZE" value="8" />
          <option name="TAB_SIZE" value="4" />
          <option name="USE_TAB_CHARACTER" value="false" />
          <option name="SMART_TABS" value="false" />
          <option name="LABEL_INDENT_SIZE" value="0" />
          <option name="LABEL_INDENT_ABSOLUTE" value="false" />
          <option name="USE_RELATIVE_INDENTS" value="false" />
        </ADDITIONAL_INDENT_OPTIONS>
        <ADDITIONAL_INDENT_OPTIONS fileType="scala">
          <option name="INDENT_SIZE" value="2" />
          <option name="CONTINUATION_INDENT_SIZE" value="2" />
          <option name="TAB_SIZE" value="2" />
          <option name="USE_TAB_CHARACTER" value="false" />
          <option name="SMART_TABS" value="false" />
          <option name="LABEL_INDENT_SIZE" value="0" />
          <option name="LABEL_INDENT_ABSOLUTE" value="false" />
          <option name="USE_RELATIVE_INDENTS" value="false" />
        </ADDITIONAL_INDENT_OPTIONS>
        <ADDITIONAL_INDENT_OPTIONS fileType="sql">
          <option name="INDENT_SIZE" value="2" />
          <option name="CONTINUATION_INDENT_SIZE" value="8" />
          <option name="TAB_SIZE" value="4" />
          <option name="USE_TAB_CHARACTER" value="false" />
          <option name="SMART_TABS" value="false" />
          <option name="LABEL_INDENT_SIZE" value="0" />
          <option name="LABEL_INDENT_ABSOLUTE" value="false" />
          <option name="USE_RELATIVE_INDENTS" value="false" />
        </ADDITIONAL_INDENT_OPTIONS>
        <ADDITIONAL_INDENT_OPTIONS fileType="xml">
          <option name="INDENT_SIZE" value="4" />
          <option name="CONTINUATION_INDENT_SIZE" value="8" />
          <option name="TAB_SIZE" value="4" />
          <option name="USE_TAB_CHARACTER" value="false" />
          <option name="SMART_TABS" value="false" />
          <option name="LABEL_INDENT_SIZE" value="0" />
          <option name="LABEL_INDENT_ABSOLUTE" value="false" />
          <option name="USE_RELATIVE_INDENTS" value="false" />
        </ADDITIONAL_INDENT_OPTIONS>
      </value>
    </option>
    <option name="USE_PER_PROJECT_SETTINGS" value="true" />
  </component>
''')
        node.append(codeFormatSettings)
    }

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

ideaWorkspace {
    withXml { provider ->
        Node node = provider.asNode()

        Node runManagerConfig = node.component.find { it.'@name' == 'RunManager' }

        // Add int test configuration to JUnit defaults
        Node runConfig = runManagerConfig.configuration.find { it.'@type' == 'JUnit'}

        Node vmParameters = runConfig.option.find { it.'@name' == 'VM_PARAMETERS' }
        vmParameters.'@value' = "\"-DintegTest.gradleHomeDir=${intTestImage.destinationDir}\" -ea -Dorg.gradle.integtest.executer=embedded"

        // Add an application configuration
        runManagerConfig.'@selected' = 'Application.Gradle'
        def appConfig = new XmlParser().parseText('''
    <configuration default="false" name="Gradle" type="Application" factoryName="Application">
      <extension name="coverage" enabled="false" merge="false" />
      <option name="MAIN_CLASS_NAME" value="org.gradle.launcher.Main" />
      <option name="VM_PARAMETERS" value="" />
      <option name="PROGRAM_PARAMETERS" value="" />
      <option name="WORKING_DIRECTORY" value="file://$PROJECT_DIR$" />
      <option name="ALTERNATIVE_JRE_PATH_ENABLED" value="false" />
      <option name="ALTERNATIVE_JRE_PATH" value="" />
      <option name="ENABLE_SWING_INSPECTOR" value="false" />
      <option name="ENV_VARIABLES" />
      <option name="PASS_PARENT_ENVS" value="true" />
      <module name="launcher" />
      <envs />
      <RunnerSettings RunnerId="Debug">
        <option name="DEBUG_PORT" value="63810" />
        <option name="TRANSPORT" value="0" />
        <option name="LOCAL" value="true" />
      </RunnerSettings>
      <RunnerSettings RunnerId="Run" />
      <ConfigurationWrapper RunnerId="Debug" />
      <ConfigurationWrapper RunnerId="Run" />
      <method />
    </configuration>
''')
        runManagerConfig.append(appConfig)
    }
}

// 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 })))
    }
    eclipseClasspath {
        plusConfigurations.add(configurations.detachedConfiguration(new DefaultSelfResolvingDependency(files { sourceSets*.resources*.srcDirs*.findAll { it.isDirectory() }} )))
        whenConfigured { classpath ->
            classpath.entries.removeAll { it instanceof org.gradle.plugins.ide.eclipse.model.SourceFolder && it.path.endsWith('/resources') }
        }
    }
}

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

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

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

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)
task uploadArchives(dependsOn: publishedProjects()*.uploadArchives)
task publishLocalArchives(dependsOn: publishedProjects()*.publishLocalArchives)

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

binDistImage = copySpec {
    from('src/toplevel') {
        expand(version: version)
    }
    from project(':docs').distDocs.destFile
    into('bin') {
        from startScriptsDir
        fileMode = 0755
    }
    into('lib') {
        from configurations.runtime
        into('plugins') {
            from configurations.plugins - configurations.runtime
        }
        into('core-impl') {
            from configurations.coreImpl - 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())
    }
}

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, integTests])

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 {
                name = 'gradleReleases'
                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

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, uploadArchives, uploadDists, ':docs:uploadDocs'
}

task wrapper(type: Wrapper) {
    gradleVersion = '1.0-milestone-2'
}

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

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

def runtimeProjects() {
    groovyProjects() - pluginProjects() - [project(':integTest'), project(':coreImpl')]
}

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

switch (System.getProperty("user.name")) {
    case "pniederw":
        allprojects {
            tasks.withType(Test) {
                maxParallelForks = 1
                forkEvery = 0
            }
        }
        break
    default:
        break
}

