ext {
    // See instructions for updating jdk8.jar at
    // https://github.com/typetools/annotated-libraries/blob/master/README.md .
    // Set jdkShaHash to 'local' to use a locally-built version of jdk8.jar .
    jdkShaHash = '54dceee1857209c0f6f90fe9ef9f45655e26642b'
    if (rootProject.hasProperty("useLocalJdk")) {
        jdkShaHash = 'local'
    }

    jdkHome = '../../jdk'
}
sourceSets {
    main {
        resources {
            // Stub files, message.properties, etc.
            srcDirs += ['src/main/java', "${buildDir}/jdk"]
        }
    }
    testannotations
}

dependencies {
    compile project(':framework')
    compile project(':dataflow')
    compile project(':javacutil')
    compile project(':checker-qual')

    testCompile group: 'junit', name: 'junit', version: '4.12'
    testCompile project(':framework-test')
    testCompile sourceSets.testannotations.output

    testannotationsCompile project(':checker-qual')
}

task cloneTypetoolsJdk() {
    description 'Obtain or update the jdk'
    if (file(jdkHome).exists()) {
        exec {
            workingDir jdkHome
            executable 'git'
            args = ['pull', '-q']
            ignoreExitValue = true
        }
    } else {
        exec {
            workingDir "${jdkHome}/../"
            executable 'git'
            args = ['clone', '-q', 'https://github.com/typetools/jdk.git', 'jdk']
        }
    }
}

task copyJdk11Files(dependsOn: cloneTypetoolsJdk, group: 'Build') {
    description "Copy annotated jdk 11 files to ${buildDir}/jdk/jdk11/"
    doLast {
        FileTree tree = fileTree(dir: "${jdkHome}/src/")
        SortedSet<String> annotatedForFiles = new TreeSet<>();
        tree.visit { FileVisitDetails fvd ->
            if (!fvd.file.isDirectory() && fvd.file.name.matches(".*\\.java")) {
                fvd.getFile().readLines().any { line ->
                    if (line.contains("@AnnotatedFor") || line.contains("org.checkerframework")) {
                        annotatedForFiles.add(fvd.file.absolutePath)
                        return true;
                    }
                }
            }
        }
        String absolutejdkHome = file(jdkHome).absolutePath
        int jdkDirStringSize = absolutejdkHome.size()
        copy {
            from(jdkHome)
            into("${buildDir}/jdk/jdk11/")
            for (String filename : annotatedForFiles) {
                include filename.substring(jdkDirStringSize)
            }
        }
    }
}

processResources.dependsOn(copyJdk11Files)

jar {
    manifest {
        attributes("Main-Class": "org.checkerframework.framework.util.CheckerMain")
    }
    doLast {
        new File("$projectDir/build/libs/README.txt").text =
"""Do not use file checker-X.Y.Z.jar, which contains only the checker subproject
and lacks other parts of the Checker Framework.
Instead, use checker/dist/checker.jar.
"""
    }
}

task copyJarsOtherThanJdk8ToDist(dependsOn: shadowJar, group: 'Build') {
    description 'Builds jars required by CheckerMain, except for jdk8.jar, and puts them in checker/dist.'
    dependsOn project(':checker-qual').tasks.jar
    // Also moves jdk8.jar to checker/dist
    doLast {
        copy {
            from file(project(':checker-qual').tasks.getByName("jar").archivePath)
            into "${projectDir}/dist"
            rename { String fileName ->
                // remove version number on checker-qual.jar
                fileName.replace(fileName, "checker-qual.jar")
            }
        }

        copy {
            from configurations.javacJar
            into "${projectDir}/dist"
            rename { String fileName ->
                fileName.replace(fileName, "javac.jar")
            }
        }
    }
}

task forceBuildJdk(group: 'Build') {
    description 'Builds jdk.jar even if no file in the JDK has changed. ' +
            'Use to update jdk.jar if defaulting has changed in a checker.'
    doLast {
        delete 'jdk/jdk.jar'
        buildJdk.execute()
    }
}

