Skip to content

Commit 406f7ff

Browse files
authored
ENT-11056: Compile the external verifier using Kotlin 1.2 (corda#7622)
This requires Kotlin 1.2 versions of core and serialization (core-1.2 and serialization-1.2 respectively), which are just "shell" modules and which compile the existing source code with Kotlin 1.2. The 1.2 plugin does not work with the current version of Gradle and so the 1.2 compiler has to be called directly. Now with two versions of Kotlin in the code base, each module needs to have its version manually specified to ensure a clean separation. Otherwise, the default Kotlin version can override 1.2 when needed. Some of the code was tidied-up or improved to enable it to be cross-compiled. For post-1.2 APIs being used, they have been copied into core-1.2 with the same method signatures. OpenTelemetryComponent was moved to node-api, along with the dependency, to avoid also having a 1.2 version for the opentelemetry module.
1 parent 4791f0d commit 406f7ff

File tree

49 files changed

+582
-254
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

49 files changed

+582
-254
lines changed

build.gradle

+24-20
Original file line numberDiff line numberDiff line change
@@ -245,7 +245,7 @@ logger.lifecycle("Building Corda version: {}", corda_release_version)
245245
logger.lifecycle("User home: {}", System.getProperty('user.home'))
246246

247247
allprojects {
248-
apply plugin: 'org.jetbrains.kotlin.jvm'
248+
apply plugin: 'java'
249249
apply plugin: 'kotlin-allopen'
250250
apply plugin: 'jacoco'
251251
apply plugin: 'org.owasp.dependencycheck'
@@ -437,10 +437,12 @@ allprojects {
437437
}
438438

439439
configurations {
440-
all {
440+
configureEach {
441441
resolutionStrategy {
442-
// Force dependencies to use the same version of Kotlin as Corda.
443-
force "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version"
442+
if (pluginManager.hasPlugin("org.jetbrains.kotlin.jvm")) {
443+
// Force dependencies to use the same version of Kotlin as Corda.
444+
force "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version"
445+
}
444446

445447
// Force dependencies to use the same version of Guava as Corda.
446448
force "com.google.guava:guava:$guava_version"
@@ -487,22 +489,24 @@ allprojects {
487489
// Effectively delete this unused and unwanted transitive dependency of Artemis.
488490
substitute module('org.jgroups:jgroups') with module("org.apache.activemq:artemis-commons:$artemis_version")
489491
}
492+
493+
// FORCE Gradle to use latest SNAPSHOT dependencies.
494+
cacheChangingModulesFor 0, 'seconds'
490495
}
491496
}
492497

493-
// Select all of the compileClasspath and runtimeClasspath etc configurations,
494-
// but NOT the "classpath" configuration, as that is used by the Gradle plugins.
495-
matching { it.name.endsWith("Classpath") }.configureEach { cfg ->
496-
cfg.resolutionStrategy {
497-
dependencySubstitution {
498-
// Force dependencies to use the same version of Kotlin as Corda.
499-
substitute module('org.jetbrains.kotlin:kotlin-stdlib-common') with module("org.jetbrains.kotlin:kotlin-stdlib-common:$kotlin_version")
500-
substitute module('org.jetbrains.kotlin:kotlin-stdlib') with module("org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version")
501-
substitute module('org.jetbrains.kotlin:kotlin-reflect') with module("org.jetbrains.kotlin:kotlin-reflect:$kotlin_version")
498+
if (pluginManager.hasPlugin("org.jetbrains.kotlin.jvm")) {
499+
// Select all of the compileClasspath and runtimeClasspath etc configurations,
500+
// but NOT the "classpath" configuration, as that is used by the Gradle plugins.
501+
matching { it.name.endsWith("Classpath") }.configureEach { cfg ->
502+
cfg.resolutionStrategy {
503+
dependencySubstitution {
504+
// Force dependencies to use the same version of Kotlin as Corda.
505+
substitute module('org.jetbrains.kotlin:kotlin-stdlib-common') with module("org.jetbrains.kotlin:kotlin-stdlib-common:$kotlin_version")
506+
substitute module('org.jetbrains.kotlin:kotlin-stdlib') with module("org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version")
507+
substitute module('org.jetbrains.kotlin:kotlin-reflect') with module("org.jetbrains.kotlin:kotlin-reflect:$kotlin_version")
508+
}
502509
}
503-
504-
// FORCE Gradle to use latest SNAPSHOT dependencies.
505-
cacheChangingModulesFor 0, 'seconds'
506510
}
507511
}
508512
}
@@ -557,9 +561,9 @@ tasks.register('jacocoRootReport', JacocoReport) {
557561
// classDirectories = files(subprojects.sourceSets.main.output)
558562
// executionData = files(subprojects.jacocoTestReport.executionData)
559563
reports {
560-
html.enabled = true
561-
xml.enabled = true
562-
csv.enabled = false
564+
html.required = true
565+
xml.required = true
566+
csv.required = false
563567
}
564568
onlyIf = {
565569
true
@@ -595,7 +599,7 @@ tasks.register('detektBaseline', JavaExec) {
595599
}
596600

597601
tasks.withType(Test).configureEach {
598-
reports.html.destination = file("${reporting.baseDir}/${name}")
602+
reports.html.outputLocation.set(file("${reporting.baseDir}/${name}"))
599603
}
600604

601605
tasks.register('testReport', TestReport) {

buildSrc/build.gradle

+3-2
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,9 @@ repositories {
4646
}
4747

4848
dependencies {
49-
implementation group: 'com.github.docker-java', name: 'docker-java', version: constants.dockerJavaVersion
50-
implementation group: 'com.github.docker-java', name: 'docker-java-transport-httpclient5', version: constants.dockerJavaVersion
49+
implementation "com.github.docker-java:docker-java:$constants.dockerJavaVersion"
50+
implementation "com.github.docker-java:docker-java-transport-httpclient5:$constants.dockerJavaVersion"
51+
implementation "org.jooq:joor:$constants.joorVersion"
5152

5253
if (System.getenv('CORDA_ARTIFACTORY_USERNAME') != null || project.hasProperty('cordaArtifactoryUsername')) {
5354
implementation "com.r3.internal.gradle.plugins:publish:$internalPublishVersion"
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
import org.gradle.api.internal.file.DefaultSourceDirectorySet
2+
3+
import static org.joor.Reflect.onClass
4+
5+
pluginManager.apply(Kotlin12Plugin.class)
6+
7+
// We cannot use the 1.2 Kotlin plugin as it only works with a very old version of Gradle, which itself will only work on Java 8. So we need
8+
// our own plugin which calls the 1.2 compiler directly.
9+
class Kotlin12Plugin implements Plugin<Project> {
10+
private static final KOTLIN_VERSION = "1.2.71"
11+
12+
@Override
13+
void apply(Project project) {
14+
project.pluginManager.apply(JavaPlugin.class)
15+
16+
project.extensions.add("kotlin_1_2_version", KOTLIN_VERSION)
17+
project.dependencies.add("implementation", "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$KOTLIN_VERSION")
18+
19+
def kotlinCompilerConfiguration = project.configurations.create("kotlinCompiler")
20+
project.dependencies.add("kotlinCompiler", "org.jetbrains.kotlin:kotlin-compiler:$KOTLIN_VERSION")
21+
22+
project.extensions.getByType(JavaPluginExtension.class).sourceSets.configureEach { sourceSet ->
23+
// Create the "src/*/kotlin" SourceDirectorySet, alongside the "java" one
24+
def kotlinSourceDirectorySet = new DefaultSourceDirectorySet(project.objects.sourceDirectorySet("kotlin", "${sourceSet.displayName} Kotlin source"))
25+
sourceSet.extensions.add(SourceDirectorySet.class, "kotlin", kotlinSourceDirectorySet)
26+
kotlinSourceDirectorySet.filter.include("**/*.java", "**/*.kt")
27+
kotlinSourceDirectorySet.srcDir(project.file("src/${sourceSet.name}/kotlin"))
28+
29+
def allKotlin = project.objects.sourceDirectorySet("allkotlin", "${sourceSet.displayName} Kotlin source")
30+
allKotlin.filter.include("**/*.kt")
31+
allKotlin.source(kotlinSourceDirectorySet)
32+
33+
sourceSet.allJava.source(kotlinSourceDirectorySet)
34+
sourceSet.allSource.source(kotlinSourceDirectorySet)
35+
36+
def kotlinBuildDir = project.layout.buildDirectory.dir("classes/kotlin/${sourceSet.name}")
37+
sourceSet.output.dir(kotlinBuildDir)
38+
39+
def taskSourceSetName = isMain(sourceSet) ? "" : sourceSet.name.capitalize()
40+
41+
def compileKotlin = project.tasks.register("compile${taskSourceSetName}Kotlin", KotlinCompile.class) { task ->
42+
// The 1.2 compiler needs to be laoded in a separate class loader, as the build classpath already contains its own version
43+
// of Kotlin.
44+
task.compilerClasspath.from(kotlinCompilerConfiguration)
45+
task.source(allKotlin)
46+
// Paradoxically, the Java sources are also required by the Kotlin compiler. This is actually so that it can correctly
47+
// resolve any references the Kotlin code makes to Java code.
48+
task.source(sourceSet.allJava)
49+
task.classpath = sourceSet.compileClasspath
50+
task.destinationDirectory = kotlinBuildDir
51+
}
52+
53+
// Compiling the Java code needs the compiled Kotlin code first
54+
project.tasks.named("compile${taskSourceSetName}Java", JavaCompile.class) { task ->
55+
task.classpath += project.files(compileKotlin.map { it.destinationDirectory })
56+
}
57+
}
58+
}
59+
}
60+
61+
abstract class KotlinCompile extends AbstractCompile {
62+
@Classpath
63+
abstract ConfigurableFileCollection getCompilerClasspath()
64+
65+
@TaskAction
66+
void compile() {
67+
def args = [
68+
"-jvm-target", "1.8",
69+
"-language-version", "1.2",
70+
"-api-version", "1.2",
71+
"-java-parameters",
72+
"-Xjvm-default=compatibility",
73+
"-no-stdlib",
74+
"-Xallow-kotlin-package", // We may have copies of stdlib APIs (see `core-1.2`)
75+
"-cp", classpath.asPath,
76+
"-d", destinationDirectory.get().asFile.absolutePath
77+
]
78+
args.addAll(source.collect { it.absolutePath })
79+
80+
logger.info("args: {}", args)
81+
82+
def compilerClassLoader = new URLClassLoader(compilerClasspath.collect { it.toURI().toURL() } as URL[])
83+
def exitCode = onClass("org.jetbrains.kotlin.cli.jvm.K2JVMCompiler", compilerClassLoader)
84+
.create()
85+
.call("exec", System.err, args as String[])
86+
.get()
87+
if (exitCode.toString() != "OK") {
88+
throw new GradleException("Compilation error. See log for more details")
89+
}
90+
}
91+
}

client/jackson/build.gradle

-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
apply plugin: 'java'
21
apply plugin: 'org.jetbrains.kotlin.jvm'
32
apply plugin: 'net.corda.plugins.api-scanner'
43
apply plugin: 'corda.common-publishing'

client/rpc/src/main/kotlin/net/corda/client/rpc/internal/RPCClientTelemetry.kt

+6-5
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,15 @@
11
package net.corda.client.rpc.internal
22

3-
import net.corda.core.internal.telemetry.OpenTelemetryComponent
43
import net.corda.core.internal.telemetry.SimpleLogTelemetryComponent
54
import net.corda.core.internal.telemetry.TelemetryServiceImpl
65
import net.corda.core.utilities.contextLogger
6+
import net.corda.nodeapi.internal.telemetry.OpenTelemetryComponent
77

8-
class RPCClientTelemetry(val serviceName: String, val openTelemetryEnabled: Boolean,
9-
val simpleLogTelemetryEnabled: Boolean, val spanStartEndEventsEnabled: Boolean,
8+
class RPCClientTelemetry(serviceName: String,
9+
val openTelemetryEnabled: Boolean,
10+
val simpleLogTelemetryEnabled: Boolean,
11+
val spanStartEndEventsEnabled: Boolean,
1012
val copyBaggageToTags: Boolean) {
11-
1213
companion object {
1314
private val log = contextLogger()
1415
}
@@ -39,4 +40,4 @@ class RPCClientTelemetry(val serviceName: String, val openTelemetryEnabled: Bool
3940
fun <T> getTelemetryHandle(telemetryClass: Class<T>): T? {
4041
return telemetryService.getTelemetryHandle(telemetryClass)
4142
}
42-
}
43+
}

confidential-identities/build.gradle

+1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
// This contains the SwapIdentitiesFlow which can be used for exchanging confidential identities as part of a flow.
22
// TODO: Merge this into core: the original plan was to develop it independently but in practice it's too widely used to break compatibility now, as finance uses it.
3+
apply plugin: 'org.jetbrains.kotlin.jvm'
34
apply plugin: 'net.corda.plugins.quasar-utils'
45
apply plugin: 'net.corda.plugins.cordapp'
56
apply plugin: 'corda.common-publishing'

constants.properties

+1
Original file line numberDiff line numberDiff line change
@@ -100,3 +100,4 @@ controlsfxVersion=8.40.15
100100
fontawesomefxCommonsVersion=11.0
101101
fontawesomefxFontawesomeVersion=4.7.0-11
102102
javaassistVersion=3.29.2-GA
103+
joorVersion=0.9.15

core-1.2/README.md

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
This is a Kotlin 1.2 version of the `core` module, which is consumed by the `verifier` module, for verifying contracts written in Kotlin
2+
1.2. This is just a "shell" module which uses the existing the code in `core` and compiles it with the 1.2 compiler.
3+
4+
To allow `core` to benefit from new APIs introduced since 1.2, those APIs much be copied into this module with the same `kotlin` package.

core-1.2/build.gradle

+34
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
apply plugin: "corda.kotlin-1.2"
2+
apply plugin: "corda.common-publishing"
3+
4+
description 'Corda core built with Kotlin 1.2'
5+
6+
sourceSets {
7+
main {
8+
java.srcDir("../core/src/main/java")
9+
kotlin.srcDir("../core/src/main/kotlin")
10+
}
11+
}
12+
13+
dependencies {
14+
// Use the same dependencies as core (minus Kotlin)
15+
implementation(project(path: ":core", configuration: "resolvableImplementation")) {
16+
exclude(group: "org.jetbrains.kotlin")
17+
}
18+
implementation "org.jetbrains.kotlin:kotlin-reflect:$kotlin_1_2_version"
19+
implementation "co.paralleluniverse:quasar-core:$quasar_version"
20+
}
21+
22+
jar {
23+
archiveBaseName = 'corda-core-1.2'
24+
}
25+
26+
// TODO Don't publish publicly as it's only needed by the `verifier` module which consumes this into a fat jar.
27+
publishing {
28+
publications {
29+
maven(MavenPublication) {
30+
artifactId 'corda-core-1.2'
31+
from components.java
32+
}
33+
}
34+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
// Implement the new post-1.2 APIs which are used by core and serialization
2+
@file:Suppress("unused", "MagicNumber", "INVISIBLE_MEMBER")
3+
4+
package kotlin.collections
5+
6+
inline fun <K, V> Iterable<K>.associateWith(valueSelector: (K) -> V): Map<K, V> {
7+
val result = LinkedHashMap<K, V>(mapCapacity(if (this is Collection<*>) size else 10).coerceAtLeast(16))
8+
return associateWithTo(result, valueSelector)
9+
}
10+
11+
inline fun <K, V, M : MutableMap<in K, in V>> Iterable<K>.associateWithTo(destination: M, valueSelector: (K) -> V): M {
12+
for (element in this) {
13+
destination.put(element, valueSelector(element))
14+
}
15+
return destination
16+
}
17+
18+
inline fun <T> Iterable<T>.sumOf(selector: (T) -> Int): Int {
19+
var sum = 0
20+
for (element in this) {
21+
sum += selector(element)
22+
}
23+
return sum
24+
}
25+
26+
inline fun <T, R : Comparable<R>> Iterable<T>.maxOf(selector: (T) -> R): R {
27+
val iterator = iterator()
28+
if (!iterator.hasNext()) throw NoSuchElementException()
29+
var maxValue = selector(iterator.next())
30+
while (iterator.hasNext()) {
31+
val v = selector(iterator.next())
32+
if (maxValue < v) {
33+
maxValue = v
34+
}
35+
}
36+
return maxValue
37+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
// Implement the new post-1.2 APIs which are used by core and serialization
2+
@file:Suppress("unused", "SpreadOperator", "NOTHING_TO_INLINE")
3+
4+
package kotlin.io.path
5+
6+
import java.io.InputStream
7+
import java.io.OutputStream
8+
import java.nio.file.Files
9+
import java.nio.file.LinkOption
10+
import java.nio.file.OpenOption
11+
import java.nio.file.Path
12+
import java.nio.file.attribute.FileAttribute
13+
14+
inline operator fun Path.div(other: String): Path = this.resolve(other)
15+
16+
fun Path.listDirectoryEntries(glob: String = "*"): List<Path> = Files.newDirectoryStream(this, glob).use { it.toList() }
17+
18+
inline fun Path.createDirectories(vararg attributes: FileAttribute<*>): Path = Files.createDirectories(this, *attributes)
19+
20+
inline fun Path.deleteIfExists(): Boolean = Files.deleteIfExists(this)
21+
22+
inline fun Path.exists(vararg options: LinkOption): Boolean = Files.exists(this, *options)
23+
24+
inline fun Path.inputStream(vararg options: OpenOption): InputStream = Files.newInputStream(this, *options)
25+
26+
inline fun Path.outputStream(vararg options: OpenOption): OutputStream = Files.newOutputStream(this, *options)
27+
28+
inline fun Path.isDirectory(vararg options: LinkOption): Boolean = Files.isDirectory(this, *options)
29+
30+
inline fun Path.isSymbolicLink(): Boolean = Files.isSymbolicLink(this)
31+
32+
inline fun Path.readSymbolicLink(): Path = Files.readSymbolicLink(this)
33+
34+
val Path.name: String
35+
get() = fileName?.toString().orEmpty()
36+
37+
inline fun Path.readBytes(): ByteArray = Files.readAllBytes(this)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
// Implement the new post-1.2 APIs which are used by core and serialization
2+
@file:Suppress("PLATFORM_CLASS_MAPPED_TO_KOTLIN", "NOTHING_TO_INLINE", "unused")
3+
4+
package kotlin.text
5+
6+
import java.util.Locale
7+
8+
inline fun String.lowercase(): String = (this as java.lang.String).toLowerCase(Locale.ROOT)
9+
10+
inline fun String.lowercase(locale: Locale): String = (this as java.lang.String).toLowerCase(locale)

0 commit comments

Comments
 (0)