Tests: Support two-stage compilation (src -> klib -> kexe)
This commit is contained in:
committed by
Ilya Matveev
parent
00d780b650
commit
e8d8df6928
@@ -94,6 +94,11 @@ To update the blackbox compiler tests set TeamCity build number in `gradle.prope
|
|||||||
|
|
||||||
./gradlew -Ptest_verbose :backend.native:tests:mpp_optional_expectation
|
./gradlew -Ptest_verbose :backend.native:tests:mpp_optional_expectation
|
||||||
|
|
||||||
|
* **-Ptest_two_stage** enables two-stage compilation of tests. If two-stage compilation is enabled, test sources are compiled into a klibrary
|
||||||
|
and then a final native binary is produced from this klibrary using the -Xsource-library compiler flag.
|
||||||
|
|
||||||
|
./gradlew -Ptest_two_stage backend.native:tests:array0
|
||||||
|
|
||||||
## Performance measurement
|
## Performance measurement
|
||||||
|
|
||||||
Firstly, it's necessary to build analyzer tool to have opportunity to compare different performance results:
|
Firstly, it's necessary to build analyzer tool to have opportunity to compare different performance results:
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ repositories {
|
|||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
compile "org.jetbrains.kotlin:kotlin-stdlib:$buildKotlinVersion"
|
compile "org.jetbrains.kotlin:kotlin-stdlib:$buildKotlinVersion"
|
||||||
compile "org.jetbrains.kotlin:kotlin-compiler:$buildKotlinVersion"
|
compile "org.jetbrains.kotlin:kotlin-compiler:$kotlinVersion"
|
||||||
compile project(':Interop:Indexer')
|
compile project(':Interop:Indexer')
|
||||||
compile "org.jetbrains.kotlin:kotlin-native-shared:$konanVersion"
|
compile "org.jetbrains.kotlin:kotlin-native-shared:$konanVersion"
|
||||||
compile project(path: ":endorsedLibraries:kliopt", configuration: "jvmRuntimeElements")
|
compile project(path: ":endorsedLibraries:kliopt", configuration: "jvmRuntimeElements")
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
* that can be found in the LICENSE file.
|
* that can be found in the LICENSE file.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import shadow.org.jetbrains.kotlin.gradle.plugin.tasks.KonanCompileNativeBinary
|
||||||
import org.jetbrains.kotlin.*
|
import org.jetbrains.kotlin.*
|
||||||
import org.jetbrains.kotlin.konan.target.KonanTarget
|
import org.jetbrains.kotlin.konan.target.KonanTarget
|
||||||
|
|
||||||
@@ -16,7 +17,7 @@ buildscript {
|
|||||||
maven {
|
maven {
|
||||||
url buildKotlinCompilerRepo
|
url buildKotlinCompilerRepo
|
||||||
}
|
}
|
||||||
maven {
|
maven {
|
||||||
url kotlinCompilerRepo
|
url kotlinCompilerRepo
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -87,6 +88,13 @@ project.convention.plugins.executor = ExecutorServiceKt.create(project)
|
|||||||
// Do not generate run tasks for konan built artifacts
|
// Do not generate run tasks for konan built artifacts
|
||||||
ext.konanNoRun = true
|
ext.konanNoRun = true
|
||||||
|
|
||||||
|
// Enable two-stage compilation if the test_two_stage property is set.
|
||||||
|
if (project.hasProperty("test_two_stage")) {
|
||||||
|
tasks.withType(KonanCompileNativeBinary.class) {
|
||||||
|
enableTwoStageCompilation = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
allprojects {
|
allprojects {
|
||||||
// Root directories for test output (logs, compiled files, statistics etc). Only single path must be in each set.
|
// Root directories for test output (logs, compiled files, statistics etc). Only single path must be in each set.
|
||||||
// backend.native/tests
|
// backend.native/tests
|
||||||
|
|||||||
@@ -26,3 +26,6 @@ testKotlinCompilerVersion=1.3.60-dev-1210
|
|||||||
konanVersion=1.3.60
|
konanVersion=1.3.60
|
||||||
org.gradle.jvmargs='-Dfile.encoding=UTF-8'
|
org.gradle.jvmargs='-Dfile.encoding=UTF-8'
|
||||||
org.gradle.workers.max=4
|
org.gradle.workers.max=4
|
||||||
|
|
||||||
|
# Uncomment to enable composite build
|
||||||
|
#kotlinProjectPath=<insert the path to Kotlin project root here>
|
||||||
+1
-9
@@ -47,16 +47,8 @@ abstract class KonanBuildingTask: KonanArtifactWithLibrariesTask(), KonanBuildin
|
|||||||
val konanVersion
|
val konanVersion
|
||||||
@Input get() = project.konanVersion.toString(true, true)
|
@Input get() = project.konanVersion.toString(true, true)
|
||||||
|
|
||||||
protected abstract fun buildArgs(): List<String>
|
|
||||||
|
|
||||||
@TaskAction
|
@TaskAction
|
||||||
open fun run() {
|
abstract fun run()
|
||||||
destinationDir.mkdirs()
|
|
||||||
if (dumpParameters) {
|
|
||||||
dumpProperties(this)
|
|
||||||
}
|
|
||||||
toolRunner.run(buildArgs())
|
|
||||||
}
|
|
||||||
|
|
||||||
// DSL.
|
// DSL.
|
||||||
|
|
||||||
|
|||||||
+135
-15
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2010-2017 JetBrains s.r.o.
|
* Copyright 2010-2019 JetBrains s.r.o.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -25,8 +25,6 @@ import org.gradle.process.CommandLineArgumentProvider
|
|||||||
import org.jetbrains.kotlin.gradle.plugin.konan.*
|
import org.jetbrains.kotlin.gradle.plugin.konan.*
|
||||||
import org.jetbrains.kotlin.gradle.plugin.model.KonanModelArtifact
|
import org.jetbrains.kotlin.gradle.plugin.model.KonanModelArtifact
|
||||||
import org.jetbrains.kotlin.gradle.plugin.model.KonanModelArtifactImpl
|
import org.jetbrains.kotlin.gradle.plugin.model.KonanModelArtifactImpl
|
||||||
import org.jetbrains.kotlin.konan.CURRENT
|
|
||||||
import org.jetbrains.kotlin.konan.KonanVersion
|
|
||||||
import org.jetbrains.kotlin.konan.library.defaultResolver
|
import org.jetbrains.kotlin.konan.library.defaultResolver
|
||||||
import org.jetbrains.kotlin.konan.target.CompilerOutputKind
|
import org.jetbrains.kotlin.konan.target.CompilerOutputKind
|
||||||
import org.jetbrains.kotlin.konan.target.Distribution
|
import org.jetbrains.kotlin.konan.target.Distribution
|
||||||
@@ -97,6 +95,14 @@ abstract class KonanCompileTask: KonanBuildingTask(), KonanCompileSpec {
|
|||||||
val apiVersion : String?
|
val apiVersion : String?
|
||||||
@Optional @Input get() = project.konanExtension.apiVersion
|
@Optional @Input get() = project.konanExtension.apiVersion
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Is the two-stage compilation enabled.
|
||||||
|
*
|
||||||
|
* In regular (one-stage) compilation, sources are directly compiled into a final native binary.
|
||||||
|
* In two-stage compilation, sources are compiled into a klib first and then a final native binary is produced from this klib.
|
||||||
|
*/
|
||||||
|
abstract val enableTwoStageCompilation: Boolean
|
||||||
|
|
||||||
protected fun directoryToKt(dir: Any) = project.fileTree(dir).apply {
|
protected fun directoryToKt(dir: Any) = project.fileTree(dir).apply {
|
||||||
include("**/*.kt")
|
include("**/*.kt")
|
||||||
exclude { it.file.startsWith(project.buildDir) }
|
exclude { it.file.startsWith(project.buildDir) }
|
||||||
@@ -104,9 +110,85 @@ abstract class KonanCompileTask: KonanBuildingTask(), KonanCompileSpec {
|
|||||||
|
|
||||||
// Command line ------------------------------------------------------------
|
// Command line ------------------------------------------------------------
|
||||||
|
|
||||||
override fun buildArgs() = mutableListOf<String>().apply {
|
// Exclude elements matching the predicate.
|
||||||
addArg("-output", artifact.canonicalPath)
|
private fun List<String>.excludeFlags(predicate: (String) -> Boolean) = filterNot(predicate)
|
||||||
|
|
||||||
|
// Exclude the listed elements.
|
||||||
|
private fun List<String>.excludeFlags(vararg keys: String) = keys.toSet().let { keysToExclude ->
|
||||||
|
excludeFlags { it in keysToExclude }
|
||||||
|
}
|
||||||
|
|
||||||
|
// Exclude the arguments passed by the given keys.
|
||||||
|
// E.g. if the list contains the following elements: ["-l", "foo", "-r", "bar"],
|
||||||
|
// call exclude("-r") returns the following list: ["-l", "foo"].
|
||||||
|
private fun List<String>.excludeArguments(vararg args: String): List<String> {
|
||||||
|
val argsToExclude = args.toSet()
|
||||||
|
val xPrefixesToExclude = argsToExclude.filter { it.startsWith("-X") }.map { "$it=" }
|
||||||
|
val result = mutableListOf<String>()
|
||||||
|
|
||||||
|
var i = 0
|
||||||
|
while (i < size) {
|
||||||
|
val key = this[i]
|
||||||
|
when {
|
||||||
|
key in argsToExclude -> {
|
||||||
|
// Skip the key and the following arg.
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
// Support args passed as -X<arg>=<value>.
|
||||||
|
xPrefixesToExclude.any { key.startsWith(it) } -> { /* Skip the key. */ }
|
||||||
|
else -> result += key
|
||||||
|
}
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
// Don't include coverage flags into the first stage because they are not supported when compiling a klib.
|
||||||
|
private fun firstStageExtraOpts() = extraOpts
|
||||||
|
.excludeFlags("-Xcoverage")
|
||||||
|
.excludeArguments("-Xcoverage-file", "-Xlibrary-to-cover")
|
||||||
|
|
||||||
|
// Don't include the -Xemit-lazy-objc-header flag into
|
||||||
|
// the second stage because this stage have no sources.
|
||||||
|
private fun secondStageExtraOpts() = extraOpts
|
||||||
|
.excludeArguments("-Xemit-lazy-objc-header")
|
||||||
|
|
||||||
|
/** Args passed to the compiler at the first stage of two-stage compilation (klib building). */
|
||||||
|
protected fun buildFirstStageArgs(klibPath: String) = mutableListOf<String>().apply {
|
||||||
|
addArg("-output", klibPath)
|
||||||
|
addArg("-produce", CompilerOutputKind.LIBRARY.name.toLowerCase())
|
||||||
|
|
||||||
|
addAll(buildCommonArgs())
|
||||||
|
|
||||||
|
addAll(firstStageExtraOpts())
|
||||||
|
|
||||||
|
allSourceFiles.mapTo(this) { it.absolutePath }
|
||||||
|
commonSrcFiles
|
||||||
|
.flatMap { it.files }
|
||||||
|
.mapTo(this) { "-Xcommon-sources=${it.absolutePath}" }
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Args passed to the compiler at the second stage of two-stage compilation (producing a final binary from the klib). */
|
||||||
|
protected fun buildSecondStageArgs(klibPath: String) = mutableListOf<String>().apply {
|
||||||
|
addArg("-output", artifact.canonicalPath)
|
||||||
|
addArg("-produce", produce.name.toLowerCase())
|
||||||
|
addArgIfNotNull("-entry", entryPoint)
|
||||||
|
|
||||||
|
addAll(buildCommonArgs())
|
||||||
|
|
||||||
|
addFileArgs("-native-library", nativeLibraries)
|
||||||
|
linkerOpts.forEach {
|
||||||
|
addArg("-linker-option", it)
|
||||||
|
}
|
||||||
|
|
||||||
|
addAll(secondStageExtraOpts())
|
||||||
|
|
||||||
|
addArg("-l", klibPath)
|
||||||
|
add("-Xsource-library=${klibPath}")
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Args passed to the compiler at both stages of the two-stage compilation and during the singe-stage compilation. */
|
||||||
|
protected fun buildCommonArgs() = mutableListOf<String>().apply {
|
||||||
addArgs("-repo", libraries.repos.map { it.canonicalPath })
|
addArgs("-repo", libraries.repos.map { it.canonicalPath })
|
||||||
|
|
||||||
if (platformConfiguration.files.isNotEmpty()) {
|
if (platformConfiguration.files.isNotEmpty()) {
|
||||||
@@ -120,12 +202,6 @@ abstract class KonanCompileTask: KonanBuildingTask(), KonanCompileSpec {
|
|||||||
// The library's directory is added in libraries.repos.
|
// The library's directory is added in libraries.repos.
|
||||||
addArgs("-library", libraries.artifacts.map { it.artifact.nameWithoutExtension })
|
addArgs("-library", libraries.artifacts.map { it.artifact.nameWithoutExtension })
|
||||||
|
|
||||||
addFileArgs("-native-library", nativeLibraries)
|
|
||||||
addArg("-produce", produce.name.toLowerCase())
|
|
||||||
linkerOpts.forEach {
|
|
||||||
addArg("-linker-option", it)
|
|
||||||
}
|
|
||||||
|
|
||||||
addArgIfNotNull("-target", konanTarget.visibleName)
|
addArgIfNotNull("-target", konanTarget.visibleName)
|
||||||
addArgIfNotNull("-language-version", languageVersion)
|
addArgIfNotNull("-language-version", languageVersion)
|
||||||
addArgIfNotNull("-api-version", apiVersion)
|
addArgIfNotNull("-api-version", apiVersion)
|
||||||
@@ -143,6 +219,21 @@ abstract class KonanCompileTask: KonanBuildingTask(), KonanCompileSpec {
|
|||||||
|
|
||||||
if (libraries.friends.isNotEmpty())
|
if (libraries.friends.isNotEmpty())
|
||||||
addArg("-friend-modules", libraries.friends.joinToString(File.pathSeparator))
|
addArg("-friend-modules", libraries.friends.joinToString(File.pathSeparator))
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Args passed to the compiler if the two-stage compilation is disabled. */
|
||||||
|
fun buildSingleStageArgs() = mutableListOf<String>().apply {
|
||||||
|
addArg("-output", artifact.canonicalPath)
|
||||||
|
addArg("-produce", produce.name.toLowerCase())
|
||||||
|
addArgIfNotNull("-entry", entryPoint)
|
||||||
|
|
||||||
|
addAll(buildCommonArgs())
|
||||||
|
|
||||||
|
addFileArgs("-native-library", nativeLibraries)
|
||||||
|
|
||||||
|
linkerOpts.forEach {
|
||||||
|
addArg("-linker-option", it)
|
||||||
|
}
|
||||||
|
|
||||||
addAll(extraOpts)
|
addAll(extraOpts)
|
||||||
|
|
||||||
@@ -253,9 +344,37 @@ abstract class KonanCompileTask: KonanBuildingTask(), KonanCompileSpec {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
// endregion
|
// endregion
|
||||||
|
|
||||||
|
override fun run() {
|
||||||
|
destinationDir.mkdirs()
|
||||||
|
if (dumpParameters) {
|
||||||
|
dumpProperties(this)
|
||||||
|
}
|
||||||
|
if (enableTwoStageCompilation) {
|
||||||
|
logger.info("Start two-stage compilation")
|
||||||
|
val intermediateDir = project.konanBuildRoot
|
||||||
|
.resolve("intermediate")
|
||||||
|
.targetSubdir(konanTarget)
|
||||||
|
.apply { mkdirs() }
|
||||||
|
val klibPrefix = CompilerOutputKind.LIBRARY.prefix(konanTarget)
|
||||||
|
val klibSuffix = CompilerOutputKind.LIBRARY.suffix(konanTarget)
|
||||||
|
val intermediateKlib = intermediateDir.resolve("$klibPrefix$artifactName$klibSuffix").absolutePath
|
||||||
|
logger.info("Start first stage")
|
||||||
|
toolRunner.run(buildFirstStageArgs(intermediateKlib))
|
||||||
|
logger.info("Start second stage")
|
||||||
|
toolRunner.run(buildSecondStageArgs(intermediateKlib))
|
||||||
|
} else {
|
||||||
|
toolRunner.run(buildSingleStageArgs())
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
open class KonanCompileProgramTask: KonanCompileTask() {
|
abstract class KonanCompileNativeBinary: KonanCompileTask() {
|
||||||
|
@Input
|
||||||
|
override var enableTwoStageCompilation: Boolean = false
|
||||||
|
}
|
||||||
|
|
||||||
|
open class KonanCompileProgramTask: KonanCompileNativeBinary() {
|
||||||
override val produce: CompilerOutputKind get() = CompilerOutputKind.PROGRAM
|
override val produce: CompilerOutputKind get() = CompilerOutputKind.PROGRAM
|
||||||
|
|
||||||
var runTask: Exec? = null
|
var runTask: Exec? = null
|
||||||
@@ -287,14 +406,14 @@ open class KonanCompileProgramTask: KonanCompileTask() {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
open class KonanCompileDynamicTask: KonanCompileTask() {
|
open class KonanCompileDynamicTask: KonanCompileNativeBinary() {
|
||||||
override val produce: CompilerOutputKind get() = CompilerOutputKind.DYNAMIC
|
override val produce: CompilerOutputKind get() = CompilerOutputKind.DYNAMIC
|
||||||
|
|
||||||
val headerFile: File
|
val headerFile: File
|
||||||
@OutputFile get() = destinationDir.resolve("$artifactPrefix${artifactName}_api.h")
|
@OutputFile get() = destinationDir.resolve("$artifactPrefix${artifactName}_api.h")
|
||||||
}
|
}
|
||||||
|
|
||||||
open class KonanCompileFrameworkTask: KonanCompileTask() {
|
open class KonanCompileFrameworkTask: KonanCompileNativeBinary() {
|
||||||
override val produce: CompilerOutputKind get() = CompilerOutputKind.FRAMEWORK
|
override val produce: CompilerOutputKind get() = CompilerOutputKind.FRAMEWORK
|
||||||
|
|
||||||
override val artifact
|
override val artifact
|
||||||
@@ -303,8 +422,9 @@ open class KonanCompileFrameworkTask: KonanCompileTask() {
|
|||||||
|
|
||||||
open class KonanCompileLibraryTask: KonanCompileTask() {
|
open class KonanCompileLibraryTask: KonanCompileTask() {
|
||||||
override val produce: CompilerOutputKind get() = CompilerOutputKind.LIBRARY
|
override val produce: CompilerOutputKind get() = CompilerOutputKind.LIBRARY
|
||||||
|
override val enableTwoStageCompilation: Boolean = false
|
||||||
}
|
}
|
||||||
|
|
||||||
open class KonanCompileBitcodeTask: KonanCompileTask() {
|
open class KonanCompileBitcodeTask: KonanCompileNativeBinary() {
|
||||||
override val produce: CompilerOutputKind get() = CompilerOutputKind.BITCODE
|
override val produce: CompilerOutputKind get() = CompilerOutputKind.BITCODE
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -75,7 +75,7 @@ open class KonanInteropTask @Inject constructor(val workerExecutor: WorkerExecut
|
|||||||
@InputFiles val headers = mutableSetOf<FileCollection>()
|
@InputFiles val headers = mutableSetOf<FileCollection>()
|
||||||
@InputFiles val linkFiles = mutableSetOf<FileCollection>()
|
@InputFiles val linkFiles = mutableSetOf<FileCollection>()
|
||||||
|
|
||||||
override fun buildArgs() = mutableListOf<String>().apply {
|
fun buildArgs() = mutableListOf<String>().apply {
|
||||||
addArg("-o", artifact.canonicalPath)
|
addArg("-o", artifact.canonicalPath)
|
||||||
|
|
||||||
addArgIfNotNull("-target", konanTarget.visibleName)
|
addArgIfNotNull("-target", konanTarget.visibleName)
|
||||||
|
|||||||
Reference in New Issue
Block a user