Skip to content

Commit 9c09b6b

Browse files
Tomcat-42awakecoding
authored andcommitted
Doc: Update Readme
Signed-off-by: Pablo <[email protected]>
1 parent 934f2d7 commit 9c09b6b

File tree

1 file changed

+338
-12
lines changed

1 file changed

+338
-12
lines changed

README.md

Lines changed: 338 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,34 @@
1-
# conan-rs
1+
<p align="center">
2+
<img src=https://blog.conan.io/assets/conan_cargo.png width=138/>
3+
</p>
24

3-
A Rust wrapper of the conan C/C++ package manager (conan.io) to simplify usage in build scripts.
5+
<h1 align="center">conan-rs</h1>
46

5-
The conan executable is assumed to be `conan` unless the `CONAN` environment variable is set.
7+
<p align="center"><strong>A Rust wrapper of the conan C/C++ package manager (conan.io) to simplify usage in build scripts</strong></p>
68

7-
Add conan to the Cargo.toml build-dependencies section:
9+
<div align="center">
10+
<a href="https://crates.io/crates/conan" target="_blank">
11+
<img src="https://img.shields.io/crates/v/conan"></a>
12+
<a href="https://docs.rs/conan" target="_blank">
13+
<img src="https://img.shields.io/docsrs/conan"></a>
14+
<a href="https://github.com/Devolutions/conan-rs" target="_blank">
15+
<img alt="GitHub Repo stars" src="https://img.shields.io/github/stars/Devolutions/conan-rs?style=social">
16+
</div>
817

