当前位置: 首页 > article >正文

【CXX】4.1 CXX与Cargo集成配置详解

一、规范设置

在Rust项目中,为了与C++代码进行互操作,cxx crate提供了一种便捷的方式。通过cxx-build crate,Cargo可以被扩展为一个C++构建系统。这在Cargo.toml文件中通过设置依赖来实现:

[dependencies]
cxx = "1.0"

[build-dependencies]
cxx-build = "1.0"

build.rs文件是Cargo的构建脚本,它在这里用于配置cxx-build。以下是一个示例脚本,展示了如何桥接Rust和C++代码,并添加额外的C++源文件以及指定C++标准:

// build.rs 
fn main() { 
cxx_build::bridge("src/main.rs")  // 返回 cc::Build 
.file("src/demo.cc") 
.std("c++11") 
.compile("cxxbridge-demo"); 
 
println!("cargo:rerun-if-changed=src/main.rs"); 
println!("cargo:rerun-if-changed=src/demo.cc"); 
println!("cargo:rerun-if-changed=include/demo.h"); 
} 

二、头文件默认根路径

在cxx-build中,C++头文件默认路径是crate。如果您的crate名称为yourcratename,并且头文件位于path/to/header.h,则在C++代码中应这样包含它:
#include "yourcratename/path/to/header.h"
如果您希望为头文件选择不同的前缀,可以通过修改build.rs中的CFG.include_prefix来实现。

// build.rs
use cxx_build::CFG;
fn main() {
    CFG.include_prefix = "my/project";

    cxx_build::bridge(...)...
}

随后,位于path/to/header.h的标头现在可以包含为:

#include "my/project/path/to/header.h"

三、包括生成的代码

如果你的#[cxx::bridge]模块包含一个extern “Rust"块,即从Rust向C++或任何共享数据结构公开的类型或函数,那么CXX生成的C++头文件可以使用Rust源文件名称添加”.h"获得。 如:

// 头文件从路径path/to/lib.rs生产: 
#include "yourcratename/path/to/lib.rs.h"

四、包括依赖项中的头文件

您可以手写头文件(.h文件)包含在Cargo包中,也可以包含CXX生成的头文件。
它的工作原理与本地头文件的include相同,如:
`#include "dependencycratename/path/to/their/header.h``
请注意,跨crate导入仅在直接依赖项之间可用。传递依赖性是不支持的。
此外,只有当依赖项的Cargo.toml清单包含链接键时,直接依赖项的头文件才可导入。否则,其头文件将无法从同一crate外部导入。

五、高级功能

以下CFG设置仅在您编写需要支持下游crates #include(包括其C++公共头文件)的库时与您相关。

1、公开导出头文件目录 (CFG.exported_header_dirs)

CFG.exported_header_dirs(绝对路径向量)定义了一组额外的目录,从这些目录中,当前crate直接依赖的crate以及导出此crate头文件的其他将能够#include headers。
向exported_header_dirs添加目录类似于通过cc crate的build::include将其添加到当前构建中,但也使该目录可用于希望#包含crate中某个头的下游crate。如果仅使用Build::include添加目录,则包含您的标头的下游crate也需要手动将相同的目录添加到自己的构建中。
使用exported_header_dirs时,您的板条箱还必须在Cargo.toml中为自己设置一个链接键。原因是Cargo对没有链接键的构建脚本的执行没有顺序,这意味着下游机箱的构建脚本可能会在您决定将什么放入exported_header_dirs之前执行。
例子

// build.rs

use cxx_build::CFG;
use std::path::PathBuf;

fn main() {
    let python3 = pkg_config::probe_library("python3").unwrap();
    let python_include_paths = python3.include_paths.iter().map(PathBuf::as_path);
    CFG.exported_header_dirs.extend(python_include_paths);

    cxx_build::bridge("src/bridge.rs").compile("demo");
}

例子
你的crate想要重新排列它导出的头文件,以及它们在crate源代码目录中的本地布局。
假设发布的crate包含一个文件./include/myheader.h,但希望它作为#include "foo/v1/public.h"可供下游crates使用。
// build.rs

use cxx_build::CFG;
use std::path::Path;
use std::{env, fs};

fn main() {
let out_dir = env::var_os(“OUT_DIR”).unwrap();
let headers = Path::new(&out_dir).join(“headers”);
CFG.exported_header_dirs.push(&headers);

// We contain `include/myheader.h` locally, but
// downstream will use `#include "foo/v1/public.h"`
let foo = headers.join("foo").join("v1");
fs::create_dir_all(&foo).unwrap();
fs::copy("include/myheader.h", foo.join("public.h")).unwrap();

