1
- # conan-rs
1
+ <p align =" center " >
2
+ <img src=https://blog.conan.io/assets/conan_cargo.png width=138/>
3
+ </p >
2
4
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 >
4
6
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 >
6
8
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 >
8
17
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
13
24
```
14
25
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:
16
29
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.
18
32
19
33
``` rust
20
34
use std :: path :: Path ;
@@ -57,7 +71,8 @@ The simplest approach is to add a conanfile.txt file alongside build.rs:
57
71
openssl/1.1.1l@devolutions/stable
58
72
```
59
73
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:
61
76
62
77
``` bash
63
78
[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
68
83
[conan-test 0.1.0] cargo:rerun-if-env-changed=CONAN
69
84
```
70
85
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