Rust cargo.toml 为 C 链接器和编译器指定自定义路径
Rust cargo.toml specify custom path for C linker and compiler
由于我对货物设置和大量文档的无知,我遇到了一些问题。
cargo.toml 文件是当前文件:
[package]
name = "hello"
version = "0.1.0"
authors = ["PC4\Author"]
[dependencies]
sdl2 = { version = "0.34.1", features = ["bundled", "static-link"] }
编译了 SDL2 依赖项,但它实际上正在使用 Visual Studio。我真正想做的是在编译板条箱依赖项时使用另一个文件夹中的自定义编译器。
您可以指定 rust 在构建依赖项时使用 gcc
编译器,只要您已经为 mingw
正确安装了 rust。为确保您的 Rust 已针对 mingw
正确配置 - 使用 。请记住,默认情况下,windows 的 Rust 将为 MSVC 配置,而不是 mingw。
下面的步骤原来是官方提到的rust-sdl2 docs
完成后,您需要一个构建脚本来 link 依赖库。但首先,您需要库。从官方下载 mingw
个特定的库 libsdl website
现在您需要将这些文件以正确的顺序放在与 cargo.toml
相同的文件夹中-
SDL2-devel-2.0.x-mingw.tar.gz\SDL2-2.0.x\i686-w64-mingw32\bin -> gnu-mingw\dll
SDL2-devel-2.0.x-mingw.tar.gz\SDL2-2.0.x\x86_64-w64-mingw32\bin -> gnu-mingw\dll
SDL2-devel-2.0.x-mingw.tar.gz\SDL2-2.0.x\i686-w64-mingw32\lib -> gnu-mingw\lib
SDL2-devel-2.0.x-mingw.tar.gz\SDL2-2.0.x\x86_64-w64-mingw32\lib -> gnu-mingw\lib
gnu-mingw
应该是和cargo.toml
同目录下的文件夹
现在您需要构建脚本本身,创建一个名为 build.rs
的文件并将其放入 cargo.toml
的 [package]
build = "build.rs"
可以找到有关构建脚本的更多信息here
这是脚本-
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 特定的内容。
现在在你的构建配置中 [build]
里面 cargo.toml
,你需要把 -
target = "x86_64-pc-windows-gnu"
中找到可用目标的列表
有关构建配置的更多信息,请参见 config docs
作为奖励,如果您想使用其他编译器(gcc
除外)。您所要做的就是确保必要的库位于同一目录中,并将它们放入您的 [target.TARGET_NAME]
linker = "path\to\c\linker"
ar = "path\to\c\ar"
用您选择的目标三元组替换 TARGET_NAME
。
编辑:根据 OP 的要求,提供有关如何将 CMake 与 Rust 结合的信息。
使用带有 Rust 的 CMake 是可能的,但是,编译和构建第三方依赖项几乎肯定需要自定义构建脚本,该脚本将能够替换依赖项自己的构建脚本。
为了说明,让我们使用带 Rust 的 CMake 制作一个自定义的简单 C 静态库。
以下步骤最初在this flames of code blog
中提到
首先你需要一个C工程,目前除了.c
文件外不需要太多,你应该把.c
文件放在一个名为libfoo
的目录中(或者不管你的图书馆叫什么)。现在,您可以将此 libfoo
目录放在与 rust
项目相同的目录中或任何您喜欢的目录中,但请记住路径。
继续把一个简单的"hello world"程序放在.c
文件里-
#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
可以搜索库并 link 它。 这是您的 c 库的名称和目标发挥作用的地方。
现在您可以简单地执行 cargo run
,它将构建 C 库以及您的 rust 项目!
您还可以 运行 详细模式 (-vv
),以查看 C 库构建的详细输出。
现在您所要做的就是从您的 main.rs
-
调用图书馆
#[link(name="foo", kind="static")]
extern {
// this is rustified prototype of the function from our C library
fn testcall(v: f32);
}
fn main() {
println!("Hello, world from Rust!");
// calling the function from foo library
unsafe {
testcall(3.14159);
};
}
很简单,但是博客的作者对extern函数进行了注释-
Note that this prototype requires a bit of manual conversion from C prototype to Rust one. It’s straightforward for simple functions operating on primitive value types, but might be more difficult to craft when more complex data types are involved.
这让我们回到了 SDL2 crate,编译它需要的 C 库,linking 它们然后 然后 构建 crate 本身肯定需要大量的修补- 但我希望这为您指明了正确的方向。
由于我对货物设置和大量文档的无知,我遇到了一些问题。
cargo.toml 文件是当前文件:
[package]
name = "hello"
version = "0.1.0"
authors = ["PC4\Author"]
[dependencies]
sdl2 = { version = "0.34.1", features = ["bundled", "static-link"] }
编译了 SDL2 依赖项,但它实际上正在使用 Visual Studio。我真正想做的是在编译板条箱依赖项时使用另一个文件夹中的自定义编译器。
您可以指定 rust 在构建依赖项时使用 gcc
编译器,只要您已经为 mingw
正确安装了 rust。为确保您的 Rust 已针对 mingw
正确配置 - 使用
下面的步骤原来是官方提到的rust-sdl2 docs
完成后,您需要一个构建脚本来 link 依赖库。但首先,您需要库。从官方下载 mingw
个特定的库 libsdl website
现在您需要将这些文件以正确的顺序放在与 cargo.toml
相同的文件夹中-
SDL2-devel-2.0.x-mingw.tar.gz\SDL2-2.0.x\i686-w64-mingw32\bin -> gnu-mingw\dll
SDL2-devel-2.0.x-mingw.tar.gz\SDL2-2.0.x\x86_64-w64-mingw32\bin -> gnu-mingw\dll
SDL2-devel-2.0.x-mingw.tar.gz\SDL2-2.0.x\i686-w64-mingw32\lib -> gnu-mingw\lib
SDL2-devel-2.0.x-mingw.tar.gz\SDL2-2.0.x\x86_64-w64-mingw32\lib -> gnu-mingw\lib
gnu-mingw
应该是和cargo.toml
现在您需要构建脚本本身,创建一个名为 build.rs
的文件并将其放入 cargo.toml
[package]
build = "build.rs"
可以找到有关构建脚本的更多信息here
这是脚本-
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 特定的内容。
现在在你的构建配置中 [build]
里面 cargo.toml
,你需要把 -
中找到可用目标的列表target = "x86_64-pc-windows-gnu"
有关构建配置的更多信息,请参见 config docs
作为奖励,如果您想使用其他编译器(gcc
除外)。您所要做的就是确保必要的库位于同一目录中,并将它们放入您的 [target.TARGET_NAME]
linker = "path\to\c\linker"
ar = "path\to\c\ar"
用您选择的目标三元组替换 TARGET_NAME
。
编辑:根据 OP 的要求,提供有关如何将 CMake 与 Rust 结合的信息。
使用带有 Rust 的 CMake 是可能的,但是,编译和构建第三方依赖项几乎肯定需要自定义构建脚本,该脚本将能够替换依赖项自己的构建脚本。
为了说明,让我们使用带 Rust 的 CMake 制作一个自定义的简单 C 静态库。
以下步骤最初在this flames of code blog
中提到首先你需要一个C工程,目前除了.c
文件外不需要太多,你应该把.c
文件放在一个名为libfoo
的目录中(或者不管你的图书馆叫什么)。现在,您可以将此 libfoo
目录放在与 rust
项目相同的目录中或任何您喜欢的目录中,但请记住路径。
继续把一个简单的"hello world"程序放在.c
文件里-
#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
可以搜索库并 link 它。 这是您的 c 库的名称和目标发挥作用的地方。
现在您可以简单地执行 cargo run
,它将构建 C 库以及您的 rust 项目!
您还可以 运行 详细模式 (-vv
),以查看 C 库构建的详细输出。
现在您所要做的就是从您的 main.rs
-
#[link(name="foo", kind="static")]
extern {
// this is rustified prototype of the function from our C library
fn testcall(v: f32);
}
fn main() {
println!("Hello, world from Rust!");
// calling the function from foo library
unsafe {
testcall(3.14159);
};
}
很简单,但是博客的作者对extern函数进行了注释-
Note that this prototype requires a bit of manual conversion from C prototype to Rust one. It’s straightforward for simple functions operating on primitive value types, but might be more difficult to craft when more complex data types are involved.
这让我们回到了 SDL2 crate,编译它需要的 C 库,linking 它们然后 然后 构建 crate 本身肯定需要大量的修补- 但我希望这为您指明了正确的方向。