/*
 * Copyright (C) 2021. Uber Technologies
 *
 * 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.
 */
plugins {
    id 'java-library'
    id 'nullaway.java-test-conventions'
    id 'me.champeau.jmh'
}

configurations {
    // create a configuration for the sources and dependencies of each benchmark
    caffeineSources
    caffeineDeps

    autodisposeSources
    autodisposeDeps

    nullawayReleaseSources
    nullawayReleaseDeps
    nullawayReleaseProcessors
}
dependencies {

    // Add NullAway and Error Prone Core as dependencies.  This ensures that the classes get included
    // in the jmh-generated jar, and hence get JIT-compiled during benchmarking.  Without this dependence, NullAway
    // can still be loaded via the processor path, but it gets reloaded on each run of compilation, skewing
    // performance measurements
    implementation project(':nullaway')
    // use the same version of Error Prone Core that we are compiling NullAway against, so we can
    // benchmark against different versions of Error Prone
    implementation deps.build.errorProneCoreForApi


    // Source jars for our desired benchmarks
    caffeineSources('com.github.ben-manes.caffeine:caffeine:3.0.2:sources') {
        transitive = false
    }
    autodisposeSources('com.uber.autodispose2:autodispose:2.1.0:sources') {
        transitive = false
    }
    nullawayReleaseSources('com.uber.nullaway:nullaway:0.9.7:sources') {
        transitive = false
    }

    caffeineDeps 'com.github.ben-manes.caffeine:caffeine:3.0.2'
    autodisposeDeps 'com.uber.autodispose2:autodispose:2.1.0'
    nullawayReleaseDeps 'com.uber.nullaway:nullaway:0.9.7'
    // Add in the compile-only dependencies of NullAway
    // Use fixed versions here since we are compiling a particular version of NullAway
    nullawayReleaseDeps "com.google.errorprone:error_prone_core:2.13.1"
    nullawayReleaseDeps "com.facebook.infer.annotation:infer-annotation:0.11.0"
    nullawayReleaseDeps "org.jetbrains:annotations:13.0"

    // To run AutoValue during NullAway compilation
    nullawayReleaseProcessors "com.google.auto.value:auto-value:1.9"

    testImplementation deps.test.junit4
}

def caffeineSourceDir = project.layout.buildDirectory.dir('caffeineSources')
def autodisposeSourceDir = project.layout.buildDirectory.dir('autodisposeSources')
def nullawayReleaseSourceDir = project.layout.buildDirectory.dir('nullawayReleaseSources')

task extractCaffeineSources(type: Copy) {
    from zipTree(configurations.caffeineSources.singleFile)
    into caffeineSourceDir
}

task extractAutodisposeSources(type: Copy) {
    from zipTree(configurations.autodisposeSources.singleFile)
    into autodisposeSourceDir
}

task extractNullawayReleaseSources(type: Copy) {
    from zipTree(configurations.nullawayReleaseSources.singleFile)
    into nullawayReleaseSourceDir
}

compileJava.dependsOn(extractCaffeineSources)
compileJava.dependsOn(extractAutodisposeSources)
compileJava.dependsOn(extractNullawayReleaseSources)

// always run jmh
tasks.getByName('jmh').outputs.upToDateWhen { false }

// a trick: to get the classpath for a benchmark, create a configuration that depends on the benchmark, and
// then filter out the benchmark itself
def caffeineClasspath = configurations.caffeineDeps.filter({f -> !f.toString().contains("caffeine-3.0.2")}).asPath
def autodisposeClasspath = configurations.autodisposeDeps.filter({f -> !f.toString().contains("autodispose-2.1.0")}).asPath
def nullawayReleaseClasspath = configurations.nullawayReleaseDeps.filter({f -> !f.toString().contains("nullaway-0.9.7")}).asPath

def nullawayReleaseProcessorpath = configurations.nullawayReleaseProcessors.asPath

// Extra JVM arguments to expose relevant paths for compiling benchmarks
def extraJVMArgs = [
    "-Dnullaway.caffeine.sources=${caffeineSourceDir.get()}",
    "-Dnullaway.caffeine.classpath=$caffeineClasspath",
    "-Dnullaway.autodispose.sources=${autodisposeSourceDir.get()}",
    "-Dnullaway.autodispose.classpath=$autodisposeClasspath",
    "-Dnullaway.nullawayRelease.sources=${nullawayReleaseSourceDir.get()}",
    "-Dnullaway.nullawayRelease.classpath=$nullawayReleaseClasspath",
    "-Dnullaway.nullawayRelease.processorpath=$nullawayReleaseProcessorpath",
]

jmh {
    // seems we need more iterations to fully warm up the JIT
    warmupIterations = 10


    jvmArgsAppend = extraJVMArgs

    // commented-out examples of how to tweak other jmh parameters; they show the default values
    // for more examples see https://github.com/melix/jmh-gradle-plugin/blob/master/README.adoc#configuration-options
    // iterations = 5
    // fork = 5
    // includes = ['DFlowMicro']
}

tasks.named('test') {
    // pass the extra JVM args so we can compile benchmarks in unit tests
    jvmArgs += extraJVMArgs
}

tasks.getByName('testJdk21').configure {
    jvmArgs += extraJVMArgs
}
