/*
 *  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.codehaus.groovy.gradle.ReleaseInfoGenerator

buildscript {
    repositories {
        if (rootProject.hasProperty('buildInChina')) {
            // Try to use Aliyun maven repository when building in China
            maven { url 'https://maven.aliyun.com/nexus/content/groups/public' }
        }
        mavenCentral()
        maven { url 'https://plugins.gradle.org/m2/' }
        // uncomment next repo if jcenter isn't available
        // we have some build components mirrored there
//        maven { url 'https://groovy.jfrog.io/artifactory/libs-release-local/' }
        maven { url 'https://oss.sonatype.org/content/repositories/snapshots' }
        maven { url 'https://jitpack.io' }
    }

    dependencies {
        // using the old "classpath" style of plugins because the new one doesn't play well with multi-modules
        classpath 'org.asciidoctor:asciidoctor-gradle-plugin:1.5.8'
        classpath 'org.jfrog.buildinfo:build-info-extractor-gradle:3.0.3'
        //classpath 'nl.javadude.gradle.plugins:license-gradle-plugin:0.11.0'
        classpath 'org.nosphere.apache:creadur-rat-gradle:0.8.0'
        classpath 'gradle.plugin.com.github.jk1:gradle-license-report:1.3'
    }
}

plugins {
    id 'me.champeau.buildscan-recipes' version '0.2.3'
    id 'com.github.spotbugs' version '4.6.0'
    id 'com.github.ben-manes.versions' version '0.42.0' // 0.43 requires gradle 7+
    id 'com.github.blindpirate.osgi' version '0.0.6'
    id 'org.sonarqube' version '3.0'
}

buildScanRecipes {
    recipe 'git-commit', baseUrl: 'https://github.com/apache/groovy/tree'
    recipe 'teamcity', baseUrl: 'https://ci.groovy-lang.org', guest: 'true'
    recipes 'git-status', 'gc-stats', 'teamcity', 'travis-ci'
}

ext.modules = {
    subprojects.findAll{ !['performance', 'binary-compatibility'].contains(it.name) }
}
ext.isReleaseVersion = !groovyVersion.toLowerCase().endsWith('snapshot')

apply from: 'gradle/bad-practices.gradle'
apply from: 'gradle/publish.gradle'
apply plugin: 'com.github.jk1.dependency-license-report'

File javaHome = new File(System.getProperty('java.home'))
logger.lifecycle "Using Java from $javaHome (version ${System.getProperty('java.version')})"

allprojects {
    apply plugin: 'java-library'

    buildDir = 'target'
    sourceCompatibility = 1.8
    targetCompatibility = 1.8

    group = 'org.codehaus.groovy'
    version = groovyVersion
    repositories {
        if (rootProject.hasProperty('buildInChina')) {
            // Try to use Aliyun maven repository when building in China
            maven { url 'https://maven.aliyun.com/nexus/content/groups/public' }
        }
        mavenCentral()
//        maven { url 'https://oss.sonatype.org/content/repositories/snapshots' }
//        mavenLocal()
    }

    ext.buildDate = isReleaseVersion?new Date():new Date(0)

    task generateReleaseInfo(type: ReleaseInfoGenerator) {
        version = rootProject.groovyVersion
        bundleVersion = rootProject.groovyBundleVersion
        buildDate = rootProject.buildDate
        outputFile = file("$buildDir/release-info/groovy-release-info.properties")
    }

    apply plugin: 'groovy'
    apply from: "${rootProject.projectDir}/gradle/indy.gradle"
    apply from: "${rootProject.projectDir}/gradle/asciidoctor.gradle"

    tasks.withType(org.asciidoctor.gradle.AsciidoctorTask) {
        outputs.cacheIf { true }
    }
}

task(copyTestResources, type: Copy)
        .from('src/test')
        .into("$buildDir/classes/test")
        .include('**/*.txt', '**/*.xml', '**/*.properties', '**/*.png', '**/*.html', '**/*.gif', '**/*.ico', '**/*.css')
test.dependsOn(copyTestResources)

repositories {
    // TODO: Some repos are needed only for some configs. Declare them just for the configuration once Gradle allows this.
    maven { url 'https://repository.jboss.org/nexus/content/groups/m2-release-proxy' } // tools
}

configurations {
    compilerCompile
    tools
    antlr2
    spec
}

ext {
    antVersion = '1.10.12'
    asmVersion = '9.5'
    antlrVersion = '2.7.7'
    antlr4Version = '4.9.0'
    bridgerVersion = '1.6.Final'
    coberturaVersion = '1.9.4.1'
    commonsCliVersion = '1.4'
    gparsVersion = '1.2.1'
    ivyVersion = '2.5.1'
    jansiVersion = '2.4.0'
    jarjarVersion = '1.7.2'
    javaParserVersion = '3.25.4'
    jlineVersion = '2.14.6'
    jmockVersion = '1.2.0'
    junitVersion = '4.13.2'
    logbackVersion = '1.2.11'
    log4jVersion = '1.2.17'
    log4j2Version = '2.20.0'
    openbeansVersion = '1.0.2'
    picocliVersion = '4.6.3'
    qdoxVersion = '1.12.1'
    slf4jVersion = '1.7.36'
    xmlunitVersion = '1.6'
    xstreamVersion = '1.4.20'
    spockVersion = '2.2-groovy-3.0'
    spotbugsVersion = '4.7.1'
    spotbugsAnnotationsVersion = '4.7.1'
    checkstyleVersion = '8.44'
    junit5Version = '5.9.2'
    junit5PlatformVersion = '1.9.2'
    jcipAnnotationsVersion = '1.0'
    jspApiVersion = '2.3.3'
    servletApiVersion = '4.0.1'
}

dependencies {
    // for internal CLI usage
    api "info.picocli:picocli:$picocliVersion" // marked as api but manually excluded later in assemble.gradle - modules will use shaded version
    // for internal usage of things like Opcode
    api "org.ow2.asm:asm:$asmVersion" // marked as api but manually excluded later in assemble.gradle - modules will use shaded version
    // for internal usage by the parser but manually excluded later in assemble.gradle - modules will use shaded version
    api "antlr:antlr:$antlrVersion"
    implementation "org.ow2.asm:asm-analysis:$asmVersion"
    implementation "org.ow2.asm:asm-commons:$asmVersion"
    implementation "org.ow2.asm:asm-tree:$asmVersion"
    implementation "org.ow2.asm:asm-util:$asmVersion"
    implementation("com.thoughtworks.xstream:xstream:$xstreamVersion") {
        exclude(group: 'xpp3', module: 'xpp3_min')
        exclude(group: 'junit', module: 'junit')
        exclude(group: 'jmock', module: 'jmock')
        exclude(group: 'xmlpull', module: 'xmlpull')
    }
    implementation "me.champeau.openbeans:openbeans:$openbeansVersion"
    implementation "org.fusesource.jansi:jansi:$jansiVersion"
    implementation("org.apache.ivy:ivy:$ivyVersion") {
        transitive = false
    }

    implementation("org.codehaus.gpars:gpars:$gparsVersion") {
        exclude(group: 'org.codehaus.groovy', module: 'groovy-all')
    }

    testImplementation "jmock:jmock:$jmockVersion"
    testImplementation "jmock:jmock-cglib:$jmockVersion"
    testImplementation "xmlunit:xmlunit:$xmlunitVersion"
    testImplementation "ch.qos.logback:logback-classic:$logbackVersion"
    testImplementation "log4j:log4j:$log4jVersion"
    testImplementation "org.apache.logging.log4j:log4j-core:$log4j2Version"
    testImplementation "org.slf4j:jcl-over-slf4j:$slf4jVersion"
    testImplementation "com.thoughtworks.qdox:qdox:$qdoxVersion"

    tools "org.pantsbuild:jarjar:$jarjarVersion"
    tools "org.jboss.bridger:bridger:$bridgerVersion"

    tools("net.sourceforge.cobertura:cobertura:$coberturaVersion") {
        exclude(module: 'asm')
        exclude(module: 'asm')
        exclude(module: 'ant')
    }
    tools "org.ow2.asm:asm:$asmVersion"
    tools "com.thoughtworks.qdox:qdox:$qdoxVersion"

    antlr2 "org.apache.ant:ant-antlr:$antVersion"

    testImplementation project(':groovy-ant')
    testImplementation project(':groovy-xml')
    testImplementation project(':groovy-dateutil')
    testImplementation project(':groovy-test')
    testImplementation project(':groovy-macro')
    spotbugsPlugins 'com.h3xstream.findsecbugs:findsecbugs-plugin:1.12.0'
    testImplementation "net.jcip:jcip-annotations:$jcipAnnotationsVersion"
}

ext.generatedDirectory = "${buildDir}/generated/sources"

sourceSets {
    main {
        java {
            srcDir "$generatedDirectory/antlr2/src/main"
            if (!JavaVersion.current().isJava9Compatible()) {
                exclude '**/v9/*'
                exclude '**/vm9/*'
            }
        }
        groovy {
            srcDir "$generatedDirectory/antlr2/src/main"
            if (!JavaVersion.current().isJava9Compatible()) {
                exclude '**/v9/*'
                exclude '**/vm9/*'
            }
        }
        resources {
            srcDirs = ['src/resources']
        }
    }
    antlr2 {
        resources {
            srcDirs = ['src/main/antlr2']
        }
    }
    test {
        groovy {
            srcDirs = ['src/test']
            if (!JavaVersion.current().isJava9Compatible()) {
                exclude '**/v9/*'
                exclude '**/vm9/*'
            }
        }
        resources {
            srcDirs = ['src/test-resources']
        }
    }
    tools {
        compileClasspath = sourceSets.main.runtimeClasspath + configurations.tools
        runtimeClasspath = output + compileClasspath
    }
}

task sourceJar(type: Jar) {
    classifier = 'sources'
    duplicatesStrategy = DuplicatesStrategy.EXCLUDE
    from sourceSets.main.allSource
    from sourceSets.antlr2.allSource
}

subprojects {
    task sourceJar(type: Jar) {
        classifier = 'sources'
        duplicatesStrategy = DuplicatesStrategy.EXCLUDE
        from sourceSets.main.allSource
    }
}

apply from: 'subprojects/parser-antlr4/build.gradle'

task ensureGrammars {
    outputs.cacheIf { true }
    description = 'Ensure all the Antlr2 generated files are up to date.'
    ext.antlrDirectory = "$projectDir/src/main/antlr2/org/codehaus/groovy/antlr"
    ext.javaParserDirectory = "$ext.antlrDirectory/java"
    ext.genPath = "$generatedDirectory/antlr2/src/main/org/codehaus/groovy/antlr"
    ext.groovyOutDir = "$ext.genPath/parser"
    ext.javaOutDir = "$ext.genPath/java"
    inputs.dir(antlrDirectory)
    outputs.dir(groovyOutDir)
    outputs.dir(javaOutDir)
    doFirst {
        new File(groovyOutDir).mkdirs()
        new File(javaOutDir).mkdirs()
        ant {
            taskdef(name: 'antlr2',
                    classname: 'org.apache.tools.ant.taskdefs.optional.ANTLR',
                    classpath: configurations.antlr2.asPath)

            antlr2(target: "$ext.antlrDirectory/groovy.g", outputdirectory: ext.groovyOutDir) {
                classpath path: configurations.compileClasspath.asPath
            }
            antlr2(target: "$ext.javaParserDirectory/java.g", outputdirectory: ext.javaOutDir) {
                classpath path: configurations.compileClasspath.asPath
            }
        }
    }
}

compileJava {
    dependsOn ensureGrammars, generateGrammarSource
    options.fork(memoryMaximumSize: javacMain_mx)

    doLast {
        ant.java(classname:'org.jboss.bridger.Bridger', classpath: rootProject.configurations.tools.asPath, outputproperty: 'stdout') {
            arg(value: "${sourceSets.main.java.outputDir.canonicalPath}/org/codehaus/groovy/runtime/DefaultGroovyMethods.class")
            arg(value: "${sourceSets.main.java.outputDir.canonicalPath}/org/codehaus/groovy/runtime/StringGroovyMethods.class")
            arg(value: "${sourceSets.main.java.outputDir.canonicalPath}/org/codehaus/groovy/classgen/Verifier.class")
            arg(value: "${sourceSets.main.java.outputDir.canonicalPath}/org/codehaus/groovy/ast/tools/GeneralUtils.class")
        }
        ant.echo('Bridger (groovy): ' + ant.properties.stdout)
    }
}

task dgmConverter(dependsOn:compileJava) {
    outputs.cacheIf { true }

    description = 'Generates DGM info file required for faster startup.'
    def classpath = files(sourceSets.main.output.classesDirs, configurations.compileClasspath)
    ext.outputDir = file("$buildDir/dgm")

    //main = 'org.codehaus.groovy.tools.DgmConverter'
    //args = ['--info', classesDir.absolutePath]
    doFirst {
        file("$outputDir/META-INF").mkdirs()
        // we use ant.java because Gradle is a bit "too smart" with JavaExec
        // as it will invalidate the task if classpath changes, which will
        // happen once Groovy files are compiled
        ant.java(classname:'org.codehaus.groovy.tools.DgmConverter', classpath: classpath.asPath) {
            arg(value: '--info')
            arg(value: "$outputDir.absolutePath")
        }
    }
    inputs.files fileTree('src/main').include('**/*GroovyMethods.java')
    outputs.dir outputDir
}

    // Gradle classloading magic with Groovy will only work if it finds a *jar*
    // on classpath. This "bootstrap jar" contains the minimal compiler, without .groovy compiled files