task buildJdk(group: 'Build', dependsOn: copyJarsOtherThanJdk8ToDist) {
    description 'Builds jdk8.jar. Requires -PuseLocalJdk, or that jdkShaHash == \'local\' in checker/build.gradle.'

    doLast {
        if (System.getenv('JAVA_HOME') == null) {
            throw new GradleException("Environment variable JAVA_HOME is not set.")
        }
        if (!jdkShaHash.is('local')) {
            throw new GradleException('To build jdk.jar, pass -PuseLocalJdk, or set jdkShaHash to \'local\' in checker/build.gradle.')
        }
        delete 'dist/jdk8.jar'
        exec {
            executable 'make'
            environment CHECKERFRAMEWORK: "${projectDir}/..",
                    ANNOTATION_TOOLS: "${afu}/..",
                    CLASSPATH: project(':checker').tasks.getByName('shadowJar').archivePath
            args += ["-C", "jdk/"]
        }

        copy {
            from 'jdk/jdk.jar'
            rename { String fileName ->
                fileName.replace(fileName, "jdk8.jar")
            }
            into 'jdk'
        }

        copy {
            from 'jdk/jdk8.jar'
            into 'dist'
        }
    }
}

task updateJdk() {
    description 'Downloads or builds jdk8.jar and copies into checker/dist'
    if (jdkShaHash.is('local')) {
        dependsOn(buildJdk)
    }
    doLast {
        if (!jdkShaHash.is('local')) {
            File downloadedJdk = new File(buildDir, "jdk8-${jdkShaHash}.jar");
            if (!downloadedJdk.exists()) {
                download {
                    src "https://github.com/typetools/annotated-libraries/raw/${jdkShaHash}/jdk8.jar"
                    overwrite true
                    dest downloadedJdk
                }
            }
            copy {
                from(downloadedJdk) {
                    rename downloadedJdk.name, 'jdk8.jar'
                }
                into "dist"
            }
        }
    }
}

task printJdkJarManifest(type: Copy) {
    description 'Outputs the manifest file for checker/dist/jdk8.jar'
    outputs.upToDateWhen { false }

    from(zipTree('dist/jdk8.jar')) {
        include "META-INF/MANIFEST.MF"
    }

    String tmpDir = "${buildDir}/tmpJDKManifest"
    into tmpDir
    doLast {
        println(new File(tmpDir, 'META-INF/MANIFEST.MF').text)
        delete tmpDir
    }

}

task copyJarsToDist(dependsOn: shadowJar, group: 'Build') {
    description 'Builds or downloads jars required by CheckerMain and puts them in checker/dist.'
    dependsOn project(':checker-qual').tasks.jar
    // Also moves jdk8.jar to checker/dist
    dependsOn updateJdk
    dependsOn copyJarsOtherThanJdk8ToDist
}
assemble.dependsOn copyJarsToDist

task printPlumeUtilJarPath {
    description "Print the path to plume-util.jar"
    doFirst { println project.configurations.compile.find { it.name.startsWith("plume-util") } }
}

task allSourcesJar(type: Jar) {
    description 'Creates a sources jar that includes sources for all Checker Framework classes in checker.jar'
    destinationDirectory = file("${projectDir}/dist")
    archiveFileName = "checker-source.jar"
    from (sourceSets.main.java, project(':framework').sourceSets.main.allJava,
            project(':dataflow').sourceSets.main.allJava, project(':javacutil').sourceSets.main.allJava)
}

task allJavadocJar(type: Jar) {
    description 'Creates javadoc jar include Javadoc for all of the framework'
    dependsOn rootProject.tasks.allJavadoc
    destinationDirectory = file("${projectDir}/dist")
    archiveFileName = "checker-javadoc.jar"
    from rootProject.tasks.allJavadoc.destinationDir
}


shadowJar {
    description 'Creates the "fat" checker.jar in dist/.'
    destinationDirectory = file("${projectDir}/dist")
    archiveFileName = "checker.jar"
    // To see what files are incorporated into the shadow jar file:
    // doFirst { println sourceSets.main.runtimeClasspath.asPath }
}
artifacts {
    // Don't add this here or else the Javadoc and the sources jar is built during the assemble task.
    // archives allJavadocJar
    // archives allSourcesJar
    archives shadowJar
}

clean {
    delete "${projectDir}/dist"
}

