8个版本
0.4.2 | 2023年11月2日 |
---|---|
0.4.1 | 2023年11月1日 |
0.3.0 | 2022年10月17日 |
0.2.0 | 2020年6月2日 |
0.1.2 | 2019年5月25日 |
#48 在 构建工具
2,998 每月下载量
52KB
1K SLoC
conan-rs
Rust对C/C++包管理器conan(conan.io)的包装,简化构建脚本中的使用
TLDR
将conan添加到构建依赖部分
cargo add conan --build
修改项目build.rs
脚本以调用cargo并自动发出conan构建信息。建议使用从cargo目标信息派生的conan配置文件
注意:假设conan可执行文件为conan
,除非设置了CONAN
环境变量。
use std::path::Path;
use std::env;
use conan::*;
fn main() {
let target_os = env::var("CARGO_CFG_TARGET_OS").unwrap();
let target_arch = env::var("CARGO_CFG_TARGET_ARCH").unwrap();
let conan_profile = format!("{}-{}", target_os, target_arch);
let command = InstallCommandBuilder::new()
.with_profile(&conan_profile)
.build_policy(BuildPolicy::Missing)
.with_option("sign", "True")
.recipe_path(Path::new("conanfile.txt"))
.build();
if let Some(build_info) = command.generate() {
println!("using conan build info");
build_info.cargo_emit();
}
let build_comman = BuildCommandBuilder::new()
.with_recipe_path(PathBuf::from("../../../conanfile.py"))
.with_build_path(PathBuf::from("../../../build/"))
.build();
if let Some(exit_status) = build_comman.run() {
println!("conan build exited with {}", exit_status);
}
}
最简单的方法是在build.rs旁边添加conanfile.txt文件
[requires]
openssl/1.1.1l@devolutions/stable
要测试是否正确导入了conan包,运行cargo -vv build
,并查找类似以下输出
[conan-test 0.1.0] using conan build info
[conan-test 0.1.0] cargo:rustc-link-search=native=/Users/mamoreau/.conan/data/openssl/1.1.1l/devolutions/stable/package/ce597277d61571523403b5b500bda70acd77cd8a/lib
[conan-test 0.1.0] cargo:rustc-link-lib=crypto
[conan-test 0.1.0] cargo:rustc-link-lib=ssl
[conan-test 0.1.0] cargo:include=/Users/mamoreau/.conan/data/openssl/1.1.1l/devolutions/stable/package/ce597277d61571523403b5b500bda70acd77cd8a/include
[conan-test 0.1.0] cargo:rerun-if-env-changed=CONAN
此示例conan配方可在此处找到,即使它不在公共conan仓库中。
文档
Conan安装
InstallCommand
结构体表示"conan install"命令,便于在Rust项目中安装包和依赖关系管理。InstallCommandBuilder
提供了一种流畅的API来构建InstallCommand
。
示例
use conan::{InstallCommandBuilder, BuildPolicy};
use std::path::Path;
fn main() -> Result<(), Box<dyn std::error::Error>> {
let install_command = InstallCommandBuilder::new()
.with_profile("default")
.build_policy(BuildPolicy::Missing)
.recipe_path(Path::new("conanfile.txt"))
.output_dir(Path::new("output_directory"))
.build();
if install_command.generate().is_some() {
println!("Packages installed successfully!");
} else {
println!("Failed to install packages.");
}
Ok(())
}
在这个例子中,InstallCommandBuilder
使用配置文件、构建策略、配方文件路径和输出目录配置了 Conan 安装命令。generate()
执行命令,在成功时返回 Some(BuildInfo)
,在失败时返回 None
。
Conan 构建
BuildCommand
结构体表示 "conan build" 命令,简化了 Rust 项目中 Conan 包的构建过程。BuildCommandBuilder
提供了一个流畅的 API 来构建 BuildCommand
。
示例
use conan::BuildCommandBuilder;
use std::path::PathBuf;
fn main() -> Result<(), Box<dyn std::error::Error>> {
let build_command = BuildCommandBuilder::new()
.with_recipe_path(PathBuf::from("conanfile.py"))
.with_build_path(PathBuf::from("build"))
.should_configure(true)
.should_build(true)
.should_install(true)
.build();
match build_command.run() {
Some(status) if status.success() => println!("Build succeeded!"),
_ => println!("Build failed."),
}
Ok(())
}
在这个例子中,使用 BuildCommandBuilder 配置 Conan 构建命令的路径和选项。run() 执行命令,在成功时返回 Some(ExitStatus),在失败时返回 None。
Conan 包
PackageCommand
结构体表示 "conan package" 命令,用于创建包。 PackageCommandBuilder
提供了一个流畅的 API 来构建 PackageCommand
。
ConanPackage
结构体提供了管理生成 C++ 库的 Conan 包的功能,并帮助将这些库与 Rust 链接起来。
示例用法
use conan::{PackageCommandBuilder, PackageComman, ConanPackage};
use std::path::PathBuf;
fn main() -> Result<(), Box<dyn std::error::Error>> {
let package_command = PackageCommandBuilder::new()
.with_recipe_path(PathBuf::from("conanfile.py"))
.build();
if let Some(status) = package_command.run() {
if !status.success() {
println!("Package command failed.");
return Ok(());
}
}
let conan_package = ConanPackage::new(PathBuf::from("./package/"));
conan_package.emit_cargo_libs_linkage(PathBuf::from("lib"))?;
Ok(())
}
用例:将 Rust 集成到遗留的 c++/conan1 代码库中
将 Rust 集成到遗留的 C++ 代码库中可以是一种战略性的举措,以利用 Rust 的内存安全特性,同时保持现有的 C++ 功能。在本指南中,我们将探讨如何使用 conan-rs
和 autocxx
将 Rust 集成到遗留的 C++/Conan 代码库中。
现有的 C++ Conan 代码库结构
您的现有 C++ 代码库(带有 Conan 和 CMake)可能如下所示
.
├── build
│ ├── bin
│ │ └── target_bin
│ ├── lib
│ │ ├── lib1.a
│ │ ├── lib2.a
│ │ ├── lib3.so
│ │ ├── lib4.so
│ │ ├── ...
│ │ └── libn.a
├── CMakeLists.txt
├── conanfile.py
├── include
│ └── ...
├── profiles
│ ├── ...
├── src
│ ├── target_bin
│ │ ├── ...
│ ├── lib1
│ │ ├── CMakeLists.txt
│ │ ├── include
│ │ │ └── ...
│ │ ├── src
│ │ │ └── ...
│ ├── ...
│ ├── libn
│ │ ├── CMakeLists.txt
│ │ ├── include
│ │ │ └── ...
│ │ ├── src
│ │ │ └── ...
请确保构建后构建目录看起来像这样(您的配置可能不同)
├── build
│ ├── bin
│ │ └── target_bin
│ ├── lib
│ │ ├── lib1.a
│ │ ├── lib2.a
│ │ ├── lib3.so
│ │ ├── lib4.so
│ │ ├── ...
│ │ └── libn.a
此外,您的 conanfile 中的 package()
方法应该将库和相关包含文件组织在一个类似下面的配置中
package
├── conaninfo.txt
├── conanmanifest.txt
├── include
└── lib
├── lib1.a
├── ...
└── libn.so
创建 Rust "桥接" 包
在代码库中创建一个 Rust 库包,作为 C++ 和 Rust 代码之间的 "桥接"。
.
├── build.rs
├── Cargo.lock
├── Cargo.toml
└── src
├── lib.rs
└── main.rs
设置依赖项
安装 conan-rs
和 autocxx
cargo add conan-rs autocxx --build
cargo add autocxx
设置构建脚本
在您的包的构建脚本(build.rs
)中,配置集成
use conan::{
BuildCommandBuilder, BuildPolicy, ConanPackage, InstallCommandBuilder, PackageCommandBuilder,
};
use std::env;
use std::path::{Path, PathBuf};
use std::process;
fn main() {
println!("cargo:rerun-if-changed=build.rs");
println!("cargo:rerun-if-changed=../../path/to/your/conanfile.py");
println!("cargo:rerun-if-changed=../../path/to/your/build/directory");
let out_dir = env::var("OUT_DIR").map(PathBuf::from).unwrap_or_else(|_| {
eprintln!("Error: OUT_DIR environment variable is not set");
process::exit(1);
});
println!("OUT_DIR: {:?}", out_dir);
let conan_profile = env::var("CONAN_PROFILE").unwrap_or_else(|_| "default".to_string());
let install_command = InstallCommandBuilder::new()
.with_profile(&conan_profile)
.with_remote("your_remote")
.build_policy(BuildPolicy::Missing)
.with_profile("../../path/to/your/conan/profile")
.recipe_path(Path::new("../../path/to/your/conanfile.py"))
.output_dir(Path::new("../../path/to/your/build/directory"))
.with_options(&["option1=True", "option2=True"])
.update_check()
.build();
if let Some(build_info) = install_command.generate() {
println!("using conan build info");
build_info.cargo_emit();
} else {
eprintln!("Error: failed to run conan install");
process::exit(1);
}
BuildCommandBuilder::new()
.with_recipe_path(PathBuf::from("../../path/to/your/conanfile.py"))
.with_build_path(PathBuf::from("../../path/to/your/build/directory"))
.build()
.run()
.unwrap_or_else(|| {
eprintln!("Error: Unable to run conan build");
process::exit(1);
});
let package_command = PackageCommandBuilder::new()
.with_recipe_path(PathBuf::from("../../path/to/your/conanfile.py"))
.with_build_path(PathBuf::from("../../path/to/your/build/directory"))
.with_package_path(out_dir.clone())
.build();
if let Some(exit_status) = package_command.run() {
println!("conan package exited with {}", exit_status);
}
let conan_package = ConanPackage::new(out_dir.clone());
if let Err(err) = conan_package.emit_cargo_libs_linkage("lib".into()) {
eprintln!("Error: Unable to emit cargo linkage: {:?}", err);
process::exit(1);
}
let include_path = out_dir.join("include");
let mut builder = autocxx_build::Builder::new("src/lib.rs", &[include_path])
.build()
.unwrap_or_else(|err| {
eprintln!("Error: Unable to generate bindings: {:?}", err);
process::exit(1);
});
builder.flag_if_supported("-std=c++14").compile("foo_bar");
println!("cargo:rerun-if-changed=src/main.rs");
}
在 Rust 中使用 C++ 库
最后,在 lib.rs
中使用 C++ 库
use autocxx::prelude::*;
include_cpp! {
#include "path/to/header.h"
safety!(unsafe_ffi)
generate!("FunctionFromCpp")
}
pub fn use_cpp_function() {
let result = ffi::FunctionFromCpp();
// Use result as needed
}
依赖项
~3.5–5.5MB
~103K SLoC