cxx_build::bridge("src/bridge.rs").compile("demo");

}

公开导出依赖项

CFG.exported_header_prefixes(字符串向量)分别引用您的一个直接依赖项的include_pefixe或其前缀。它们描述了您的哪些依赖项参与了crate的C++公共API,而不是crate实现的私有使用。
一般来说,如果您的某个header#包含来自某个依赖项的内容,则需要将该依赖项的include_prefix放入CFG.exported_header_prefix中(或将其链接键放入CFG.export ed_header_links中;见下文)。另一方面,如果只有C++实现文件而不是头文件从依赖项导入,则不会导出该依赖项。
导出标头的意义在于,如果下游代码(crate𝒜)包含来自crate(")的标头的#include,并且您的标头包含来自依赖项(⻜)中的某些内容的#incleed,导出的依赖项⻜在下游机箱𝒜的构建过程中变得可用。否则,下游板条箱𝒜不知道⻜,也无法找到您的标头所指的标头,从而无法构建。
使用exported_header_prefixes时,您的板条箱还必须在Cargo.toml中为自己设置一个链接键。
例子
假设你有一个有5个直接依赖项的crate,每个依赖项的include_prefix都是:

"crate0"
"group/api/crate1"
"group/api/crate2"
"group/api/contrib/crate3"
"detail/crate4"

您的头包含前四个类型,因此我们将这些类型作为公共API的一部分重新导出,而crate4仅由您的cc文件内部使用,而不是您的头文件,因此我们不导出:

// build.rs

use cxx_build::CFG;

fn main() {
    CFG.exported_header_prefixes = vec!["crate0", "group/api"];

    cxx_build::bridge("src/bridge.rs")
        .file("src/impl.cc")
        .compile("demo");
}

为了实现更细粒度的控制,有CFG.exported_header_links(字符串向量),每个链接都引用机箱直接依赖项之一的links属性(links manifest键)。
这实现了与CFG.exported_header_prefixes相同的结果,方法是将C++依赖项重新导出为机箱的公共API的一部分,但对于多个机箱可能共享相同的include_prefixes并且您希望导出一些但不导出其他机箱的情况,可以进行更精细的控制。Cargo保证链接属性是唯一的标识符。
使用exported_header_links时,您的板条箱还必须在Cargo.toml中为自己设置一个链接键。
例子

// build.rs

use cxx_build::CFG;

fn main() {
    CFG.exported_header_links.push("git2");

    cxx_build::bridge("src/bridge.rs").compile("demo");
}

http://www.kler.cn/a/556421.html

相关文章:

  • DeepSeek04-导出导入模型文件
  • Bootstrap Blazor UI 中 <Table> 组件 <TableColumn> 使用备忘01:EF Core 外码处理
  • Could not download npm for node v14.21.3(nvm无法下载节点v14.21.3的npm)
  • SeaTunnel社区「Demo方舟计划」首期活动上线—— MySQL CDC实时同步至PostgreSQL实战
  • Android 底层判断/dev/video节点是否是可用摄像头
  • 机器学习实战(10):深度学习初探——卷积神经网络(CNN)
  • Python连接MySQL数据库完全指南
  • 【Linux】命名管道------Linux进程间通信的桥梁
  • AI时代前端工程师的自主学习:挑战与机遇
  • MySQL 5.7优化
  • MySQL(1)基础篇
  • C语言预处理学习笔记
  • Web入侵实战分析-常见web攻击类应急处置实验2
  • Test the complete case
  • 区块链中的递归长度前缀(RLP)序列化详解
  • 黑马点评_登录模块
  • 雷军推荐:WPS 与 Pastemate 联用,效率飞升新高度
  • 线段树【C语言】【C++】
  • pycharm 调试 debug 进入 remote_sources
  • 【Vue3 项目中父子组件之间如何互相传值、传递方法】