task bootstrapJar(type: Jar) {
    dependsOn compileJava, dgmConverter

    from compileJava.destinationDir
    from dgmConverter.outputDir

    duplicatesStrategy = DuplicatesStrategy.EXCLUDE
    destinationDir = file("$buildDir/bootstrap")
    classifier = 'bootstrap'
}

allprojects {
    dependencies {
        compileOnly "com.github.spotbugs:spotbugs-annotations:$spotbugsAnnotationsVersion"
    }

    tasks.withType(AbstractCopyTask) { t ->
        duplicatesStrategy = t.name.contains('TestResources') ? DuplicatesStrategy.INCLUDE : DuplicatesStrategy.EXCLUDE
    }

    tasks.withType(JavaCompile) {
        options.encoding = 'UTF-8'
        options.incremental = true
        //options.compilerArgs << '-Xlint:unchecked' << '-Xlint:deprecation'

        if (classpath) {
            classpath = classpath + files(dgmConverter.outputDir)
        }
    }

    if (rootProject.indyCapable()) {
        task compileGroovyWithIndy(type: GroovyCompile) {
            source = sourceSets.main.groovy
            classpath = compileGroovy.classpath
            sourceCompatibility = 1.8
            targetCompatibility = 1.8
            groovyOptions.optimizationOptions['indy'] = true
            destinationDir = file("$buildDir/classes/indy")
        }
    }

    tasks.withType(GroovyCompile) {
        groovyOptions.forkOptions.jvmArgs += ['-Dgroovy.antlr4.cache.threshold=100']
        groovyOptions.fork(memoryMaximumSize: groovycMain_mx)
        groovyOptions.encoding = 'UTF-8'
        //options.compilerArgs << '-Xlint:unchecked' << '-Xlint:deprecation'
        groovyClasspath = files(
                rootProject.compileJava.classpath,
                files(project==rootProject?rootProject.bootstrapJar:rootProject.jar)
        )

        // TODO: this null check was required after adding JMH plugin to performance project
        classpath = (classpath != null) ? classpath + groovyClasspath : groovyClasspath
    }.configureEach {
        options.incremental = true
    }
}

