В проекте Rust можно иметь сценарии сборки для компиляции и связывания стороннего кода не-Rust. В сочетании с cc
crate для упрощения вызова компилятора C / C ++ это довольно забавно.
Макет проекта:
├── build.rs
├── Cargo.toml
└── src
├── fib.nim
└── main.rs
Сам build.rs
:
use std::io::{self, Write};
use std::process::Command;
fn main() {
let output = Command::new("nim")
.arg("c")
.arg("--noMain")
.arg("--noLinking")
.arg("--nimcache:nimcache")
.arg("src/fib.nim")
.output()
.expect("Failed to invoke nim compiler");
if !output.status.success() {
let msg = String::from_utf8_lossy(output.stderr.as_slice());
let _ = writeln!(io::stderr(), "\nerror occurred: {}\n", msg);
std::process::exit(1);
}
cc::Build::new()
.include("/usr/lib/nim")
.warnings(false)
.file("nimcache/fib.nim.c")
.file("nimcache/stdlib_system.nim.c")
.compile("fib_nim");
}
Обратите внимание, что здесь есть несколько зависящих от платформы битов, в основном расположение заголовков Nim. И компилятору Nim также сказано поместить промежуточные файлы в каталог с именем nimcache
внутри проекта root вместо каталога по умолчанию в домашнем каталоге пользователя.
Файл Cargo.toml
:
[package]
name = "nim-ffi"
version = "0.1.0"
authors = ["rustacean"]
edition = "2018"
[dependencies]
libc = "0.2"
[build-dependencies]
cc = "1.0"
И, наконец, основной исходный файл Rust:
use libc::c_int;
extern "C" {
fn NimMain();
fn fib(_: c_int) -> c_int;
}
fn main() {
// initialize nim gc memory, types and stack
unsafe {
NimMain();
}
let res = unsafe { fib(20) };
println!("Nim fib(20) is: {}", res);
}
Он успешно собирается и работает:
$ cargo run
Nim fib(20) is: 6765