9-
```toml
10-
# Cargo.toml
11-
[build-dependencies]
12-
conan = "0.4.1"
18+
## TLDR
19+
20+
Add conan to the build-dependencies section:
21+
22+
```bash
23+
cargo add conan --build
1324
```
1425

15-
Modify the project build.rs script to invoke cargo and emit the conan build information automatically.
26+
Modify the project `build.rs` script to invoke cargo and emit the conan build
27+
information automatically. Using conan profiles with names derived from the
28+
cargo target information is recommended:
1629

17-
Using conan profiles with names derived from the cargo target information is recommended:
30+
NOTE: The conan executable is assumed to be `conan` unless the `CONAN`
31+
environment variable is set.
1832

1933
```rust
2034
use std::path::Path;
@@ -57,7 +71,8 @@ The simplest approach is to add a conanfile.txt file alongside build.rs:
5771
openssl/1.1.1l@devolutions/stable
5872
```
5973

60-
To test if the conan packages are properly imported, run `cargo -vv build`, and look for output similar to this:
74+
To test if the conan packages are properly imported, run `cargo -vv build`, and
75+
look for output similar to this:
6176

6277
```bash
6378
[conan-test 0.1.0] using conan build info
@@ -68,4 +83,315 @@ To test if the conan packages are properly imported, run `cargo -vv build`, and
6883
[conan-test 0.1.0] cargo:rerun-if-env-changed=CONAN
6984
```
7085

71-
This sample conan recipe is available [here](https://github.com/Devolutions/conan-public), even if it is not available in a public conan repository.
86+
This sample conan recipe is available
87+
[here](https://github.com/Devolutions/conan-public), even if it is not available
88+
in a public conan repository.
89+
90+
## Documentation
91+
92+
### Conan Install
93+
94+
The `InstallCommand` struct represents the "conan install" command, facilitating
95+
package installation and dependency management in Rust projects.
96+
`InstallCommandBuilder` provides a fluent API for constructing an
97+
`InstallCommand`.
98+
99+
### Example
100+
101+
```rust
102+
use conan::{InstallCommandBuilder, BuildPolicy};
103+
use std::path::Path;
104+
105+
fn main() -> Result<(), Box<dyn std::error::Error>> {
106+
let install_command = InstallCommandBuilder::new()
107+
.with_profile("default")
108+
.build_policy(BuildPolicy::Missing)
109+
.recipe_path(Path::new("conanfile.txt"))
110+
.output_dir(Path::new("output_directory"))
111+
.build();
112+
113+
if install_command.generate().is_some() {
114+
println!("Packages installed successfully!");
115+
} else {
116+
println!("Failed to install packages.");
117+
}
118+
119+
Ok(())
120+
}
121+
```
122+
123+
In this example, `InstallCommandBuilder` configures the Conan install command
124+
with a profile, build policy, recipe file path, and output directory.
125+
`generate()` executes the command, returning `Some(BuildInfo)` on success or
126+
`None` on failure.
127+
128+
### Conan Build
129+
130+
The `BuildCommand` struct represents the "conan build" command, facilitating the
131+
build process of Conan packages in Rust projects. `BuildCommandBuilder` provides
132+
a fluent API to construct a `BuildCommand`.
133+
134+
### Example
135+
136+
```rust
137+
use conan::BuildCommandBuilder;
138+
use std::path::PathBuf;
139+
140+
fn main() -> Result<(), Box<dyn std::error::Error>> {
141+
let build_command = BuildCommandBuilder::new()
142+
.with_recipe_path(PathBuf::from("conanfile.py"))
143+
.with_build_path(PathBuf::from("build"))
144+
.should_configure(true)
145+
.should_build(true)
146+
.should_install(true)
147+
.build();
148+
149+
match build_command.run() {
150+
Some(status) if status.success() => println!("Build succeeded!"),
151+
_ => println!("Build failed."),
152+
}
153+
154+
Ok(())
155+
}
156+
```
157+
158+
In this example, _BuildCommandBuilder_ is used to configure the Conan build
159+
command with paths and options. _run()_ executes the command, returning
160+
_Some(ExitStatus)_ on success or _None_ on failure.
161+
162+
### Conan Package
163+
164+
The `PackageCommand` struct represents the "conan package" command and is used
165+
for creating packages. The `PackageCommandBuilder` provides a fluent API for
166+
constructing a `PackageCommand`.
167+
168+
The `ConanPackage` struct provides functionality for managing Conan packages
169+
that generate C++ libraries, and it aids in linking these libraries with Rust.
170+
171+
#### Example Usage:
172+
173+
```rust
174+
use conan::{PackageCommandBuilder, PackageComman, ConanPackage};
175+
use std::path::PathBuf;
176+
177+
fn main() -> Result<(), Box<dyn std::error::Error>> {
178+
let package_command = PackageCommandBuilder::new()
179+
.with_recipe_path(PathBuf::from("conanfile.py"))
180+
.build();
181+
182+
if let Some(status) = package_command.run() {
183+
if !status.success() {
184+
println!("Package command failed.");
185+
return Ok(());
186+
}
187+
}
188+
189+
let conan_package = ConanPackage::new(PathBuf::from("./package/"));
190+
conan_package.emit_cargo_libs_linkage(PathBuf::from("lib"))?;
191+
192+
Ok(())
193+
}
194+
```
195+
196+
## Use Case: Integrating Rust into a legacy c++/conan1 codebase
197+
198+
Integrating Rust into a legacy C++ codebase can be a strategic move to leverage
199+
Rust's memory safety features while maintaining existing C++ functionality. In
200+
this guide, we will explore how to integrate Rust into a legacy C++/Conan
201+
codebase using `conan-rs` and `autocxx`.
202+
203+
### Existing C++ Conan Codebase Structure
204+
205+
Your existing C++ codebase with Conan and CMake might look like this:
206+
207+
```
208+
.
209+
├── build
210+
│ ├── bin
211+
│ │ └── target_bin
212+
│ ├── lib
213+
│ │ ├── lib1.a
214+
│ │ ├── lib2.a
215+
│ │ ├── lib3.so
216+
│ │ ├── lib4.so
217+
│ │ ├── ...
218+
│ │ └── libn.a
219+
├── CMakeLists.txt
220+
├── conanfile.py
221+
├── include
222+
│ └── ...
223+
├── profiles
224+
│ ├── ...
225+
├── src
226+
│ ├── target_bin
227+
│ │ ├── ...
228+
│ ├── lib1
229+
│ │ ├── CMakeLists.txt
230+
│ │ ├── include
231+
│ │ │ └── ...
232+
│ │ ├── src
233+
│ │ │ └── ...
234+
│ ├── ...
235+
│ ├── libn
236+
│ │ ├── CMakeLists.txt
237+
│ │ ├── include
238+
│ │ │ └── ...
239+
│ │ ├── src
240+
│ │ │ └── ...
241+
```
242+
243+
Make sure that after a build the build dir look like this(Your configuration may
244+
vary):
245+
246+
```
247+
├── build
248+
│ ├── bin
249+
│ │ └── target_bin
250+
│ ├── lib
251+
│ │ ├── lib1.a
252+
│ │ ├── lib2.a
253+
│ │ ├── lib3.so
254+
│ │ ├── lib4.so
255+
│ │ ├── ...
256+
│ │ └── libn.a
257+
```
258+
259+
Also, the `package()` method in your conanfile should organize your libs and
260+
associated includes in a config akin to:
261+
262+
```
263+
package
264+
├── conaninfo.txt
265+
├── conanmanifest.txt
266+
├── include
267+
└── lib
268+
├── lib1.a
269+
├── ...
270+
└── libn.so
271+
```
272+
273+
### Creating the Rust "Bridge" Crate
274+
275+
Create a Rust library crate within the codebase to act as the "bridge" between
276+
the C++ and Rust code:
277+
278+
```
279+
.
280+
├── build.rs
281+
├── Cargo.lock
282+
├── Cargo.toml
283+
└── src
284+
├── lib.rs
285+
└── main.rs
286+
```
287+
288+
### Setting Up Dependencies
289+
290+
Install `conan-rs` and `autocxx`:
291+
292+
```bash
293+
cargo add conan-rs autocxx --build
294+
cargo add autocxx
295+
```
296+
297+
### Setting the build script:
298+
299+
In your crate's build script (`build.rs`), configure the integration:
300+
301+
```rust
302+
use conan::{
303+
BuildCommandBuilder, BuildPolicy, ConanPackage, InstallCommandBuilder, PackageCommandBuilder,
304+
};
305+
use std::env;
306+
use std::path::{Path, PathBuf};
307+
use std::process;
308+
309+
fn main() {
310+
println!("cargo:rerun-if-changed=build.rs");
311+
println!("cargo:rerun-if-changed=../../path/to/your/conanfile.py");
312+
println!("cargo:rerun-if-changed=../../path/to/your/build/directory");
313+
314+
let out_dir = env::var("OUT_DIR").map(PathBuf::from).unwrap_or_else(|_| {
315+
eprintln!("Error: OUT_DIR environment variable is not set");
316+
process::exit(1);
317+
});
318+
319+
println!("OUT_DIR: {:?}", out_dir);
320+
321+
let conan_profile = env::var("CONAN_PROFILE").unwrap_or_else(|_| "default".to_string());
322+
let install_command = InstallCommandBuilder::new()
323+
.with_profile(&conan_profile)
324+
.with_remote("your_remote")
325+
.build_policy(BuildPolicy::Missing)
326+
.with_profile("../../path/to/your/conan/profile")
327+
.recipe_path(Path::new("../../path/to/your/conanfile.py"))
328+
.output_dir(Path::new("../../path/to/your/build/directory"))
329+
.with_options(&["option1=True", "option2=True"])
330+
.update_check()
331+
.build();
332+
333+
if let Some(build_info) = install_command.generate() {
334+
println!("using conan build info");
335+
build_info.cargo_emit();
336+
} else {
337+
eprintln!("Error: failed to run conan install");
338+
process::exit(1);
339+
}
340+
341+
BuildCommandBuilder::new()
342+
.with_recipe_path(PathBuf::from("../../path/to/your/conanfile.py"))
343+
.with_build_path(PathBuf::from("../../path/to/your/build/directory"))
344+
.build()
345+
.run()
346+
.unwrap_or_else(|| {
347+
eprintln!("Error: Unable to run conan build");
348+
process::exit(1);
349+
});
350+
351+
let package_command = PackageCommandBuilder::new()
352+
.with_recipe_path(PathBuf::from("../../path/to/your/conanfile.py"))
353+
.with_build_path(PathBuf::from("../../path/to/your/build/directory"))
354+
.with_package_path(out_dir.clone())
355+
.build();
356+
357+
if let Some(exit_status) = package_command.run() {
358+
println!("conan package exited with {}", exit_status);
359+
}
360+
361+
let conan_package = ConanPackage::new(out_dir.clone());
362+
if let Err(err) = conan_package.emit_cargo_libs_linkage("lib".into()) {
363+
eprintln!("Error: Unable to emit cargo linkage: {:?}", err);
364+
process::exit(1);
365+
}
366+
367+
let include_path = out_dir.join("include");
368+
let mut builder = autocxx_build::Builder::new("src/lib.rs", &[include_path])
369+
.build()
370+
.unwrap_or_else(|err| {
371+
eprintln!("Error: Unable to generate bindings: {:?}", err);
372+
process::exit(1);
373+
});
374+
375+
builder.flag_if_supported("-std=c++14").compile("foo_bar");
376+
println!("cargo:rerun-if-changed=src/main.rs");
377+
}
378+
```
379+
380+
### Using C++ Libraries in Rust
381+
382+
Finally, use the C++ libraries in `lib.rs`:
383+
384+
```rust
385+
use autocxx::prelude::*;
386+
387+
include_cpp! {
388+
#include "path/to/header.h"
389+
safety!(unsafe_ffi)
390+
generate!("FunctionFromCpp")
391+
}
392+
393+
pub fn use_cpp_function() {
394+
let result = ffi::FunctionFromCpp();
395+
// Use result as needed
396+
}
397+
```

0 commit comments

Comments
 (0)