compileTestGroovy {
    groovyOptions.fork(memoryMaximumSize: groovycTest_mx)
}

task checkCompatibility {
    doLast {
        assert JavaVersion.current().java9Compatible
    }
}

if (!JavaVersion.current().java9Compatible) {
    logger.lifecycle '''
    **************************************** WARNING ********************************************
    ******   You are running the build with an older JDK. NEVER try to release with 1.8.   ******
    ******   You must use a JDK 1.9+ in order to compile all features of the language.     ******
    *********************************************************************************************
'''
}

// Workaround to be able to access SpotBugsTask from external gradle script.
// More info: https://discuss.gradle.org/t/buildscript-dependencies-in-external-script/23243
project.extensions.extraProperties.set('SpotBugsTask', com.github.spotbugs.snom.SpotBugsTask)

apply from: 'gradle/test.gradle'
apply from: 'gradle/groovydoc.gradle'
apply from: 'gradle/docs.gradle'
apply from: 'gradle/assemble.gradle'
apply from: 'gradle/upload.gradle'
apply from: 'gradle/idea.gradle'
apply from: 'gradle/eclipse.gradle'
apply from: 'gradle/quality.gradle'
apply from: 'gradle/parallel-build-fixes.gradle'

// If a local configuration file for tweaking the build is present, apply it
if (file('user.gradle').exists()) {
    apply from: 'user.gradle'
}