// Add non-junit tests
createCheckTypeTask(project.name, 'org.checkerframework.checker.compilermsgs.CompilerMessagesChecker', "CompilerMessages")
checkCompilerMessages {
    doFirst {
        options.compilerArgs += [
                '-Apropfiles=' + sourceSets.main.resources.filter { file -> file.name.equals('messages.properties') }.asPath + ":"
                        + project(':framework').sourceSets.main.resources.filter { file -> file.name.equals('messages.properties') }.asPath
        ]
    }
}

task nullnessExtraTests(type: Exec, dependsOn: copyJarsToDist, group: 'Verification') {
    description 'Run extra tests for the Nullness Checker.'
    executable 'make'
    environment JAVAC: "${projectDir}/bin/javac", JAVAP: 'javap'
    args = ['-C', 'tests/nullness-extra/']
}

task commandLineTests(type: Exec, dependsOn: copyJarsToDist, group: 'Verification') {
    description 'Run tests that need a special command line.'
    executable 'make'
    environment JAVAC: "${projectDir}/bin/javac"
    args = ['-C', 'tests/command-line/']
}

task tutorialTests(dependsOn: copyJarsToDist, group: 'Verification') {
    description 'Test that the tutorial is working as expected.'
    doLast {
        ant.ant(dir: "${rootDir}/docs/tutorial/tests", useNativeBasedir: 'true', inheritAll: 'false') {
            target(name: 'check-tutorial')
        }
    }
}

task exampleTests(type: Exec, dependsOn: copyJarsToDist, group: 'Verification') {
    description 'Run tests for the example programs.'
    executable 'make'
    environment JAVAC: "${projectDir}/bin/javac"
    args = ['-C', '../docs/examples']
}

task demosTests(dependsOn: copyJarsToDist, group: 'Verification') {
    description 'Test that the demos are working as expected.'
    doLast {
        if (JavaVersion.current() == JavaVersion.VERSION_1_8) {
            File demosDir = new File(projectDir, '../../checker-framework-demos');
            if (!demosDir.exists()) {
                exec {
                    workingDir file(demosDir.toString() + '/../')
                    executable 'git'
                    args = ['clone', 'https://github.com/typetools/checker-framework.demos.git', 'checker-framework-demos']
                }
            } else {
                exec {
                    workingDir demosDir
                    executable 'git'
                    args = ['pull', 'https://github.com/typetools/checker-framework.demos.git']
                    ignoreExitValue = true
                }
            }
            ant.properties.put('checker.lib', file("${projectDir}/dist/checker.jar").absolutePath)
            ant.ant(dir: demosDir.toString())
        } else {
            println("Skipping demosTests because they only work with Java 8.")
        }
    }
}

task allNullnessTests(type: Test, group: 'Verification') {
    description 'Run all Junit tests for the Nullness Checker.'
    include '**/Nullness*.class'
}

// These are tests that should only be run with JDK 11.
task jtregJdk11Tests(dependsOn: ':downloadJtreg', group: 'Verification') {
    description 'Run the jtreg tests made for JDK 11.'
    dependsOn('compileJava')
    dependsOn('compileTestJava')
    dependsOn(':checker:updateJdk')
    dependsOn('shadowJar')

    String jtregOutput = "${buildDir}/jtregJdk11"
    String name = 'all'
    doLast {
        if(isJava8) {
            println "This test is only run with JDK 11."
            return;
        }
        exec {
            executable "${jtregHome}/bin/jtreg"
            args = [
                    "-dir:${projectDir}/jtregJdk11",
                    "-workDir:${jtregOutput}/${name}/work",
                    "-reportDir:${jtregOutput}/${name}/report",
                    "-verbose:summary",
                    "-javacoptions:-g",
                    "-keywords:!ignore",
                    "-samevm",
                    "-javacoptions:-classpath ${tasks.shadowJar.archiveFile.get()}:${sourceSets.test.output.asPath}",
                    "-vmoptions:-classpath ${tasks.shadowJar.archiveFile.get()}:${sourceSets.test.output.asPath}",
                    "-vmoptions:--add-opens=jdk.compiler/com.sun.tools.javac.comp=ALL-UNNAMED",
                    "-javacoptions:-classpath ${sourceSets.testannotations.output.asPath}",
                    // Location of jtreg tests
                    '.'
            ]
        }


    }
}
