1
0
mirror of https://github.com/mamoe/mirai.git synced 2025-03-26 23:50:16 +08:00

Configure Rust interop

This commit is contained in:
Him188 2022-05-26 02:45:32 +01:00
parent be8100fc9a
commit ee71263f41
No known key found for this signature in database
GPG Key ID: BA439CDDCF652375
8 changed files with 201 additions and 29 deletions
buildSrc/src/main/kotlin
install.sh
mirai-core/src/nativeMainInterop

View File

@ -16,6 +16,9 @@ import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation.Companion.MAIN_COMPI
import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation.Companion.TEST_COMPILATION_NAME
import org.jetbrains.kotlin.gradle.plugin.KotlinPlatformType
import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeCompilation
import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget
import java.io.File
/*
* Copyright 2019-2022 Mamoe Technologies and contributors.
@ -60,16 +63,18 @@ fun Project.configureHMPPJvm() {
val nativeMainSets = mutableListOf<KotlinSourceSet>()
val nativeTestSets = mutableListOf<KotlinSourceSet>()
val nativeTargets = mutableListOf<KotlinNativeTarget>()
if (ideaActive) {
when {
val target = when {
Os.isFamily(Os.FAMILY_MAC) -> if (Os.isArch("aarch64")) macosArm64("native") else macosX64("native")
Os.isFamily(Os.FAMILY_WINDOWS) -> mingwX64("native")
else -> linuxX64("native")
}
nativeTargets.add(target)
} else {
// 1.6.0
val nativeTargets: List<String> = arrayOf(
val nativeTargetNames: List<String> = arrayOf(
// serialization doesn't support those commented targets
// "androidNativeArm32, androidNativeArm64, androidNativeX86, androidNativeX64",
"iosArm32, iosArm64, iosX64, iosSimulatorArm64",
@ -80,11 +85,12 @@ fun Project.configureHMPPJvm() {
"mingwX64",
// "wasm32" // linuxArm32Hfp, mingwX86
).flatMap { it.split(",") }.map { it.trim() }
presets.filter { it.name in nativeTargets }
presets.filter { it.name in nativeTargetNames }
.forEach { preset ->
val target = targetFromPreset(preset, preset.name)
val target = targetFromPreset(preset, preset.name) as KotlinNativeTarget
nativeMainSets.add(target.compilations[MAIN_COMPILATION_NAME].kotlinSourceSets.first())
nativeTestSets.add(target.compilations[TEST_COMPILATION_NAME].kotlinSourceSets.first())
nativeTargets.add(target)
}
if (!ideaActive) {
@ -98,31 +104,8 @@ fun Project.configureHMPPJvm() {
}
}
// nativeTarget.apply {
// val myrust by compilations.getByName("main").cinterops.creating {
// headers(project.projectDir.resolve("untitled/myrust.h"))
// }
//
// binaries {
// sharedLib {
// linkerOpts("-v")
// linkerOpts("-L${project.projectDir.resolve("untitled/target/debug/").absolutePath}")
//// linkerOpts("-lmyrust")
// linkerOpts("-Wl,-undefined,dynamic_lookup") // resolve symbols in runtime
// baseName = "mykotlin"
// }
//
// executable {
//
// linkerOpts("-v")
// linkerOpts("-L${project.projectDir.resolve("untitled/target/debug/").absolutePath}")
//// linkerOpts("-lmyrust")
// linkerOpts("-Wl,-undefined,dynamic_lookup") // resolve symbols in runtime
// baseName = "KotlinExecutable"
// entryPoint = "main.main"
// }
// }
// }
configureNativeInterop("main", projectDir.resolve("src/nativeMainInterop"), nativeTargets)
configureNativeInterop("test", projectDir.resolve("src/nativeTestInterop"), nativeTargets)
val sourceSets = kotlinSourceSets.orEmpty()
@ -151,4 +134,107 @@ fun Project.configureHMPPJvm() {
nativeMain.dependsOn(commonMain)
nativeTest.dependsOn(commonTest)
}
}
private fun Project.configureNativeInterop(
compilationName: String,
nativeInteropDir: File,
nativeTargets: MutableList<KotlinNativeTarget>
) {
if (nativeInteropDir.exists() && nativeInteropDir.isDirectory && nativeInteropDir.resolve("build.rs").exists()) {
val crateName = project.name.replace("-", "_")
val headerName = "$crateName.h"
val rustLibDir = nativeInteropDir.resolve("target/debug/")
configure(nativeTargets) {
compilations.getByName(compilationName).cinterops.create(compilationName) {
val headerFile = nativeInteropDir.resolve(headerName)
if (headerFile.exists()) headers(headerFile)
defFile(nativeInteropDir.resolve("interop.def"))
}
binaries {
sharedLib {
linkerOpts("-v")
linkerOpts("-L${rustLibDir.absolutePath.replace("\\", "/")}")
// linkerOpts("-lmyrust")
linkerOpts("-Wl,-undefined,dynamic_lookup") // resolve symbols in runtime
baseName = project.name
}
}
}
val cbindgen = tasks.register("cbindgen${compilationName.titlecase()}") {
group = "mirai"
inputs.files(nativeInteropDir.resolve("src"))
outputs.file(nativeInteropDir.resolve(headerName))
doLast {
exec {
workingDir(nativeInteropDir)
commandLine(
"cbindgen",
"--config", "cbindgen.toml",
"--crate", crateName,
"--output", headerName
)
}
}
}
val bindgen = tasks.register("bindgen${compilationName.titlecase()}") {
group = "mirai"
val bindingsPath = nativeInteropDir.resolve("src/bindings.rs")
inputs.files(nativeInteropDir.resolve("src"))
outputs.file(bindingsPath)
doLast {
exec {
workingDir(nativeInteropDir)
// bindgen input.h -o bindings.rs
commandLine(
"bindgen",
buildDir.resolve("bin/native/debugShared/lib${crateName}_api.h"),
"-o", bindingsPath,
)
}
}
}
var targetCompilation: KotlinNativeCompilation? = null
configure(nativeTargets) {
val compilations = compilations.filter { nativeInteropDir.name.contains(it.name, ignoreCase = true) }
check(compilations.isNotEmpty()) { "Should be at lease one corresponding native compilation, but found 0" }
targetCompilation = compilations.single()
targetCompilation!!.compileKotlinTask.dependsOn(cbindgen)
// tasks.getByName("cinteropNative$name").dependsOn(cbindgen)
}
targetCompilation!!
val compileRust = tasks.register("compileRust${compilationName.titlecase()}") {
group = "mirai"
inputs.files(nativeInteropDir.resolve("src"))
outputs.file(rustLibDir.resolve("lib$crateName.dynlib"))
dependsOn(targetCompilation!!.compileKotlinTask)
dependsOn(bindgen)
dependsOn(tasks.findByName("linkDebugSharedNative"))
doLast {
exec {
workingDir(nativeInteropDir)
commandLine(
"cargo",
"build",
"--all"
)
}
}
}
tasks.getByName("assemble").dependsOn(compileRust)
}
}
fun String.titlecase(): String {
if (this.isEmpty()) return this
val c = get(0)
return replaceFirst(c, Character.toTitleCase(c))
}

11
install.sh Normal file
View File

@ -0,0 +1,11 @@
#
# Copyright 2019-2022 Mamoe Technologies and contributors.
#
# 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
# Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
#
# https://github.com/mamoe/mirai/blob/dev/LICENSE
#
cargo install --force cbindgen
cargo install bindgen

View File

@ -0,0 +1,8 @@
/target
Cargo.lock
myrust.h
*.iml
src/bindings.rs
/*.h

View File

@ -0,0 +1,14 @@
[package]
name = "mirai_core"
version = "0.1.0"
[dependencies]
[lib]
name = "mirai_core"
crate-type = ["cdylib"] # Creates dynamic lib
# crate-type = ["staticlib"] # Creates static lib
[build-dependencies]
bindgen = "0.53.1"
cbindgen = "0.20.0"

View File

@ -0,0 +1,32 @@
/*
* Copyright 2019-2022 Mamoe Technologies and contributors.
*
* 使 GNU AFFERO GENERAL PUBLIC LICENSE version 3 , .
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
*
* https://github.com/mamoe/mirai/blob/dev/LICENSE
*/
extern crate bindgen;
extern crate cbindgen;
use std::env;
use std::path::PathBuf;
use cbindgen::Config;
use cbindgen::Language::C;
fn main() {
// let crate_dir = env::var("CARGO_MANIFEST_DIR").unwrap();
// cbindgen::Builder::new()
// .with_crate(crate_dir)
// .with_language(C)
// .generate()
// .expect("Unable to generate bindings")
// .write_to_file("nativeInterop.h");
println!("cargo:rustc-link-search=../../build/bin/native/debugShared");
println!("cargo:rustc-link-lib=mirai_core");
}

View File

@ -0,0 +1,10 @@
# This is a template cbindgen.toml file with all of the default values.
# Some values are commented out because their absence is the real default.
#
# See https://github.com/eqrion/cbindgen/blob/master/docs.md#cbindgentoml
# for detailed documentation of every option here.
language = "C"

View File

@ -0,0 +1,11 @@
/*
* Copyright 2019-2022 Mamoe Technologies and contributors.
*
* 使 GNU AFFERO GENERAL PUBLIC LICENSE version 3 , .
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
*
* https://github.com/mamoe/mirai/blob/dev/LICENSE
*/
/// cbindgen:ignore
mod bindings;