/*
 * build.gradle
 *
 * This source file is part of the FoundationDB open source project
 *
 * Copyright 2015-2018 Apple Inc. and the FoundationDB project 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.
 */

buildscript {
    repositories {
        if (Boolean.parseBoolean(mavenLocalEnabled)) {
            mavenLocal()
            logger.warn 'Warning: you have enabled maven local repository. This should only be used on local workstations to test undeployed library dependencies.'
        }
        mavenCentral()
    }

    dependencies {
        classpath 'org.jboss.tattletale:tattletale:1.2.0.Beta2'
    }
}

plugins {
    alias(libs.plugins.protobuf)
    alias(libs.plugins.versions)
    alias(libs.plugins.spotbugs)
    alias(libs.plugins.nexus)
    alias(libs.plugins.download)
}

ext {
    SpotBugsTask = com.github.spotbugs.snom.SpotBugsTask
    fdbEnvironment = [:]
}

defaultTasks 'build'

apply from: 'gradle/root.gradle'

def isReleaseBuild = Boolean.parseBoolean(getProperty('releaseBuild'))

allprojects {
    apply plugin: 'base'
    apply plugin: 'java-library'
    apply plugin: 'maven-publish'
    apply plugin: 'signing'
    apply plugin: 'project-reports'
    apply plugin: 'com.github.ben-manes.versions'
    apply plugin: 'de.undercouch.download'

    // Configure all source files to be read using UTF-8
    project.tasks.withType(JavaCompile) {
        options.encoding = "utf-8"
    }
    project.tasks.withType(Javadoc) {
        options.encoding = "utf-8"
    }
    project.tasks.withType(Test) {
        systemProperty "file.encoding", "utf-8"
        if (project.hasProperty("apiVersion")) {
            systemProperty "com.apple.foundationdb.apiVersion", project.getProperty("apiVersion")
        }
    }

    // Propagate debugging option to tests
    // This makes sure that all tests are running in debug mode by default which is convenient, when e.g. working
    // with IntelliJ because we do not have to set the 'debugBuild' configuration for each test individually,
    // though this requires making sure that we set 'debugBuild' to false explicitly when producing a release
    // or snapshot build to make sure that the tests are _not_ running in that mode and rather running in prod-like
    // environment.
    project.tasks.withType(Test) {
        systemProperty "debugBuild", System.getProperty("debugBuild", "true")
        dependsOn("sourcesJar")
    }

    buildDir = ".out"
    ext {
        distDir = ".dist"
    }

    // Add -SNAPSHOT to version of non-release builds
    if (!isReleaseBuild) {
        def versionString = project.version.toString()
        if (!versionString.endsWith("-SNAPSHOT")) {
            project.version = versionString + "-SNAPSHOT"
        }
    }

    // Configure JUnit tests
    tasks.withType(Test) {
        reports.junitXml.outputLocation = project.file("${->project.buildDir}/test-results")
    }

    def projectName = it.name
    def projectVersion = it.version

    // Configure JAR generation
    tasks.jar.configure {
        description = "Produces a Jar with the main classes in .out/."
        manifest {
            attributes "Built-JDK": System.getProperty("java.version"),
                    "Specification-Title": projectName,
                    "Specification-Version": "${-> projectVersion}",
                    "Specification-Vendor": "Apple Inc.",
                    "Implementation-Title": projectName,
                    "Implementation-Version": "${-> projectVersion}",
                    "Implementation-Vendor": "Apple Inc."
        }
        doFirst {
            manifest {
                attributes "Built-Date": new Date(),
                        "Built-By": System.getProperty("user.name")
            }
        }
    }

    // Create the sources jar
    task sourcesJar(type: Jar, dependsOn: 'compileJava') {
        description = "Assembles a Jar archive containing the main sources."
        group = JavaBasePlugin.DOCUMENTATION_GROUP
        archiveAppendix = null
        archiveClassifier = "sources"
        from sourceSets.main.allSource
    }

    // Create the Javadoc jar
    task javadocJar(type: Jar) {
        description = "Assembles a Jar archive containing the main Javadoc."
        group = JavaBasePlugin.DOCUMENTATION_GROUP
        archiveAppendix = null
        archiveClassifier = "javadoc"
        from tasks.javadoc
    }

    // Distribution
    task createDistribution(type: Sync) {
        // source directory depends on the subproject
        into distDir
    }

    // Create umbrella task for all packaging operations
    tasks.create("package", DefaultTask) {
        description = "Produces main, sources, and Javadoc artifacts in .dist/."
        dependsOn createDistribution
    }

    build {
        dependsOn tasks.jar, tasks.package
    }

    signing {
        def signingKey = findProperty("signingKey")
        def signingPassword = findProperty("signingPassword")
        if (signingKey != null && signingPassword != null) {
            useInMemoryPgpKeys(signingKey, signingPassword)
        }
    }

    repositories {
        if (System.getenv("ARTIFACT_VERSION") != null) {
           version = "${System.getenv('ARTIFACT_VERSION')}"
        } else if (System.getenv("RECORD_LAYER_BUILD_NUMBER") != null) {
           version = "${version}.${System.getenv('RECORD_LAYER_BUILD_NUMBER')}"
        }
        if (Boolean.parseBoolean(mavenLocalEnabled)) {
            mavenLocal()
            logger.warn 'Warning: you have enabled maven local repository. This should only be used on local workstations to test undeployed library dependencies.'
        }
        mavenCentral()
    }
}

