• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Rust Module Configuration Rules and Guide
2
3## Introduction
4
5Rust is a static, strongly typed programming language. It has advantages such as secure memory management, high running performance, and native support for multi-thread development. Rust uses Cargo to create projects and compile and build Rust code.
6
7To integrate C/C++ code and improve the build speed, OpenHarmony uses Generate Ninja (GN) and Ninja as its build system. GN has simple and easy-to-use build language, and Ninja provides direct and efficient assembly-level build rules.
8
9To integrate Rust code and maximize the interaction between the C/C++ code used in OpenHarmony and Rust, OpenHarmony uses GN as a unified build tool to build Rust source code files (xxx.rs) and is added with features such as interoperability with C/C++, compile time lints, test, IDL conversion, third-party library integration, and IDE. In addition, the GN framework is extended to support automatic interface conversion, which greatly simplifying development.
10
11### Basic Concepts
12
13| Term | Description                                                        |
14| ----- | ------------------------------------------------------------ |
15| Cargo | Cargo is an official build tool used by Rust. It allows Rust projects to declare dependencies and ensures reproducible builds.|
16| crate | Crate is a unit that can be independently compiled.                               |
17| Lint| Lint is a code analysis tool used to flag programming errors, bugs, stylistic errors, and suspicious constructs. It performs extensive error analysis on programs.|
18
19
20
21## Configuration Rules
22OpenHarmony provides a variety of GN templates for compiling Rust executables, dynamic libraries, and static libraries. The following table describes the templates.
23
24| GN Template                  | Description             | Output                                           |
25| ------------------------ | ----------------- | ----------------------------------------------- |
26| ohos_rust_executable     | Rust executable file.   | Rust executable file, without the file name extension.                       |
27| ohos_rust_shared_library | Rust dynamic library.       | Rust dylib dynamic library, with the default file name extension **.dylib.so**.            |
28| ohos_rust_static_library | Rust static library.       | Rust rlib static library, with the default file name extension **.rlib**.                 |
29| ohos_rust_proc_macro     | Rust proc_macro library.   | Rust proc_macro library, with the default file name extension **.so**.                |
30| ohos_rust_shared_ffi     | Rust Foreign Function Interface (FFI) dynamic library.   | Rust cdylib dynamic library, which is called by the C/C++ module. The default file name extension is **.so**.|
31| ohos_rust_static_ffi     | Rust FFI static library.   | Rust staticlib library, which is called by the C/C++ module. The default file name extension is **.a**.  |
32| ohos_rust_cargo_crate    | Third-party Cargo crate.| Third-party Rust crates, which support rlib, dylib, and bin.             |
33| ohos_rust_systemtest     | Rust system test cases. | Executable system test cases for Rust, without the file name extension.               |
34| ohos_rust_unittest       | Rust unit test cases. | Executable unit test cases for Rust, without the file name extension.               |
35
36
37
38## Configuration Guide
39The configuration of the Rust module is similar to that of the C/C++ module. For details, see [Module Configuration Rules](subsys-build-module.md). The following provides examples of using different Rust templates.
40### Configuring a Rust Static Library
41The following example shows how to use the **ohos_rust_executable** and **ohos_rust_static_library** templates to build a binary executable and a static rlib library, respectively. The executable depends on the static library.
42
43The procedure is as follows:
44
451. Create **build/rust/tests/test_rlib_crate/src/simple_printer.rs**.
46
47   ```rust
48   //! simple_printer
49
50   /// struct RustLogMessage
51
52   pub struct RustLogMessage {
53       /// i32: id
54       pub id: i32,
55       /// String: msg
56       pub msg: String,
57   }
58
59   /// function rust_log_rlib
60   pub fn rust_log_rlib(msg: RustLogMessage) {
61       println!("id:{} message:{:?}", msg.id, msg.msg)
62   }
63   ```
64
652. Create **build/rust/tests/test_rlib_crate/src/main.rs**.
66
67   ```rust
68   //! rlib_crate example for Rust.
69
70   extern crate simple_printer_rlib;
71
72   use simple_printer_rlib::rust_log_rlib;
73   use simple_printer_rlib::RustLogMessage;
74
75   fn main() {
76       let msg: RustLogMessage = RustLogMessage {
77           id: 0,
78           msg: "string in rlib crate".to_string(),
79       };
80       rust_log_rlib(msg);
81   }
82   ```
83
843. Configure the GN build script **build/rust/tests/test_rlib_crate/BUILD.gn**.
85
86   ```
87   import("//build/ohos.gni")
88
89   ohos_rust_executable("test_rlib_crate") {
90     sources = [ "src/main.rs" ]
91     deps = [ ":simple_printer_rlib" ]
92   }
93
94   ohos_rust_static_library("simple_printer_rlib") {
95     sources = [ "src/simple_printer.rs" ]
96     crate_name = "simple_printer_rlib"
97     crate_type = "rlib"
98     features = [ "std" ]
99   }
100   ```
101
1024. Run the **BUILD.gn** to generate the build targets.
103
104   ![test_rlib_crate](./figures/test_rlib_crate.png)
105
106### Configuring a Third-Party Library
107
108The **BUILD.gn** file of the rust third-party library can be automatically generated using the cargo2gn tool. For details, see [Using Cargo2gn](subsys-build-cargo2gn-guide.md).
109
110The following example shows how to use the **ohos_rust_executable** and **ohos_rust_cargo_crate** templates to compile a third-party static library rlib file that contains a prebuilt file **build.rs**.
111
112The procedure is as follows:
113
1141. Create **build/rust/tests/test_rlib_cargo_crate/crate/src/lib.rs**.
115
116   ```rust
117   include!(concat!(env!("OUT_DIR"), "/generated/generated.rs"));
118
119   pub fn say_hello_from_crate() {
120       assert_eq!(run_some_generated_code(), 45);
121       #[cfg(is_new_rustc)]
122       println!("Is new rustc");
123       #[cfg(is_old_rustc)]
124       println!("Is old rustc");
125       #[cfg(is_ohos)]
126       println!("Is ohos");
127       #[cfg(is_mac)]
128       println!("Is darwin");
129       #[cfg(has_feature_a)]
130       println!("Has feature_a");
131       #[cfg(not(has_feature_a))]
132       panic!("Wasn't passed feature_a");
133       #[cfg(not(has_feature_b))]
134       #[cfg(test_a_and_b)]
135       panic!("feature_b wasn't passed");
136       #[cfg(has_feature_b)]
137       #[cfg(not(test_a_and_b))]
138       panic!("feature_b was passed");
139   }
140
141   #[cfg(test)]
142   mod tests {
143       /// Test features are passed through from BUILD.gn correctly. This test is the target configuration.
144       #[test]
145       #[cfg(test_a_and_b)]
146       fn test_features_passed_target1() {
147           #[cfg(not(has_feature_a))]
148           panic!("feature a was not passed");
149           #[cfg(not(has_feature_b))]
150           panic!("feature b was not passed");
151       }
152
153       #[test]
154       fn test_generated_code_works() {
155           assert_eq!(crate::run_some_generated_code(), 45);
156       }
157   }
158   ```
159
1602. Create **build/rust/tests/test_rlib_cargo_crate/crate/src/main.rs**.
161
162   ```rust
163   pub fn main() {
164       test_rlib_crate::say_hello_from_crate();
165   }
166   ```
167
1683. Create **build/rust/tests/test_rlib_cargo_crate/crate/build.rs**.
169
170   ```rust
171   use std::env;
172   use std::path::Path;
173   use std::io::Write;
174   use std::process::Command;
175   use std::str::{self, FromStr};
176
177   fn main() {
178       println!("cargo:rustc-cfg=build_script_ran");
179       let my_minor = match rustc_minor_version() {
180           Some(my_minor) => my_minor,
181           None => return,
182       };
183
184       if my_minor >= 34 {
185           println!("cargo:rustc-cfg=is_new_rustc");
186       } else {
187           println!("cargo:rustc-cfg=is_old_rustc");
188       }
189
190       let target = env::var("TARGET").unwrap();
191
192       if target.contains("ohos") {
193           println!("cargo:rustc-cfg=is_ohos");
194       }
195       if target.contains("darwin") {
196           println!("cargo:rustc-cfg=is_mac");
197       }
198
199       let feature_a = env::var_os("CARGO_FEATURE_MY_FEATURE_A").is_some();
200       if feature_a {
201           println!("cargo:rustc-cfg=has_feature_a");
202       }
203       let feature_b = env::var_os("CARGO_FEATURE_MY_FEATURE_B").is_some();
204       if feature_b {
205           println!("cargo:rustc-cfg=has_feature_b");
206       }
207
208       // Tests used to verify whether Cargo features are enabled.
209       assert!(Path::new("build.rs").exists());
210       assert!(Path::new(&env::var_os("CARGO_MANIFEST_DIR").unwrap()).join("build.rs").exists());
211       assert!(Path::new(&env::var_os("OUT_DIR").unwrap()).exists());
212
213       // Ensure that the following env var is set.
214       env::var_os("CARGO_CFG_TARGET_ARCH").unwrap();
215
216       generate_some_code().unwrap();
217   }
218
219   fn generate_some_code() -> std::io::Result<()> {
220       let test_output_dir = Path::new(&env::var_os("OUT_DIR").unwrap()).join("generated");
221       let _ = std::fs::create_dir_all(&test_output_dir);
222       // Test that environment variables from .gn files are passed to build scripts.
223       let preferred_number = env::var("ENV_VAR_FOR_BUILD_SCRIPT").unwrap();
224       let mut file = std::fs::File::create(test_output_dir.join("generated.rs"))?;
225       write!(file, "fn run_some_generated_code() -> u32 {{ {} }}", preferred_number)?;
226       Ok(())
227   }
228
229   fn rustc_minor_version() -> Option<u32> {
230       let rustc_bin = match env::var_os("RUSTC") {
231           Some(rustc_bin) => rustc_bin,
232           None => return None,
233       };
234
235       let output = match Command::new(rustc_bin).arg("--version").output() {
236           Ok(output) => output,
237           Err(_) => return None,
238       };
239
240       let rustc_version = match str::from_utf8(&output.stdout) {
241           Ok(rustc_version) => rustc_version,
242           Err(_) => return None,
243       };
244
245       let mut pieces = rustc_version.split('.');
246       if pieces.next() != Some("rustc 1") {
247           return None;
248       }
249
250       let next_var = match pieces.next() {
251           Some(next_var) => next_var,
252           None => return None,
253       };
254
255       u32::from_str(next_var).ok()
256   }
257   ```
258
2594. Configure the GN build script **build/rust/tests/test_rlib_cargo_crate/BUILD.gn**.
260
261   ```
262   import("//build/templates/rust/ohos_cargo_crate.gni")
263
264   ohos_cargo_crate("target") {
265     crate_name = "test_rlib_crate"
266     crate_root = "crate/src/lib.rs"
267     sources = [ "crate/src/lib.rs" ]
268
269     #To generate the build_script binary
270     build_root = "crate/build.rs"
271     build_sources = [ "crate/build.rs" ]
272     build_script_outputs = [ "generated/generated.rs" ]
273
274     features = [
275       "my-feature_a",
276       "my-feature_b",
277       "std",
278     ]
279     rustflags = [
280       "--cfg",
281       "test_a_and_b",
282     ]
283     rustenv = [ "ENV_VAR_FOR_BUILD_SCRIPT=45" ]
284   }
285
286   # Exists to test the case that a single crate has both a library and a binary
287   ohos_cargo_crate("test_rlib_crate_associated_bin") {
288     crate_root = "crate/src/main.rs"
289     crate_type = "bin"
290     sources = [ "crate/src/main.rs" ]
291
292     #To generate the build_script binary
293     build_root = "crate/build.rs"
294     build_sources = [ "crate/build.rs" ]
295     features = [
296       "my-feature_a",
297       "my-feature_b",
298       "std",
299     ]
300     rustenv = [ "ENV_VAR_FOR_BUILD_SCRIPT=45" ]
301     deps = [ ":target" ]
302   }
303   ```
304
3055. Run the **BUILD.gn** to generate the build target.
306
307   ![test_rlib_cargo_crate](./figures/test_rlib_cargo_crate.png)
308
309### Other Configuration Examples
310You can find the Rust module configuration examples in the **build/rust/tests** directory.
311| Directory                                    | Description                                                  |
312| -------------------------------------------- | ------------------------------------------------------------ |
313| build/rust/tests/test_bin_crate              | Tests the build of an executable file on the host platform and running of the executable file on the target platform.|
314| build/rust/tests/test_static_link            | Tests the static linking of an executable file to a standard library.                          |
315| build/rust/tests/test_dylib_crate            | Tests the build of a dynamic library and dynamic linking.                            |
316| build/rust/tests/test_rlib_crate             | Tests the build of a static library and static linking.                            |
317| build/rust/tests/test_proc_macro_crate       | Tests the build of Rust process macros and the linking function. Test cases are provided for different types of macros.|
318| build/rust/tests/test_cdylib_crate           | Tests the generation of Rust FFI bindings to a C/C++ dynamic library.                           |
319| build/rust/tests/test_staticlib_crate        | Tests the generation of Rust FFI bindings to a C/C++ static library.                           |
320| build/rust/tests/test_rust_ut                | Tests the Rust code unit test template.                   |
321| build/rust/tests/test_rust_st                | Tests the Rust code system test template.                   |
322| build/rust/tests/test_bin_cargo_crate        | Tests the build and running of a Rust third-party executable file. The third-party source code contains **build.rs**.|
323| build/rust/tests/test_rlib_cargo_crate       | Tests the build of a Rust third-party static library and static linking. The third-party source code contains **build.rs**.|
324| build/rust/tests/test_proc_macro_cargo_crate | Tests the build of Rust third-party process macros and linking. The third-party source code contains **build.rs**.    |
325
326## Reference
327
328### Feature Examples
329
330#### Linking a C/C++ library in Rust Source Code
331By default, the dynamic library of the OpenHarmony C/C++ module is in the **.z.so** format. However, when the Rust **-l** command is executed, only the dynamic library in the **.so** format is linked by default. If a C/C++ dynamic library is used as the dependency, you need to add **output_extension = "so"** to the GN build script of the dynamic library to make the generated dynamic library be named with **.so** instead of **.z.so**.
332
333If a dynamic library is directly linked in the Rust source code, the dynamic library must be in **.so** format. In this case, use the dynamic library name without "lib". The following is an example of linking **libhilog.so** in the Rust source code.
334
335```rust
336#[link(name = "hilog")]
337```
338#### Using externs
339If a module depends on the binary rlib library, you can use the **externs** attribute.
340```
341executable("foo") {
342    sources = [ "main.rs" ]
343    externs = [{                    # Convert it to `--extern bar=path/to/bar.rlib` during the compilation.
344        crate_name = "bar"
345        path = "path/to/bar.rlib"
346    }]
347}
348```
349### Lint Rules
350The OpenHarmony framework supports two types of lints: rustc lints and Clippy lints. Each type of lint has three levels: openharmony (highest), vendor, and none (lowest).
351
352When configuring the Rust module, you can specify the lint level in **rustc_lints** or **clippy_lints**.
353
354If **rustc_lints** or **clippy_lints** is not configured in the module, the lint level is matched based on the module path. Different restrictions apply to the syntax specifications of Rust code in different directories. Therefore, you need to pay attention to the path of the module when configuring the Rust module to build in OpenHarmony.
355
356#### Levels of rustc Lints and Clippy Lints
357| **Lint Type**| **Module Attribute**| **Lint Level**| **Lint Level Flag**| **Lint Content**                                               |
358| ------------- | ------------ | ------------- | ----------------- | ------------------------------------------------------------ |
359| rustc lints  | rustc_lints  | openharmony   | RustOhosLints     | "-A deprecated", "-D missing-docs", "-D warnings"           |
360| rustc lints  | rustc_lints  | vendor        | RustcVendorLints  | "-A deprecated", "-D warnings"                                |
361| rustc lints  | rustc_lints  | none          | allowAllLints     | "-cap-lints allow"                                           |
362| Clippy lints | clippy_lints | openharmony   | ClippyOhosLints   | "-A clippy::type-complexity", "-A clippy::unnecessary-wraps", "-A clippy::unusual-byte-groupings", "-A clippy::upper-case-acronyms" |
363| Clippy lints | clippy_lints | vendor        | ClippyVendorLints | "-A clippy::complexity", "-A Clippy::perf", "-A clippy::style" |
364| Clippy lints | clippy_lints | none          | allowAllLints     | "--cap-lints allow"                                          |
365
366#### Mapping Between Code Paths and Lint Levels
367| Path      | Lint Level  |
368| ---------- | ----------- |
369| thirdparty | none        |
370| prebuilts  | none        |
371| vendor     | vendor      |
372| device     | vendor      |
373| others     | openharmony |
374
375### [Interactive Tool User Guide](subsys-build-bindgen-cxx-guide.md)
376### [Using Cargo2gn](subsys-build-cargo2gn-guide.md)
377