你可以在构建依赖时指定Rust使用
gcc
编译器,只要你已经正确安装了针对
mingw
的Rust。为了确保你的Rust正确配置为
mingw
,请使用
这个线程。请记住,默认情况下,Windows版Rust将被配置为MSVC,而不是mingw。
以下步骤最初在官方
rust-sdl2文档中提到。
完成上述步骤后,您需要一个构建脚本来链接库到依赖项。但首先,您需要这些库。从官方
libsdl网站下载
mingw
特定的库。
现在,你需要将这些文件放在与
cargo.toml
相同的文件夹中,按正确的顺序排列。
SDL2-devel-2.0.x-mingw.tar.gz\SDL2-2.0.x\i686-w64-mingw32\bin -> gnu-mingw\dll\32
SDL2-devel-2.0.x-mingw.tar.gz\SDL2-2.0.x\x86_64-w64-mingw32\bin -> gnu-mingw\dll\64
SDL2-devel-2.0.x-mingw.tar.gz\SDL2-2.0.x\i686-w64-mingw32\lib -> gnu-mingw\lib\32
SDL2-devel-2.0.x-mingw.tar.gz\SDL2-2.0.x\x86_64-w64-mingw32\lib -> gnu-mingw\lib\64
gnu-mingw
应该是与 cargo.toml
文件在同一目录下的文件夹。
现在,您需要构建脚本本身,创建一个名为 build.rs
的文件,并将其放置在 cargo.toml
的 [package]
中。
build = "build.rs"
有关构建脚本的更多信息,请参见此处。
这是脚本内容-
use std::env;
use std::path::PathBuf;
fn main() {
let target = env::var("TARGET").unwrap();
if target.contains("pc-windows") {
let manifest_dir = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap());
let mut lib_dir = manifest_dir.clone();
let mut dll_dir = manifest_dir.clone();
lib_dir.push("gnu-mingw");
dll_dir.push("gnu-mingw");
lib_dir.push("lib");
dll_dir.push("dll");
if target.contains("x86_64") {
lib_dir.push("64");
dll_dir.push("64");
}
else {
lib_dir.push("32");
dll_dir.push("32");
}
println!("cargo:rustc-link-search=all={}", lib_dir.display());
for entry in std::fs::read_dir(dll_dir).expect("Can't read DLL dir") {
let entry_path = entry.expect("Invalid fs entry").path();
let file_name_result = entry_path.file_name();
let mut new_file_path = manifest_dir.clone();
if let Some(file_name) = file_name_result {
let file_name = file_name.to_str().unwrap();
if file_name.ends_with(".dll") {
new_file_path.push(file_name);
std::fs::copy(&entry_path, new_file_path.as_path()).expect("Can't copy from DLL dir");
}
}
}
}
}
注意: 这个意味着故意省略了 MSVC 特定的内容。
现在,在你的构建配置,也就是 cargo.toml
中的 [build]
部分,你需要加入以下内容-
target = "x86_64-pc-windows-gnu"
可以在 cargo build 文档 中找到可用目标的列表。
更多有关构建配置的信息可以在 配置文档 中找到。
作为额外的奖励,如果你想使用其他编译器(而不是 gcc
),你只需要确保必要的库与可执行文件在同一个目录中,并将其放入 [target.TARGET_NAME]
中即可。
linker = "path\\to\\c\\linker"
ar = "path\\to\\c\\ar"
将TARGET_NAME
替换为您选择的目标三元组。
编辑:根据OP的请求,提供如何将CMake与rust结合使用的信息。
使用CMake与rust是可能的,但是编译和构建第三方依赖项几乎肯定需要一个自定义的构建脚本,该脚本将能够替换依赖项自己的构建脚本。
为了说明这一点,让我们使用CMake和rust制作一个自定义的简单C静态库。
以下步骤最初在此flames of code博客中提到
首先,您需要一个C项目,现在不需要太多,只需要一个.c
文件,您应该将.c
文件放在名为libfoo
(或任何您的库可能被称为)的目录中。现在,您可以将此 libfoo
目录放在与您的 rust
项目相同的目录中或任何您喜欢的位置,但请记住路径。
继续在.c
文件中放入一个简单的“hello world”程序-
#include <stdio.h>
void testcall(float value)
{
printf("Hello, world from C! Value passed: %f\n",value);
}
(注意:该函数不应为主函数,因为我们正在构建一个静态库)
现在我们需要在相同的目录中添加一个CMakelists.txt
文件-
cmake_minimum_required(VERSION 3.0)
project(LibFoo C)
add_library(foo STATIC foo.c)
install(TARGETS foo DESTINATION .)
这是一个相当简单的脚本,虽然最后一行很重要 - 它确保库的目标是 .
- 我们稍后需要从Rust中找到此库。
所以现在,文件结构可能如下所示-
.
├── Cargo.lock
├── Cargo.toml
├── libfoo
│ ├── CMakeLists.txt
│ └── foo.c
└── src
└── main.rs
现在关于 Rust 的部分,你需要为项目编写一个构建脚本,并且需要使用构建依赖项
cmake
。
将构建脚本添加到
cargo.toml
中。
[package]
build="build.rs"
还有依赖关系 -
[build-dependencies]
cmake = "0.1.31"
现在在你的
build.rs
中,你需要调用
cmake
。
extern crate cmake;
use cmake::Config;
fn main()
{
let dst = Config::new("libfoo").build();
println!("cargo:rustc-link-search=native={}", dst.display());
println!("cargo:rustc-link-lib=static=foo");
}
.build()
部分很简单,但为什么要加那些 println!
呢?
这些代码将必要的命令写入 stdout
,以便 cargo
可以搜索库并进行链接。 这就是你的 c 库名称和目标起作用的地方。
现在,您只需执行 cargo run
,它将构建 C 库以及 Rust 项目!
您还可以在详细模式下运行它(-vv
),以查看 C 库生成的详细输出。
现在,您所要做的就是从 main.rs
中调用该库-
#[link(name="foo", kind="static")]
extern {
fn testcall(v: f32);
}
fn main() {
println!("Hello, world from Rust!");
unsafe {
testcall(3.14159);
};
}
很简单,但是博客的作者留了一条注释给外部函数-
注意,这个原型需要从C原型手动转换到Rust原型。对于操作基本值类型的简单函数而言,这件事很简单,但是当涉及更复杂的数据类型时可能会更加困难。
这使我们回到SDL2箱体,编译它所需的C库,链接它们,然后构建箱体本身肯定需要大量调整 - 但我希望这指引您走向正确的方向。
cc-rs
来构建库,自己编写一个构建脚本。 - Chase