apply from: 'gradle/signing.gradle'

licenseReport {
    excludeGroups = [
            'com.googlecode', // openbeans has no pom but is ASLv2
            'org.multiverse'  // we never include this optional dependency of an optional dependency
    ]
}

// UNCOMMENT THE FOLLOWING TASKS IF YOU WANT TO RUN LICENSE CHECKING
//task licenseFormatCustom(type:nl.javadude.gradle.plugins.license.License) {
//    source = fileTree(dir:'src').include ('**/*.java','**/*.groovy','**/*.html','**/*.css','**/*.xml','**/*.properties','**/*.properties')
//}
//
//task licenseFormatGradle(type:nl.javadude.gradle.plugins.license.License) {
//    source = files(fileTree(dir:projectDir).include('**/*.gradle'),fileTree('buildSrc').include('**/*.groovy'))
//}
//
//licenseFormat.dependsOn licenseFormatCustom
//licenseFormat.dependsOn licenseFormatGradle
//

def checkRepo(repo) {
    if (repo instanceof MavenArtifactRepository) {
        def repoUrl = repo.url.toString()
        if (repoUrl.startsWith('http://')) {
            throw new GradleException("Build should not use insecure HTTP-based URLs for repositories. Found: $repoUrl")
        }
    }
}

project.afterEvaluate {
    allprojects.repositories.each { handler ->
        handler.each { checkRepo(it) }
    }
    buildscript.repositories.each { handler ->
        handler.each { checkRepo(it) }
    }
}

def UNSTABLE = /^([\d.-]+(alpha|beta|rc|ea|b|m)[\d.-]+(groovy[\d.-]+)?|2.4.0-b[\d.]+|200\d{5}(\.\d{6})?|2004-03-19)$/
// ignore non-stable releases
tasks.named('dependencyUpdates')?.configure {
    gradleReleaseChannel = 'current'
    rejectVersionIf {
        !(it.currentVersion.toLowerCase() ==~ UNSTABLE) && it.candidate.version.toLowerCase() ==~ UNSTABLE
    }
}