subprojects {
    apply from: rootProject.file('gradle/testing.gradle')

    java {
        sourceCompatibility = JavaVersion.VERSION_11
        targetCompatibility = JavaVersion.VERSION_11
    }

    def publishBuild = Boolean.parseBoolean(findProperty('publishBuild') ?: 'false')
    def autoServiceVersion = publishBuild ? libs.versions.autoService.asProvider().get() : libs.versions.autoService.development.get()

    project.configurations.all {
        resolutionStrategy.eachDependency {details ->
            if (details.requested.group == 'com.google.auto.service'
                    && details.requested.name == 'auto-service') {
                details.useVersion autoServiceVersion
            }
        }
    }

    ext {
        coreProjectName = "fdb-record-layer-core"
        shadedProjectName = "fdb-record-layer-core-shaded"
        publishLibrary = true
    }

    artifacts {
        add("archives", tasks.sourcesJar)
        add("archives", tasks.javadocJar)
        add("archives", tasks.testJar)
    }

    afterEvaluate { project ->
        if (plugins.hasPlugin('java')) {
            javadoc {
                // exclude generated Protobuf classes
                exclude "**/*Proto.java"
                exclude "**/*ProtoV3.java"
                exclude "com/apple/foundationdb/record/planprotos/*.java"
                exclude "com/apple/foundationdb/record/query/plan/cascades/debug/eventprotos/*.java"
                options.addBooleanOption('Xwerror', true)

		// Quiet javadoc errors for missing comments, etc.
		// It would be good to have more coverage, but this allows us to move forward with incomplete docs
                options.addBooleanOption('Xdoclint:none', true)

                options.addStringOption('source', '11')
                options.with {
                    overview "src/main/javadoc/overview.html"

                    // Make our Javadocs link to external resources
                    // These use "linksOffline" in order to ensure that we can do offline builds as well as to tweak
                    // a few package lists. These should be periodically regenerated by running the
                    // ${packageListDir}/genpackagelists.py script. (See that script for more details.)
                    def packageListDir = "${project.rootDir}/build/package-lists"
                    linksOffline "https://apple.github.io/foundationdb/javadoc/", "${packageListDir}/fdb-java/"
                    linksOffline "https://developers.google.com/protocol-buffers/docs/reference/java/", "${packageListDir}/protobuf/"

                    // This is for dependent sub-projects so that their links to things in the Record Layer work
                    linksOffline "https://foundationdb.github.io/fdb-record-layer/api/fdb-extensions/", "${packageListDir}/fdb-extensions/"
                    linksOffline "https://foundationdb.github.io/fdb-record-layer/api/fdb-record-layer-core/", "${packageListDir}/fdb-record-layer-core/"
                }
            }
            compileJava {
                //enable compilation in a separate daemon process
                options.fork = true

                //enable incremental compilation
                options.incremental = true

                //target byte-code compatibility with Java 11 (regardless of build JDK)
                options.release = 11
            }
        }
    }

    apply from: rootProject.file('gradle/check.gradle')
    apply from: rootProject.file('gradle/strict.gradle')

    if(System.getenv("DO_NOT_CHECK") != null) {
        task check(overwrite: true, dependsOn: quickCheck) { }
    }

    check.dependsOn quickCheck

    // At the end of a release build, we want to update all the yamsql files to replace !current_version with the
    // version being built
    task updateYamsql(type: Task) {
        mustRunAfter 'test'
        mustRunAfter 'build'
        doLast {
            if (isReleaseBuild) {
                // don't replace !current_version in the supported-version resources, as those are there to test
                // supported_version, we want the !current_version to stick around
                println("Replacing !current_version...")
                sourceSets["test"].resources.srcDirs.stream().flatMap( resourceDir ->
                    fileTree(dir: resourceDir).matching {
                        include '**/*.yamsql'
                        exclude '/initial-version/*'
                        exclude '/supported-version/*'
                    }.stream()
                ).forEach { yamsql ->
                    def original = yamsql.text
                    def updated = original.replaceAll("(?<!\")!current_version(?!\")", project.version)
                    if (!original.equals(updated)) {
                        println("Replacing !current_version in " + yamsql)
                        // Replace !current_version in all yamsql, unless it is surrounded by ", allowing it to be used in
                        // comments, without issue, e.g. in showcasing-tests
                        yamsql.write(updated)
                        exec {
                            workingDir '.'
                            commandLine 'git', 'add', yamsql
                        }
                    }
                }
            }
        }
    }
}

