description 'Corda core (deterministic)'

apply from: '../deterministic.gradle'
apply plugin: 'com.jfrog.artifactory'
apply plugin: 'net.corda.plugins.publish-utils'
apply plugin: 'java-library'
apply plugin: 'idea'

evaluationDependsOn(":core")

// required by DJVM and Avian JVM (for running inside the SGX enclave) which only supports Java 8.
targetCompatibility = VERSION_1_8

def javaHome = System.getProperty('java.home')
def jarBaseName = "corda-${project.name}".toString()

configurations {
    deterministicLibraries.extendsFrom api
    deterministicArtifacts.extendsFrom deterministicLibraries
}

dependencies {
    compileOnly project(':core')

    // Configure these by hand. It should be a minimal subset of core's dependencies,
    // and without any obviously non-deterministic ones such as Hibernate.

    // These "api" dependencies will become "compile" scoped in our published POM.
    api "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
    api "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version"
    api "javax.persistence:javax.persistence-api:2.2"
    api "com.google.code.findbugs:jsr305:$jsr305_version"
    api "org.slf4j:slf4j-api:$slf4j_version"

    // These dependencies will become "runtime" scoped in our published POM.
    // See publish.dependenciesFrom.defaultScope.
    deterministicLibraries "org.bouncycastle:bcprov-jdk15on:$bouncycastle_version"
    deterministicLibraries "org.bouncycastle:bcpkix-jdk15on:$bouncycastle_version"
    deterministicLibraries "net.i2p.crypto:eddsa:$eddsa_version"
}

jar {
    archiveBaseName = 'DOES-NOT-EXIST'
    // Don't build a jar here because it would be the wrong one.
    // The jar we really want will be built by the metafix task.
    enabled = false
}

def coreJarTask = tasks.getByPath(':core:jar')
def originalJar = coreJarTask.outputs.files.singleFile

task patchCore(type: Zip, dependsOn: coreJarTask) {
    destinationDirectory = file("$buildDir/source-libs")
    metadataCharset 'UTF-8'
    archiveClassifier = 'transient'
    archiveExtension = 'jar'

    from(compileKotlin)
    from(processResources)
    from(zipTree(originalJar)) {
        exclude 'net/corda/core/crypto/DelegatingSecureRandomService*.class'
        exclude 'net/corda/core/crypto/SHA256DigestSupplier.class'
        exclude 'net/corda/core/internal/*ToggleField*.class'
        exclude 'net/corda/core/serialization/*SerializationFactory*.class'
        exclude 'net/corda/core/serialization/internal/CheckpointSerializationFactory*.class'
        exclude 'net/corda/core/internal/rules/*.class'
    }

    reproducibleFileOrder = true
    includeEmptyDirs = false
}

import proguard.gradle.ProGuardTask
task predeterminise(type: ProGuardTask) {
    injars patchCore
    outjars file("$buildDir/proguard/pre-deterministic-${project.version}.jar")

    if (JavaVersion.current().isJava9Compatible()) {
        libraryjars "$javaHome/jmods"
    } else {
        libraryjars "$javaHome/lib/rt.jar"
        libraryjars "$javaHome/lib/jce.jar"
    }
    configurations.compileClasspath.forEach {
        if (originalJar != it) {
            libraryjars it, filter: '!META-INF/versions/**'
        }
    }

    keepattributes '*'
    keepdirectories
    dontwarn '**$1$1,org.hibernate.annotations.*'
    dontpreverify
    dontobfuscate
    dontoptimize
    dontnote
    printseeds
    verbose

    keep '@interface net.corda.core.* { *; }'
    keep '@interface net.corda.core.contracts.** { *; }'
    keep '@interface net.corda.core.serialization.** { *; }'
    keep '@net.corda.core.KeepForDJVM class * { *; }', includedescriptorclasses:true
    keepclassmembers 'class net.corda.core.** { public synthetic <methods>; }'
}

import net.corda.gradle.jarfilter.JarFilterTask
task jarFilter(type: JarFilterTask) {
    jars predeterminise
    annotations {
        forDelete = [
            "net.corda.core.DeleteForDJVM"
        ]
        forStub = [
            "net.corda.core.StubOutForDJVM"
        ]
        forRemove = [
            "co.paralleluniverse.fibers.Suspendable",
            "org.hibernate.annotations.Immutable"
        ]
        forSanitise = [
            "net.corda.core.DeleteForDJVM"
        ]
    }
}

task determinise(type: ProGuardTask) {
    injars jarFilter
    outjars file("$buildDir/proguard/$jarBaseName-${project.version}.jar")

    if (JavaVersion.current().isJava9Compatible()) {
        libraryjars "$javaHome/jmods"
    } else {
        libraryjars "$javaHome/lib/rt.jar"
        libraryjars "$javaHome/lib/jce.jar"
    }
    configurations.deterministicLibraries.forEach {
        libraryjars it, filter: '!META-INF/versions/**'
    }

    // Analyse the JAR for dead code, and remove (some of) it.
    optimizations 'code/removal/simple,code/removal/advanced'
    printconfiguration

    keepattributes '*'
    keepdirectories
    dontobfuscate
    dontnote
    printseeds
    verbose

    keep '@interface net.corda.core.CordaInternal { *; }'
    keep '@interface net.corda.core.DoNotImplement { *; }'
    keep '@interface net.corda.core.KeepForDJVM { *; }'
    keep '@interface net.corda.core.contracts.** { *; }'
    keep '@interface net.corda.core.serialization.** { *; }'
    keep '@net.corda.core.KeepForDJVM class * { *; }', includedescriptorclasses:true
    keepclassmembers 'class net.corda.core.** { public synthetic <methods>; }'
}

import net.corda.gradle.jarfilter.MetaFixerTask
import static org.gradle.api.JavaVersion.VERSION_1_8

task metafix(type: MetaFixerTask) {
    outputDir file("$buildDir/libs")
    jars determinise
    suffix ""

    // Strip timestamps from the JAR to make it reproducible.
    preserveTimestamps = false
}

// DOCSTART 01
task checkDeterminism(type: ProGuardTask, dependsOn: jdkTask) {
    injars metafix

    libraryjars deterministic_rt_jar

    configurations.deterministicLibraries.forEach {
        libraryjars it, filter: '!META-INF/versions/**'
    }

    keepattributes '*'
    dontpreverify
    dontobfuscate
    dontoptimize
    verbose

    keep 'class *'
}
// DOCEND 01

defaultTasks "determinise"
determinise.finalizedBy metafix
metafix.finalizedBy checkDeterminism
assemble.dependsOn checkDeterminism

def deterministicJar = metafix.outputs.files.singleFile
artifacts {
    deterministicArtifacts file: deterministicJar, name: jarBaseName, type: 'jar', extension: 'jar', builtBy: metafix
    publish file: deterministicJar, name: jarBaseName, type: 'jar', extension: 'jar', builtBy: metafix
}

publish {
    dependenciesFrom configurations.deterministicArtifacts
    publishSources = false
    publishJavadoc = false
    name jarBaseName
}

idea {
    module {
        if (project.hasProperty("deterministic_idea_sdk")) {
            jdkName project.property("deterministic_idea_sdk") as String
        }
    }
}
