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.<br>
6To 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.
7To 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.
8
9### Basic Concepts
10
11| Term | Description                                                        |
12| ----- | ------------------------------------------------------------ |
13| Cargo | Cargo is an official build tool used by Rust. It allows Rust projects to declare dependencies and ensures reproducible builds.|
14| crate | Crate is a unit that can be independently compiled.                               |
15| 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.|
16
17
18
19## Configuration Rules
20OpenHarmony provides a variety of GN templates for compiling Rust executables, dynamic libraries, and static libraries. The following table describes the templates.
21
22| GN Template                    | Description             | Output                               |
23|--------------------------|-----------------|-----------------------------------|
24| ohos_rust_executable     | Rust executable file.      | Rust executable file, without the file name extension.                   |
25| ohos_rust_shared_library | Rust dynamic library.        | Rust dylib dynamic library, with the default file name extension **.dylib.so**.      |
26| ohos_rust_static_library | Rust static library.        | Rust rlib static library, with the default file name extension **.rlib**.           |
27| ohos_rust_proc_macro     | Rust proc_macro library. | Rust proc_macro library, with the default file name extension **.so**.        |
28| 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**.|
29| 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**.|
30| ohos_rust_cargo_crate    | Third-party Cargo crate. | Third-party Rust crates, which support rlib, dylib, and bin.      |
31| ohos_rust_systemtest     | Rust system test cases.     | Executable system test cases for Rust, without the file name extension.               |
32| ohos_rust_unittest       | Rust unit test cases.     | Executable unit test cases for Rust, without the file name extension.               |
33| ohos_rust_fuzztest       | Rust fuzzing test cases.  | Executable fuzzing test cases for Rust, without the file name extension.             |
34
35
36## Configuration Guide
37The 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.
38### Configuring a Rust Static Library
39The 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.
40The procedure is as follows:
41
421. Create **build/rust/tests/test_rlib_crate/src/simple_printer.rs**.
43
44   ```rust
45   //! simple_printer
46
47   /// struct RustLogMessage
48
49   pub struct RustLogMessage {
50       /// i32: id
51       pub id: i32,
52       /// String: msg
53       pub msg: String,
54   }
55
56   /// function rust_log_rlib
57   pub fn rust_log_rlib(msg: RustLogMessage) {
58       println!("id:{} message:{:?}", msg.id, msg.msg)
59   }
60   ```
61
622. Create **build/rust/tests/test_rlib_crate/src/main.rs**.
63
64   ```rust
65   //! rlib_crate example for Rust.
66
67   extern crate simple_printer_rlib;
68
69   use simple_printer_rlib::rust_log_rlib;
70   use simple_printer_rlib::RustLogMessage;
71
72   fn main() {
73       let msg: RustLogMessage = RustLogMessage {
74           id: 0,
75           msg: "string in rlib crate".to_string(),
76       };
77       rust_log_rlib(msg);
78   }
79   ```
80
813. Configure the GN build script **build/rust/tests/test_rlib_crate/BUILD.gn**.
82
83   ```
84   import("//build/ohos.gni")
85
86   ohos_rust_executable("test_rlib_crate") {
87     sources = [ "src/main.rs" ]
88     deps = [ ":simple_printer_rlib" ]
89   }
90
91   ohos_rust_static_library("simple_printer_rlib") {
92     sources = [ "src/simple_printer.rs" ]
93     crate_name = "simple_printer_rlib"
94     crate_type = "rlib"
95     features = [ "std" ]
96   }
97   ```
98
994. Run **BUILD.gn** to generate the build targets.
100
101   ![test_rlib_crate](./figures/test_rlib_crate.png)
102
103### Configuring a Third-Party Library
104
105The **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).
106
107The 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**.
108The procedure is as follows:
109
1101. Create **build/rust/tests/test_rlib_cargo_crate/crate/src/lib.rs**.
111
112   ```rust
113   include!(concat!(env!("OUT_DIR"), "/generated/generated.rs"));
114
115   pub fn say_hello_from_crate() {
116       assert_eq!(run_some_generated_code(), 45);
117       #[cfg(is_new_rustc)]
118       println!("Is new rustc");
119       #[cfg(is_old_rustc)]
120       println!("Is old rustc");
121       #[cfg(is_ohos)]
122       println!("Is ohos");
123       #[cfg(is_mac)]
124       println!("Is darwin");
125       #[cfg(has_feature_a)]
126       println!("Has feature_a");
127       #[cfg(not(has_feature_a))]
128       panic!("Wasn't passed feature_a");
129       #[cfg(not(has_feature_b))]
130       #[cfg(test_a_and_b)]
131       panic!("feature_b wasn't passed");
132       #[cfg(has_feature_b)]
133       #[cfg(not(test_a_and_b))]
134       panic!("feature_b was passed");
135   }
136
137   #[cfg(test)]
138   mod tests {
139       /// Test features are passed through from BUILD.gn correctly. This test is the target configuration.
140       #[test]
141       #[cfg(test_a_and_b)]
142       fn test_features_passed_target1() {
143           #[cfg(not(has_feature_a))]
144           panic!("feature a was not passed");
145           #[cfg(not(has_feature_b))]
146           panic!("feature b was not passed");
147       }
148
149       #[test]
150       fn test_generated_code_works() {
151           assert_eq!(crate::run_some_generated_code(), 45);
152       }
153   }
154   ```
155
1562. Create **build/rust/tests/test_rlib_cargo_crate/crate/src/main.rs**.
157
158   ```rust
159   pub fn main() {
160       test_rlib_crate::say_hello_from_crate();
161   }
162   ```
163
1643. Create **build/rust/tests/test_rlib_cargo_crate/crate/build.rs**.
165
166   ```rust
167   use std::env;
168   use std::path::Path;
169   use std::io::Write;
170   use std::process::Command;
171   use std::str::{self, FromStr};
172
173   fn main() {
174       println!("cargo:rustc-cfg=build_script_ran");
175       let my_minor = match rustc_minor_version() {
176           Some(my_minor) => my_minor,
177           None => return,
178       };
179
180       if my_minor >= 34 {
181           println!("cargo:rustc-cfg=is_new_rustc");
182       } else {
183           println!("cargo:rustc-cfg=is_old_rustc");
184       }
185
186       let target = env::var("TARGET").unwrap();
187
188       if target.contains("ohos") {
189           println!("cargo:rustc-cfg=is_ohos");
190       }
191       if target.contains("darwin") {
192           println!("cargo:rustc-cfg=is_mac");
193       }
194
195       let feature_a = env::var_os("CARGO_FEATURE_MY_FEATURE_A").is_some();
196       if feature_a {
197           println!("cargo:rustc-cfg=has_feature_a");
198       }
199       let feature_b = env::var_os("CARGO_FEATURE_MY_FEATURE_B").is_some();
200       if feature_b {
201           println!("cargo:rustc-cfg=has_feature_b");
202       }
203
204       // Tests used to verify whether Cargo features are enabled.
205       assert!(Path::new("build.rs").exists());
206       assert!(Path::new(&env::var_os("CARGO_MANIFEST_DIR").unwrap()).join("build.rs").exists());
207       assert!(Path::new(&env::var_os("OUT_DIR").unwrap()).exists());
208
209       // Ensure that the following env var is set.
210       env::var_os("CARGO_CFG_TARGET_ARCH").unwrap();
211
212       generate_some_code().unwrap();
213   }
214
215   fn generate_some_code() -> std::io::Result<()> {
216       let test_output_dir = Path::new(&env::var_os("OUT_DIR").unwrap()).join("generated");
217       let _ = std::fs::create_dir_all(&test_output_dir);
218       // Test that environment variables from .gn files are passed to build scripts.
219       let preferred_number = env::var("ENV_VAR_FOR_BUILD_SCRIPT").unwrap();
220       let mut file = std::fs::File::create(test_output_dir.join("generated.rs"))?;
221       write!(file, "fn run_some_generated_code() -> u32 {{ {} }}", preferred_number)?;
222       Ok(())
223   }
224
225   fn rustc_minor_version() -> Option<u32> {
226       let rustc_bin = match env::var_os("RUSTC") {
227           Some(rustc_bin) => rustc_bin,
228           None => return None,
229       };
230
231       let output = match Command::new(rustc_bin).arg("--version").output() {
232           Ok(output) => output,
233           Err(_) => return None,
234       };
235
236       let rustc_version = match str::from_utf8(&output.stdout) {
237           Ok(rustc_version) => rustc_version,
238           Err(_) => return None,
239       };
240
241       let mut pieces = rustc_version.split('.');
242       if pieces.next() != Some("rustc 1") {
243           return None;
244       }
245
246       let next_var = match pieces.next() {
247           Some(next_var) => next_var,
248           None => return None,
249       };
250
251       u32::from_str(next_var).ok()
252   }
253   ```
254
2554. Configure the GN build script **build/rust/tests/test_rlib_cargo_crate/BUILD.gn**.
256
257   ```
258   import("//build/templates/rust/ohos_cargo_crate.gni")
259
260   ohos_cargo_crate("target") {
261     crate_name = "test_rlib_crate"
262     crate_root = "crate/src/lib.rs"
263     sources = [ "crate/src/lib.rs" ]
264
265     #To generate the build_script binary
266     build_root = "crate/build.rs"
267     build_sources = [ "crate/build.rs" ]
268     build_script_outputs = [ "generated/generated.rs" ]
269
270     features = [
271       "my-feature_a",
272       "my-feature_b",
273       "std",
274     ]
275     rustflags = [
276       "--cfg",
277       "test_a_and_b",
278     ]
279     rustenv = [ "ENV_VAR_FOR_BUILD_SCRIPT=45" ]
280   }
281
282   # Exists to test the case that a single crate has both a library and a binary
283   ohos_cargo_crate("test_rlib_crate_associated_bin") {
284     crate_root = "crate/src/main.rs"
285     crate_type = "bin"
286     sources = [ "crate/src/main.rs" ]
287
288     #To generate the build_script binary
289     build_root = "crate/build.rs"
290     build_sources = [ "crate/build.rs" ]
291     features = [
292       "my-feature_a",
293       "my-feature_b",
294       "std",
295     ]
296     rustenv = [ "ENV_VAR_FOR_BUILD_SCRIPT=45" ]
297     deps = [ ":target" ]
298   }
299   ```
300
3015. Run **BUILD.gn** to generate the build target.
302
303   ![test_rlib_cargo_crate](./figures/test_rlib_cargo_crate.png)
304
305### Other Configuration Examples
306You can find the Rust module configuration examples in the **build/rust/tests** directory.
307
308| Directory                                        | Description                                                    |
309|----------------------------------------------|----------------------------------------------------------|
310| 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.|
311| build/rust/tests/test_static_link            | Tests the static linking of an executable file to a standard library.                                       |
312| build/rust/tests/test_dylib_crate            | Tests the build of a dynamic library and dynamic linking.                                        |
313| build/rust/tests/test_rlib_crate             | Tests the build of a static library and static linking.                                        |
314| 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.                      |
315| build/rust/tests/test_cdylib_crate           | Tests the generation of Rust FFI bindings to a C/C++ dynamic library.                                   |
316| build/rust/tests/test_staticlib_crate        | Tests the generation of Rust FFI bindings to a C/C++ static library.                                   |
317| build/rust/tests/rust_test_ut                | Tests the Rust code unit test template.                              |
318| build/rust/tests/rust_test_st                | Tests the Rust code system test template.                              |
319| 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**.                    |
320| 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**.                    |
321| 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**.                      |
322| build/rust/tests/rust_test_fuzzb             | Tests the Rust code fuzzing test template.                                     |
323## Reference
324
325### Feature Examples
326
327#### Linking a C/C++ library in Rust Source Code
328By 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**.
329If 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.
330
331```rust
332#[link(name = "hilog")]
333```
334#### Using externs
335If a module depends on the binary rlib library, you can use the **externs** attribute.
336```
337executable("foo") {
338    sources = [ "main.rs" ]
339    externs = [{                    # Convert it to `--extern bar=path/to/bar.rlib` during the compilation.
340        crate_name = "bar"
341        path = "path/to/bar.rlib"
342    }]
343}
344```
345### Lint Rules
346The 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).
347When configuring the Rust module, you can specify the lint level in **rustc_lints** or **clippy_lints**.
348If **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.
349
350#### Levels of Rustc Lints and Clippy Lints
351| **Lint Type**| **Module Attribute**| **Lint Level**| **Lint Level Flag**| **Lint Content**                                               |
352| ------------- | ------------ | ------------- | ----------------- | ------------------------------------------------------------ |
353| rustc lints  | rustc_lints  | openharmony   | RustOhosLints     | "-A deprecated", "-D missing-docs", "-D warnings"           |
354| rustc lints  | rustc_lints  | vendor        | RustcVendorLints  | "-A deprecated", "-D warnings"                                |
355| rustc lints  | rustc_lints  | none          | allowAllLints     | "-cap-lints allow"                                           |
356| Clippy lints | clippy_lints| openharmony   | ClippyOhosLints   | "-A clippy::type-complexity", "-A clippy::unnecessary-wraps", "-A clippy::unusual-byte-groupings", "-A clippy::upper-case-acronyms" |
357| Clippy lints | clippy_lints | vendor        | ClippyVendorLints | "-A clippy::complexity", "-A Clippy::perf", "-A clippy::style" |
358| Clippy lints | clippy_lints | none          | allowAllLints     | "--cap-lints allow"                                          |
359
360#### Mapping Between Code Paths and Lint Levels
361| Path      | Lint Level  |
362| ---------- | ----------- |
363| thirdparty | none        |
364| prebuilts  | none        |
365| vendor     | vendor      |
366| device     | vendor      |
367| others     | openharmony |
368
369### [Interactive Tool User Guide](subsys-build-bindgen-cxx-guide.md)
370### [Using Cargo2gn](subsys-build-cargo2gn-guide.md)
371