// Configure publishing for maven central. This is done in the top-level build.gradle, and then
// all of the subprojects configure the artifacts they want published by applying
// ${rootDir}/gradle/publishing.gradle in the project gradle. By default, we publish a library
// from each package, but this can be configured by adjusting the publishLibrary variable
if (Boolean.parseBoolean(centralPublish)) {
    nexusPublishing {
        repositories {
            sonatype {
                // Update the URL now that the OSSRH service has been sunset: https://central.sonatype.org/news/20250326_ossrh_sunset/
                nexusUrl.set(uri("https://ossrh-staging-api.central.sonatype.com/service/local/"))
            }
        }
    }
}

// Script for upgrading gradle. To upgrade the gradle version, set the property to the version
// you want and run the command _twice_.
wrapper {
    def gradleVersion = getProperty("gradleVersion") ?: project.gradle.gradleVersion
    conventionMapping.distributionUrl = {
        // use gradle-all rather than gradle-bin distribution because IntelliJ's Gradle integration
        // works better with the former
        "https://services.gradle.org/distributions/gradle-$gradleVersion-all.zip" as String
    }
}

clean.doLast {
    delete ".idea_build"
    allprojects.each { p ->
        delete p.tasks.createDistribution.destinationDir
    }
}

apply from: 'gradle/sphinx.gradle'

if (!JavaVersion.current().isJava8Compatible()) {
    throw new Exception("Java 8 is required to build fdb-record-layer")
}

def fdbEnvironmentFile = new File("${rootProject.projectDir}/fdb-environment.properties")
if (fdbEnvironmentFile.exists()) {
    fdbEnvironmentFile.eachLine { line ->
        def eq = line.indexOf('=')
        if (eq > 0) ext.fdbEnvironment.put(line.substring(0, eq), line.substring(eq + 1))
    }
}
if (!ext.fdbEnvironment.isEmpty()) {
    def fdbenv = ext.fdbEnvironment
    allprojects {
        tasks.withType(Test) { task ->
            task.environment(fdbenv)
        }
    }
}
