Rust cargo.toml 为 C 链接器和编译器指定自定义路径

Gam*_*per 11 rust sdl-2 rust-crates rust-cargo

由于我对货物设置和大量文档的无知,我遇到了一些问题。

cargo.toml 文件是当前的:

[package]
name = "hello"
version = "0.1.0"
authors = ["PC4\\Author"]

[dependencies]
sdl2 = { version = "0.34.1", features = ["bundled", "static-link"] }
Run Code Online (Sandbox Code Playgroud)

SDL2 依赖项已编译,但实际上使用的是 Visual Studio。我真正想做的是在编译 crate 依赖项时使用另一个文件夹中的自定义编译器。

Cha*_*ase 9

您可以gcc在构建依赖项时指定 rust 以使用编译器,只要您已为mingw. 为了确保您的 Rust 正确配置为mingw使用此线程。请记住,默认情况下,Windows 的 Rust 将配置为 MSVC,而不是 mingw。

以下步骤最初在rust-sdl2官方文档中提到

完成后,您将需要一个构建脚本来将库链接到依赖项。但首先,您需要库。mingw从官方libsdl 网站下载特定库

现在您需要将这些文件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
Run Code Online (Sandbox Code Playgroud)

gnu-mingw 应该是在同一目录中的文件夹 cargo.toml

现在您需要构建脚本本身,创建一个名为的文件build.rs并将其放入您[package]cargo.toml

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");
                }
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

注意:这有意省略了 MSVC 特定的内容。

现在在你的构建配置[build]里面cargo.toml,你需要把-

目标 = "x86_64-pc-windows-gnu"

可以在货物构建文档中找到可用目标的列表

更多关于构建配置的信息可以在配置文档中找到

作为奖励,如果您想使用其他编译器(除了gcc)。您所要做的就是确保必要的库位于同一目录中并将它们放入您的[target.TARGET_NAME]

linker = "path\\to\\c\\linker"
ar = "path\\to\\c\\ar"
Run Code Online (Sandbox Code Playgroud)

替换TARGET_NAME为您选择的目标三倍。

编辑:根据 OP 的要求,提供有关如何将 CMake 与 Rust 结合的信息。

使用带有 rust 的 CMake 是可能的,但是,编译和构建第三方依赖项几乎肯定需要一个自定义构建脚本,该脚本能够替换依赖项自己的构建脚本。

为了说明这一点,让我们使用带有 rust 的 CMake 制作一个自定义的、简单的 C 静态库。

以下步骤最初在此代码博客的火焰中提到

首先,您需要一个 C 项目,目前除了.c文件之外不需要太多 ,您应该将.c文件放在一个名为libfoo(或您的库可能被调用的任何名称)的目录中。现在您可以将此libfoo目录放在与您的rust项目相同的目录中或您喜欢的任何位置,但请记住路径。

继续在.c文件中放置一个简单的“hello world”程序 -

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
Run Code Online (Sandbox Code Playgroud)

:该功能应该不会是主要的,因为我们正在构建一个静态库)

现在我们需要CMakelists.txt在同一个目录中 -

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");
                }
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

这是一个非常简单的脚本,尽管最后一行很重要 - 它确保库的目的地是.- 我们稍后必须从 rust 中找到这个库。

所以现在,文件结构可能看起来像 -

.
??? Cargo.lock
??? Cargo.toml
??? libfoo
?   ??? CMakeLists.txt
?   ??? foo.c
??? src
    ??? main.rs
Run Code Online (Sandbox Code Playgroud)

现在对于 Rust 部分,您将需要一个构建脚本和cmake项目的构建依赖项。

将构建脚本添加到cargo.toml-

[package]
build="build.rs"
Run Code Online (Sandbox Code Playgroud)

而依赖——

[build-dependencies]
cmake = "0.1.31"
Run Code Online (Sandbox Code Playgroud)

现在在你的build.rs,你必须调用cmake-

linker = "path\\to\\c\\linker"
ar = "path\\to\\c\\ar"
Run Code Online (Sandbox Code Playgroud)

.build()部分很简单,但为什么那些println!在那里?

那些将必要的命令写入stdout,以便cargo可以搜索库并链接它。这就是您的 c 库的名称和目的地发挥作用的地方

现在您可以简单地执行cargo run,它将构建 C 库以及您的 Rust 项目!

您还可以以详细模式 ( -vv)运行它,以查看 C 库构建的详细输出。

现在您要做的就是从您的main.rs-调用库

#include <stdio.h>

void testcall(float value)
{
    printf("Hello, world from C! Value passed: %f\n",value);
}
Run Code Online (Sandbox Code Playgroud)

非常简单,但是博客的作者为 extern 函数留下了注释-

请注意,此原型需要从 C 原型到 Rust 原型的一些手动转换。对于在原始值类型上操作的简单函数来说,这很简单,但在涉及更复杂的数据类型时可能更难制作。

这让我们回到了 SDL2 crate,编译它需要的 C 库,链接它们然后构建 crate 本身肯定需要大量的修补 - 但我希望这为您指明了正确的方向。