[Build] Refactor compiling native code to bitcode

Also switch to kts in the corresponding buildscripts.
This commit is contained in:
Ilya Matveev
2020-10-06 13:03:42 +07:00
committed by Ilya Matveev
parent 9663e724aa
commit cb146e06ab
10 changed files with 275 additions and 177 deletions
+4
View File
@@ -82,6 +82,10 @@ gradlePlugin {
id = "swift-benchmarking"
implementationClass = "org.jetbrains.kotlin.benchmark.SwiftBenchmarkingPlugin"
}
create("compileToBitcode") {
id = "compile-to-bitcode"
implementationClass = "org.jetbrains.kotlin.bitcode.CompileToBitcodePlugin"
}
}
}
@@ -16,7 +16,7 @@ import org.gradle.api.tasks.Input
import org.gradle.api.tasks.InputFiles
import org.gradle.api.tasks.OutputFile
import org.gradle.api.tasks.TaskAction
import org.jetbrains.kotlin.konan.target.HostManager
import org.jetbrains.kotlin.bitcode.CompileToBitcode
import java.io.FileReader
import java.io.FileWriter
@@ -8,11 +8,14 @@ package org.jetbrains.kotlin
import org.gradle.api.NamedDomainObjectCollection
import org.gradle.api.NamedDomainObjectContainer
import org.gradle.api.Project
import org.gradle.api.file.FileCollection
import org.gradle.api.plugins.ExtraPropertiesExtension
import org.gradle.api.provider.Provider
import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension
import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation
import org.jetbrains.kotlin.gradle.plugin.KotlinTargetPreset
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeCompilation
import java.io.File
/*
* This file includes internal short-cuts visible only inside of the 'buildSrc' module.
@@ -38,3 +41,8 @@ internal val NamedDomainObjectCollection<KotlinTargetPreset<*>>.mingwX64: Kotlin
internal val NamedDomainObjectContainer<out KotlinCompilation<*>>.main: KotlinNativeCompilation
get() = getByName(::main.name) as KotlinNativeCompilation
internal val FileCollection.isNotEmpty: Boolean
get() = !isEmpty
internal fun Provider<File>.resolve(child: String): Provider<File> = map { it.resolve(child) }
@@ -1,47 +1,51 @@
/*
* Copyright 2010-2017 JetBrains s.r.o.
*
* 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.
* Copyright 2010-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin
package org.jetbrains.kotlin.bitcode
import org.gradle.api.Action
import org.gradle.api.DefaultTask
import org.gradle.api.tasks.InputDirectory
import org.gradle.api.tasks.OutputFile
import org.gradle.api.tasks.TaskAction
import org.gradle.api.file.FileCollection
import org.gradle.api.tasks.*
import org.jetbrains.kotlin.ExecClang
import org.jetbrains.kotlin.konan.target.Family
import org.jetbrains.kotlin.konan.target.HostManager
import org.jetbrains.kotlin.konan.target.KonanTarget
import java.io.File
import javax.inject.Inject
open class CompileToBitcode @Inject constructor(@InputDirectory val srcRoot: File,
val folderName: String,
val target: String) : DefaultTask() {
open class CompileToBitcode @Inject constructor(
val srcRoot: File,
val folderName: String,
val target: String
) : DefaultTask() {
enum class Language {
C, CPP
}
// Compiler args are part of compilerFlags so we don't register them as an input.
val compilerArgs = mutableListOf<String>()
@Input
val linkerArgs = mutableListOf<String>()
val excludeFiles = mutableListOf<String>()
var srcDir = File(srcRoot, "cpp")
var headersDir = File(srcRoot, "headers")
var excludeFiles: List<String> = listOf(
"**/*Test.cpp",
"**/*Test.mm",
)
var includeFiles: List<String> = listOf(
"**/*.cpp",
"**/*.mm"
)
// Source files and headers are registered as inputs by the `inputFiles` and `headers` properties.
var srcDirs: FileCollection = project.files(srcRoot.resolve("cpp"))
var headersDirs: FileCollection = project.files(srcRoot.resolve("headers"))
@Input
var skipLinkagePhase = false
var excludedTargets = mutableListOf<String>()
@Input
var language = Language.CPP
private val targetDir by lazy { File(project.buildDir, target) }
@@ -57,9 +61,10 @@ open class CompileToBitcode @Inject constructor(@InputDirectory val srcRoot: Fil
Language.CPP -> "clang++"
}
@get:Input
val compilerFlags: List<String>
get() {
val commonFlags = listOf("-c", "-emit-llvm", "-I$headersDir")
val commonFlags = listOf("-c", "-emit-llvm") + headersDirs.map { "-I$it" }
val languageFlags = when (language) {
Language.C ->
// Used flags provided by original build of allocator C code.
@@ -75,17 +80,30 @@ open class CompileToBitcode @Inject constructor(@InputDirectory val srcRoot: Fil
return commonFlags + languageFlags + compilerArgs
}
@get:SkipWhenEmpty
@get:InputFiles
val inputFiles: Iterable<File>
get() {
val srcFilesPatterns =
when (language) {
Language.C -> listOf("**/*.c")
Language.CPP -> listOf("**/*.cpp", "**/*.mm")
}
return project.fileTree(srcDir) {
it.include(srcFilesPatterns)
it.exclude(excludeFiles)
}.files
return srcDirs.flatMap { srcDir ->
project.fileTree(srcDir) {
it.include(includeFiles)
it.exclude(excludeFiles)
}.files
}
}
@get:InputFiles
protected val headers: Iterable<File>
get() {
return headersDirs.files.flatMap { dir ->
project.fileTree(dir) {
val includePatterns = when (language) {
Language.C -> arrayOf("**/.h")
Language.CPP -> arrayOf("**/*.h", "**/*.hpp")
}
it.include(*includePatterns)
}.files
}
}
@OutputFile
@@ -93,15 +111,14 @@ open class CompileToBitcode @Inject constructor(@InputDirectory val srcRoot: Fil
@TaskAction
fun compile() {
if (target in excludedTargets) return
objDir.mkdirs()
val plugin = project.convention.getPlugin(ExecClang::class.java)
plugin.execKonanClang(target, Action {
plugin.execKonanClang(target) {
it.workingDir = objDir
it.executable = executable
it.args = compilerFlags + inputFiles.map { it.absolutePath }
})
}
if (!skipLinkagePhase) {
project.exec {
@@ -114,4 +131,4 @@ open class CompileToBitcode @Inject constructor(@InputDirectory val srcRoot: Fil
}
}
}
}
}
@@ -0,0 +1,59 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.bitcode
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.api.plugins.BasePlugin
import org.jetbrains.kotlin.createCompilationDatabasesFromCompileToBitcodeTasks
import java.io.File
import javax.inject.Inject
/**
* A plugin creating extensions to compile
*/
open class CompileToBitcodePlugin: Plugin<Project> {
override fun apply(target: Project) = with(target) {
extensions.create(EXTENSION_NAME, CompileToBitcodeExtension::class.java, target)
afterEvaluate {
// TODO: Support providers (https://docs.gradle.org/current/userguide/lazy_configuration.html)
// in database tasks and create them along with corresponding compile tasks (not in afterEvaluate).
createCompilationDatabasesFromCompileToBitcodeTasks(project, COMPILATION_DATABASE_TASK_NAME)
}
}
companion object {
const val EXTENSION_NAME = "bitcode"
const val COMPILATION_DATABASE_TASK_NAME = "CompilationDatabase"
}
}
open class CompileToBitcodeExtension @Inject constructor(val project: Project) {
private val targetList = with(project) {
provider { rootProject.property("targetList") as List<String> } // TODO: Can we make it better?
}
fun create(name: String, srcDir: File = project.file("src/$name"), configurationBlock: CompileToBitcode.() -> Unit = {}) {
targetList.get().forEach { targetName ->
project.tasks.register(
"${targetName}${name.snakeCaseToCamelCase().capitalize()}",
CompileToBitcode::class.java,
srcDir, name, targetName
).configure {
it.group = BasePlugin.BUILD_GROUP
it.description = "Compiles '$name' to bitcode for $targetName"
it.configurationBlock()
}
}
}
companion object {
private fun String.snakeCaseToCamelCase() =
split('_').joinToString(separator = "") { it.capitalize() }
}
}
-29
View File
@@ -1,29 +0,0 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the LICENSE file.
*/
import org.jetbrains.kotlin.CompileToBitcode
import org.jetbrains.kotlin.CompilationDatabaseKt
// TODO: consider using some Gradle plugins to build and test
targetList.each { targetName ->
tasks.create("${targetName}Hash", CompileToBitcode, file("src/hash"), 'hash', targetName)
}
targetList.each { targetName ->
tasks.create("${targetName}Files", CompileToBitcode, file("src/files"), 'files', targetName)
}
CompilationDatabaseKt.createCompilationDatabasesFromCompileToBitcodeTasks(project, "CompilationDatabase")
task build {
dependsOn "${hostName}Hash"
dependsOn "${hostName}Files"
}
task clean {
doLast {
delete buildDir
}
}
+26
View File
@@ -0,0 +1,26 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the LICENSE file.
*/
plugins {
id("compile-to-bitcode")
}
bitcode {
create("hash")
create("files")
}
val hostName: String by project
val build by tasks.registering {
dependsOn("${hostName}Hash")
dependsOn("${hostName}Files")
}
val clean by tasks.registering {
doLast {
delete(buildDir)
}
}
-106
View File
@@ -1,106 +0,0 @@
/*
* Copyright 2010-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the LICENSE file.
*/
import org.jetbrains.kotlin.CompileToBitcode
import org.jetbrains.kotlin.CompilationDatabaseKt
import org.jetbrains.kotlin.UtilsKt
// TODO: consider using some Gradle plugins to build and test
void includeRuntime(CompileToBitcode task) {
task.compilerArgs.add('-I' + project.file('../common/src/hash/headers'))
task.compilerArgs.add('-I' + project.file('src/main/cpp'))
}
targetList.each { targetName ->
tasks.create("${targetName}Runtime", CompileToBitcode, file('src/main'), "runtime", targetName).configure {
dependsOn ":common:${targetName}Hash"
dependsOn "${targetName}StdAlloc"
dependsOn "${targetName}OptAlloc"
dependsOn "${targetName}Mimalloc"
dependsOn "${targetName}Launcher"
dependsOn "${targetName}Debug"
dependsOn "${targetName}Release"
dependsOn "${targetName}Strict"
dependsOn "${targetName}Relaxed"
dependsOn "${targetName}ProfileRuntime"
dependsOn "${targetName}ObjC"
dependsOn "${targetName}ExceptionsSupport"
includeRuntime(delegate)
linkerArgs.add(project.file("../common/build/$targetName/hash.bc").path)
}
tasks.create("${targetName}Mimalloc", CompileToBitcode, file('src/mimalloc'), "mimalloc", targetName).configure {
language = CompileToBitcode.Language.C
excludeFiles.addAll(["**/alloc-override*.c", "**/page-queue.c", "**/static.c"])
if (!UtilsKt.targetSupportsMimallocAllocator(targetName))
excludedTargets.add(targetName)
srcDir = new File(srcRoot, "c")
compilerArgs.add("-DKONAN_MI_MALLOC=1")
headersDir = new File(srcDir, "include")
}
tasks.create("${targetName}Launcher", CompileToBitcode, file('src/launcher'), "launcher", targetName).configure {
includeRuntime(delegate)
}
tasks.create("${targetName}Debug", CompileToBitcode, file('src/debug'), "debug", targetName).configure {
includeRuntime(delegate)
}
tasks.create("${targetName}StdAlloc", CompileToBitcode, file('src/std_alloc'), "std_alloc", targetName)
tasks.create("${targetName}OptAlloc", CompileToBitcode, file('src/opt_alloc'), "opt_alloc", targetName)
tasks.create("${targetName}ExceptionsSupport", CompileToBitcode, file('src/exceptions_support'),
"exceptionsSupport", targetName).configure {
includeRuntime(delegate)
}
tasks.create("${targetName}Release", CompileToBitcode, file('src/release'), "release", targetName).configure {
includeRuntime(delegate)
}
tasks.create("${targetName}Strict", CompileToBitcode, file('src/strict'), "strict", targetName).configure {
includeRuntime(delegate)
}
tasks.create("${targetName}Relaxed", CompileToBitcode, file('src/relaxed'), "relaxed", targetName).configure {
includeRuntime(delegate)
}
tasks.create("${targetName}ProfileRuntime", CompileToBitcode, file('src/profile_runtime'),
"profileRuntime", targetName)
tasks.create("${targetName}ObjC", CompileToBitcode, file('src/objc'), "objc", targetName).configure {
includeRuntime(delegate)
}
}
CompilationDatabaseKt.createCompilationDatabasesFromCompileToBitcodeTasks(project, "CompilationDatabase")
task hostRuntime(dependsOn: "${hostName}Runtime")
task clean {
doLast {
delete buildDir
}
}
task generateJsMath {
dependsOn ':distCompiler'
doLast {
def jsinteropScript = isWindows() ? "jsinterop.bat" : "jsinterop"
def jsinterop = "$distDir/bin/$jsinteropScript"
def targetDir = "$buildDir/generated"
"$jsinterop -pkg kotlinx.interop.wasm.math -o $targetDir/math -target wasm32".execute().waitFor()
def generated = file("$targetDir/math-build/natives/js_stubs.js")
def mathJs = file('src/main/js/math.js')
mathJs.write("// NOTE: THIS FILE IS AUTO-GENERATED!\n" +
"// Run ':runtime:generateJsMath' to re-generate it.\n\n")
generated.withReader {
mathJs.append(it)
}
}
}
+119
View File
@@ -0,0 +1,119 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the LICENSE file.
*/
import org.jetbrains.kotlin.*
import org.jetbrains.kotlin.bitcode.CompileToBitcode
plugins {
id("compile-to-bitcode")
}
fun CompileToBitcode.includeRuntime() {
headersDirs += files("../common/src/hash/headers", "src/main/cpp")
}
val hostName: String by project
val targetList: List<String> by project
bitcode {
create("runtime", file("src/main")) {
dependsOn(
":common:${target}Hash",
"${target}StdAlloc",
"${target}OptAlloc",
"${target}Mimalloc",
"${target}Launcher",
"${target}Debug",
"${target}Release",
"${target}Strict",
"${target}Relaxed",
"${target}ProfileRuntime",
"${target}Objc",
"${target}ExceptionsSupport"
)
includeRuntime()
linkerArgs.add(project.file("../common/build/$target/hash.bc").path)
}
create("mimalloc") {
language = CompileToBitcode.Language.C
includeFiles = listOf("**/*.c")
excludeFiles += listOf("**/alloc-override*.c", "**/page-queue.c", "**/static.c")
srcDirs = files("$srcRoot/c")
compilerArgs.add("-DKONAN_MI_MALLOC=1")
headersDirs = files("$srcRoot/c/include")
onlyIf { targetSupportsMimallocAllocator(target) }
}
create("launcher") {
includeRuntime()
}
create("debug") {
includeRuntime()
}
create("std_alloc")
create("opt_alloc")
create("exceptionsSupport", file("src/exceptions_support")) {
includeRuntime()
}
create("release") {
includeRuntime()
}
create("strict") {
includeRuntime()
}
create("relaxed") {
includeRuntime()
}
create("profileRuntime", file("src/profile_runtime"))
create("objc") {
includeRuntime()
}
}
val hostRuntime by tasks.registering {
dependsOn("${hostName}Runtime")
}
val clean by tasks.registering {
doLast {
delete(buildDir)
}
}
val generateJsMath by tasks.registering {
dependsOn(":distCompiler")
doLast {
val distDir: File by project
val jsinteropScript = if (PlatformInfo.isWindows()) "jsinterop.bat" else "jsinterop"
val jsinterop = "$distDir/bin/$jsinteropScript"
val targetDir = "$buildDir/generated"
project.exec {
commandLine(
jsinterop,
"-pkg", "kotlinx.interop.wasm.math",
"-o", "$targetDir/math",
"-target", "wasm32"
)
}
val generated = file("$targetDir/math-build/natives/js_stubs.js")
val mathJs = file("src/main/js/math.js")
mathJs.writeText(
"// NOTE: THIS FILE IS AUTO-GENERATED!\n" +
"// Run ':runtime:generateJsMath' to re-generate it.\n\n"
)
mathJs.appendText(generated.readText())
}
}
+1 -1
View File
@@ -31,7 +31,7 @@ include ':backend.native:tests'
include ':backend.native:debugger-tests'
include ':utilities:basic-utils'
include ':utilities:cli-runner'
include 'dependencyPacker'
include ':dependencyPacker'
include ':performance'
include ':performance:ring'