diff --git a/en/device-dev/subsystems/Readme-EN.md b/en/device-dev/subsystems/Readme-EN.md index ee9395bf43e05bb96162763054bef034a971c3b0..3ffc19418a1e83b0e87abc59cbfc460dfca1a392 100644 --- a/en/device-dev/subsystems/Readme-EN.md +++ b/en/device-dev/subsystems/Readme-EN.md @@ -18,6 +18,10 @@ - [Configuring Parameters for Accelerating Local Build](subsys-build-reference.md) - [Viewing Ninja Build Information](subsys-build-reference.md) - [HAP Build Guide](subsys-build-gn-hap-compilation-guide.md) + - Rust Build Guide + - [Rust Module Configuration Rules and Guide](subsys-build-rust-compilation.md) + - [Using Interactive Tools](subsys-build-bindgen-cxx-guide.md) + - [Using Cargo2gn](subsys-build-cargo2gn-guide.md) - [FAQs](subsys-build-FAQ.md) - [ArkCompiler Development](subsys-arkcompiler-guide.md) - [Distributed Remote Startup](subsys-remote-start.md) diff --git a/en/device-dev/subsystems/figures/bindgen_and_cxx_tools.png b/en/device-dev/subsystems/figures/bindgen_and_cxx_tools.png new file mode 100644 index 0000000000000000000000000000000000000000..655472225f3dd5581e5849e0af80ff89468094a1 Binary files /dev/null and b/en/device-dev/subsystems/figures/bindgen_and_cxx_tools.png differ diff --git a/en/device-dev/subsystems/figures/bindgen_test.png b/en/device-dev/subsystems/figures/bindgen_test.png new file mode 100644 index 0000000000000000000000000000000000000000..4c4d01b567e35fd07ce7a8a90256281cd9fcc165 Binary files /dev/null and b/en/device-dev/subsystems/figures/bindgen_test.png differ diff --git a/en/device-dev/subsystems/figures/cpp_call_rust.png b/en/device-dev/subsystems/figures/cpp_call_rust.png new file mode 100644 index 0000000000000000000000000000000000000000..49503982f893fb6c24d1e41c24ae54aa9681e2c6 Binary files /dev/null and b/en/device-dev/subsystems/figures/cpp_call_rust.png differ diff --git a/en/device-dev/subsystems/figures/rust_call_cpp.png b/en/device-dev/subsystems/figures/rust_call_cpp.png new file mode 100644 index 0000000000000000000000000000000000000000..eba899d0b111c71420b43c36c21e519228a06d54 Binary files /dev/null and b/en/device-dev/subsystems/figures/rust_call_cpp.png differ diff --git a/en/device-dev/subsystems/figures/test_rlib_cargo_crate.png b/en/device-dev/subsystems/figures/test_rlib_cargo_crate.png new file mode 100644 index 0000000000000000000000000000000000000000..86ade8272f625e6aa4336c713d52ee537918531c Binary files /dev/null and b/en/device-dev/subsystems/figures/test_rlib_cargo_crate.png differ diff --git a/en/device-dev/subsystems/figures/test_rlib_crate.png b/en/device-dev/subsystems/figures/test_rlib_crate.png new file mode 100644 index 0000000000000000000000000000000000000000..3df5877b7a3f583513527de1adcdabb80755961a Binary files /dev/null and b/en/device-dev/subsystems/figures/test_rlib_crate.png differ diff --git a/en/device-dev/subsystems/subsys-build-bindgen-cxx-guide.md b/en/device-dev/subsystems/subsys-build-bindgen-cxx-guide.md new file mode 100644 index 0000000000000000000000000000000000000000..cd9bdd0db7b444dc30ed56b666a62d54e194fdcb --- /dev/null +++ b/en/device-dev/subsystems/subsys-build-bindgen-cxx-guide.md @@ -0,0 +1,421 @@ +# Interactive Tool User Guide + +## Introduction + +You can use Bindgen and CXX to implement the interaction between Rust and C/C++. Bindgen enables Rust to call C by converting C interfaces into Rust interfaces. CXX implement interaction between C++ and Rust by generating bindings between C interfaces and Rust interfaces. + +![bindgen_and_cxx_tools](./figures/bindgen_and_cxx_tools.png) + +## Using Bindgen + +### Procedure +The following example shows how to use Bindgen to implement invocation of C by Rust. + +1. In the header file **lib.h**, define two interfaces **FuncAAddB** and **SayHello** in C. The **FuncAAddB** interface calculates the sum of two numbers, and the **SayHello** interface prints strings. + + ```c + #ifndef BUILD_RUST_TESTS_BINDGEN_TEST_LIB_H_ + #define BUILD_RUST_TESTS_BINDGEN_TEST_LIB_H_ + #include + #include "build/rust/tests/test_bindgen_test/test_for_hello_world/lib2.h" + + uint32_t FuncAAddB(uint32_t a, uint32_t b); + void SayHello(const char *message); + + #endif // BUILD_RUST_TESTS_BINDGEN_TEST_LIB_H_ + ``` + + +2. Add the implementation of the two interfaces to **lib.c**. + + ```c + #include "build/rust/tests/test_bindgen_test/test_for_hello_world/lib.h" + #include + #include + + void SayHello(const char *message) + { + printf("This is a test for bindgen hello world:\n"); + printf("%s\n", message); + } + + uint32_t FuncAAddB(uint32_t a, uint32_t b) + { + printf("This is a test for bindgen of a + b:\n"); + return a + b; + } + ``` + +3. Create the **main.rs** file to call C interfaces through c_ffi using Rust. Note that insecure interfaces called by Rust must be encapsulated by using **unsafe**. + + ```rust + //! bindgen test for hello world + #![allow(clippy::approx_constant)] + mod c_ffi { + #![allow(dead_code)] + #![allow(non_upper_case_globals)] + #![allow(non_camel_case_types)] + include!(env!("BINDGEN_RS_FILE")); + } + /// pub fn add_two_numbers_in_c + pub fn add_two_numbers_in_c(a: u32, b: u32) -> u32 { + unsafe { c_ffi::FuncAAddB(a, b) } + } + + use std::ffi::c_char; + use std::ffi::CString; + + /// fn main() + fn main() { + println!("{} + {} = {}", 3, 7, add_two_numbers_in_c(3, 7)); + let c_str = CString::new("This is a message from C").unwrap(); + let c_world: *const c_char = c_str.as_ptr() as *const c_char; + unsafe { + c_ffi::SayHello(c_world); + } + } + + ``` + +4. Create the **BUILD.gn** file to define the dependency of the Rust module on the C module. + + ```GN + import("//build/ohos.gni") + + ohos_shared_library("c_lib") { + sources = [ "lib.c" ] + defines = [ "COMPONENT_IMPLEMENTATION" ] + } + + rust_bindgen("c_lib_bindgen") { + header = "lib.h" + } + + ohos_rust_executable("bindgen_test") { + deps = [ ":c_lib" ] + deps += [ ":c_lib_bindgen" ] + sources = [ "main.rs" ] + bindgen_output = get_target_outputs(":c_lib_bindgen") + inputs = bindgen_output + rustenv = [ "BINDGEN_RS_FILE=" + rebase_path(bindgen_output[0]) ] + crate_root = "main.rs" + } + ``` + +**Verification** + +![bindgen_test](./figures/bindgen_test.png) + + +## Using CXX + +### Calling Rust Interfaces by C++ + +1. In the Rust **lib.rs** file, add the C++ interfaces to be called in **mod ffi**, and add the interfaces in **extern "Rust"** to expose them to C++. + + ```rust + //! #[cxx::bridge] + #[cxx::bridge] + mod ffi{ + #![allow(dead_code)] + #[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] + struct Shared { + z: usize, + } + extern "Rust"{ + fn print_message_in_rust(); + fn r_return_primitive() -> usize; + fn r_return_shared() -> Shared; + fn r_return_rust_string() -> String; + fn r_return_sum(_: usize, _: usize) -> usize; + } + } + + fn print_message_in_rust(){ + println!("Here is a test for cpp call Rust."); + } + fn r_return_shared() -> ffi::Shared { + println!("Here is a message from Rust,test for ffi::Shared:"); + ffi::Shared { z: 1996 } + } + fn r_return_primitive() -> usize { + println!("Here is a message from Rust,test for usize:"); + 1997 + } + fn r_return_rust_string() -> String { + println!("Here is a message from Rust,test for String"); + "Hello World!".to_owned() + } + fn r_return_sum(n1: usize, n2: usize) -> usize { + println!("Here is a message from Rust,test for {} + {} is:",n1 ,n2); + n1 + n2 + } + + ``` + +2. Include **lib.rs.h** (converted from **lib.rs** by the CXX tool) in C++ code. + + ```c++ + #include + #include "build/rust/tests/test_cxx/src/lib.rs.h" + + int main(int argc, const char* argv[]) + { + int a = 2021; + int b = 4; + print_message_in_rust(); + std::cout << r_return_primitive() << std::endl; + std::cout << r_return_shared().z << std::endl; + std::cout << std::string(r_return_rust_string()) << std::endl; + std::cout << r_return_sum(a, b) << std::endl; + return 0; + } + ``` + +3. Create the **BUILD.gn** file. The underlying rust_cxx calls the CXX tool to convert the **lib.rs** file into **lib.rs.h** and **lib.rs.cc**. **ohos_rust_static_ffi** implements compilation of the Rust source code, and **ohos_executable** implements compilation of the C++ code. + + ``` + import("//build/ohos.gni") + import("//build/templates/rust/rust_cxx.gni") + + rust_cxx("test_cxx_exe_gen") { + sources = [ "src/lib.rs" ] + } + + ohos_rust_static_ffi("test_cxx_examp_rust") { + sources = [ "src/lib.rs" ] + deps = [ "//build/rust:cxx_rustdeps" ] + } + + ohos_executable("test_cxx_exe") { + sources = [ "main.cpp" ] + sources += get_target_outputs(":test_cxx_exe_gen") + + include_dirs = [ "${target_gen_dir}" ] + deps = [ + ":test_cxx_examp_rust", + ":test_cxx_exe_gen", + "//build/rust:cxx_cppdeps", + ] + } + ``` + +**Verification** +![cpp_call_rust](./figures/cpp_call_rust.png) + + +### Calling C++ by Rust + +1. Create the header file **client_blobstore.h**. + + ```c++ + #ifndef BUILD_RUST_TESTS_CLIENT_BLOBSTORE_H + #define BUILD_RUST_TESTS_CLIENT_BLOBSTORE_H + #include + #include "third_party/rust/cxx/include/cxx.h" + + namespace nsp_org { + namespace nsp_blobstore { + struct MultiBufs; + struct Metadata_Blob; + + class client_blobstore { + public: + client_blobstore(); + uint64_t put_buf(MultiBufs &buf) const; + void add_tag(uint64_t blobid, rust::Str add_tag) const; + Metadata_Blob get_metadata(uint64_t blobid) const; + + private: + class impl; + std::shared_ptr impl; + }; + + std::unique_ptr blobstore_client_new(); + } // namespace nsp_blobstore + } // namespace nsp_org + #endif + ``` + +2. Create the **client_blobstore.cpp** file. + + ```c++ + #include + #include + #include + #include + #include + #include "src/main.rs.h" + #include "build/rust/tests/test_cxx_rust/include/client_blobstore.h" + + namespace nsp_org { + namespace nsp_blobstore { + // Toy implementation of an in-memory nsp_blobstore. + // + // The real implementation of client_blobstore could be a large complex C++ + // library. + class client_blobstore::impl { + friend client_blobstore; + using Blob = struct { + std::string data; + std::set tags; + }; + std::unordered_map blobs; + }; + + client_blobstore::client_blobstore() : impl(new class client_blobstore::impl) {} + + // Upload a new blob and return a blobid that serves as a handle to the blob. + uint64_t client_blobstore::put_buf(MultiBufs &buf) const + { + std::string contents; + + // Traverse the caller's res_chunk iterator. + // + // In reality there might be sophisticated batching of chunks and/or parallel + // upload implemented by the nsp_blobstore's C++ client. + while (true) { + auto res_chunk = next_chunk(buf); + if (res_chunk.size() == 0) { + break; + } + contents.append(reinterpret_cast(res_chunk.data()), res_chunk.size()); + } + + // Insert into map and provide caller the handle. + auto res = std::hash {} (contents); + impl->blobs[res] = {std::move(contents), {}}; + return res; + } + + // Add add_tag to an existing blob. + void client_blobstore::add_tag(uint64_t blobid, rust::Str add_tag) const + { + impl->blobs[blobid].tags.emplace(add_tag); + } + + // Retrieve get_metadata about a blob. + Metadata_Blob client_blobstore::get_metadata(uint64_t blobid) const + { + Metadata_Blob get_metadata {}; + auto blob = impl->blobs.find(blobid); + if (blob != impl->blobs.end()) { + get_metadata.size = blob->second.data.size(); + std::for_each(blob->second.tags.cbegin(), blob->second.tags.cend(), + [&](auto &t) { get_metadata.tags.emplace_back(t); }); + } + return get_metadata; + } + + std::unique_ptr blobstore_client_new() + { + return std::make_unique(); + } + } // namespace nsp_blobstore + } // namespace nsp_org + + ``` + +3. In **ffi** of the **main.rs** file, use the macro **includes!** to import the header file **client_blobstore.h**. Then, the **main()** function of Rust can call the C++ interfaces in ffi mode. + + ```rust + //! test_cxx_rust + #[cxx::bridge(namespace = "nsp_org::nsp_blobstore")] + mod ffi { + // Shared structs with fields visible to both languages. + struct Metadata_Blob { + size: usize, + tags: Vec, + } + + // Rust types and signatures exposed to C++. + extern "Rust" { + type MultiBufs; + + fn next_chunk(buf: &mut MultiBufs) -> &[u8]; + } + + // C++ types and signatures exposed to Rust. + unsafe extern "C++" { + include!("build/rust/tests/test_cxx_rust/include/client_blobstore.h"); + + type client_blobstore; + + fn blobstore_client_new() -> UniquePtr; + fn put_buf(&self, parts: &mut MultiBufs) -> u64; + fn add_tag(&self, blobid: u64, add_tag: &str); + fn get_metadata(&self, blobid: u64) -> Metadata_Blob; + } + } + + // An iterator over contiguous chunks of a discontiguous file object. + // + // Toy implementation uses a Vec> but in reality this might be iterating + // over some more complex Rust data structure like a rope, or maybe loading + // chunks lazily from somewhere. + /// pub struct MultiBufs + pub struct MultiBufs { + chunks: Vec>, + pos: usize, + } + /// pub fn next_chunk + pub fn next_chunk(buf: &mut MultiBufs) -> &[u8] { + let next = buf.chunks.get(buf.pos); + buf.pos += 1; + next.map_or(&[], Vec::as_slice) + } + + /// fn main() + fn main() { + let client = ffi::blobstore_client_new(); + + // Upload a blob. + let chunks = vec![b"fearless".to_vec(), b"concurrency".to_vec()]; + let mut buf = MultiBufs { chunks, pos: 0 }; + let blobid = client.put_buf(&mut buf); + println!("This is a test for Rust call cpp:"); + println!("blobid = {}", blobid); + + // Add a add_tag. + client.add_tag(blobid, "rust"); + + // Read back the tags. + let get_metadata = client.get_metadata(blobid); + println!("tags = {:?}", get_metadata.tags); + } + ``` + +4. Create the **BUILD.gn** file. Use CXX to convert **main.rs** into **lib.rs.h** and **lib.rs.cc**, which are used as the source code of **test_cxx_rust_staticlib**. Compile Rust **main.rs**, and add the dependency **test_cxx_rust_staticlib**. + + ``` + import("//build/ohos.gni") + + rust_cxx("test_cxx_rust_gen") { + sources = [ "src/main.rs" ] + } + + ohos_static_library("test_cxx_rust_staticlib") { + sources = [ "src/client_blobstore.cpp" ] + sources += get_target_outputs(":test_cxx_rust_gen") + include_dirs = [ + "${target_gen_dir}", + "//third_party/rust/cxx/v1/crate/include", + "include", + ] + deps = [ + ":test_cxx_rust_gen", + "//build/rust:cxx_cppdeps", + ] + } + + ohos_rust_executable("test_cxx_rust") { + sources = [ "src/main.rs" ] + deps = [ + ":test_cxx_rust_staticlib", + "//build/rust:cxx_rustdeps", + ] + } + ``` + +**Verification** +![rust_call_cpp](./figures/rust_call_cpp.png) diff --git a/en/device-dev/subsystems/subsys-build-cargo2gn-guide.md b/en/device-dev/subsystems/subsys-build-cargo2gn-guide.md new file mode 100644 index 0000000000000000000000000000000000000000..fcf8a04804809485cbaed0cee49ca467272a4caf --- /dev/null +++ b/en/device-dev/subsystems/subsys-build-cargo2gn-guide.md @@ -0,0 +1,95 @@ +# Using Cargo2gn +## Overview + +Rust uses Cargo as its build system and package manager. **Cargo.toml** is the manifest file of Cargo. It contains metadata such as the name, version, and dependencies for crates (packages) in Rust. + +When used in OpenHarmony, **Cargo.toml** needs to be converted into **BUILD.gn** rules. cargo2gn is the tool that can achieve this purpose. It can convert one or more Rust libraries. + +## Converting a Single Library +1. Go to the directory of the rust library to be converted, for example, **bindgen**. + + ``` + cd openharmony/third_party/rust/bindgen + ``` + +2. Create the configuration file **cargo2gn.json**. + + ``` + { + "copy-out": true, + "run": true, + "add-workspace": true, + "cargo-bin": "/mnt/xxx/openharmony/prebuilts/rustc/linux-x86_64/current/bin" + } + ``` + +3. Run the following command to perform the conversion: + + ``` + python /mnt/xxx/openharmony/build/scripts/cargo2gn.py --config cargo2gn.json + ``` + + The conversion result is as follows: + + ``` + import("//build/templates/rust/ohos_cargo_crate.gni") + + ohos_cargo_crate("lib") { + crate_name = "bindgen" + crate_type = "rlib" + crate_root = "./lib.rs" + + sources = ["./lib.rs"] + edition = "2018" + cargo_pkg_version = "0.64.0" + cargo_pkg_authors = "Jyun-Yan You , Emilio Cobos Álvarez , Nick Fitzgerald , The Servo project developers" + cargo_pkg_name = "bindgen" + cargo_pkg_description = "Automatically generates Rust FFI bindings to C and C++ libraries." + deps = [ + "//third_party/rust/bitflags:lib", + "//third_party/rust/cexpr:lib", + "//third_party/rust/clang-sys:lib", + "//third_party/rust/lazy_static:lib", + "//third_party/rust/lazycell:lib", + "//third_party/rust/log:lib", + "//third_party/rust/peeking_take_while:lib", + "//third_party/rust/proc-macro2:lib", + "//third_party/rust/quote:lib", + "//third_party/rust/regex:lib", + "//third_party/rust/rustc-hash:lib", + "//third_party/rust/shlex:lib", + "//third_party/rust/syn:lib", + "//third_party/rust/which:lib", + ] + features = [ + "default", + "log", + "logging", + "static", + "which", + "which-rustfmt", + ] + build_root = "build.rs" + build_sources = ["build.rs"] + build_script_outputs = ["host-target.txt"] + } + ``` + + + +## Converting Multiple Libraries in Batches +1. Go to the **rust** directory. + + ``` + cd openharmony/third_party/rust + ``` +2. Add all the rust libraries to be converted to [workspace] in **Cargo.toml** in the **rust** directory. + + ``` + [workspace] + members = [ + "aho-corasick", + "memchr", + ] + ``` +3. Perform steps 2 and 3 in section "Converting a Single Library". diff --git a/en/device-dev/subsystems/subsys-build-module.md b/en/device-dev/subsystems/subsys-build-module.md index bc5ec28957f446b4b4ca8b221580467083d2963f..49f58da2bdc3b0b8577b178eb0b418cec59eb3d3 100644 --- a/en/device-dev/subsystems/subsys-build-module.md +++ b/en/device-dev/subsystems/subsys-build-module.md @@ -23,6 +23,17 @@ ohos_app_scope ohos_js_assets ohos_resources +# Rust templates +ohos_rust_executable +ohos_rust_shared_library +ohos_rust_static_library +ohos_rust_proc_macro +ohos_rust_shared_ffi +ohos_rust_static_ffi +ohos_rust_cargo_crate +ohos_rust_systemtest +ohos_rust_unittest + # Other templates # Configuration file ohos_prebuilt_etc @@ -33,7 +44,7 @@ ohos_sa_profile You are advised to use the OpenHarmony customized templates. -### C/C++ Template Examples +### C/C++ Template Example The .gni file corresponding to the templates starting with **ohos** is located in **openharmony/build/templates/cxx/cxx.gni**. @@ -69,7 +80,7 @@ ohos_shared_library("helloworld") { cfi = [boolean] # Whether to enable the control-flow integrity (CFI) check. cfi_cross_dso = [boolean] # Whether to enable the cross-DSO CFI check. integer_overflow = [boolean] # Whether to enable the integer overflow check. - boundary_sanitize = [boolean] # Whether to enable the bounds check. + boundary_sanitize = [boolean] # Whether to enable the boundary check. ubsan = [boolean] # Whether to enable some Undefined Behavior Sanitizer (UBSAN) options. all_ubsan = [boolean] # Whether to enable all UBSAN options. ... @@ -91,12 +102,12 @@ ohos_shared_library("helloworld") { } ``` -**ohos_static_library** example: +**ohos_static_library** example ```shell import("//build/ohos.gni") ohos_static_library("helloworld") { - sources = ["file"] # Source code in .c format. + sources = ["file"] # .c files. include_dirs = ["dir"] # Directories to be included. configs = [] # Configuration. deps = [] # Define dependent modules that belong to the same component. @@ -114,11 +125,11 @@ ohos_static_library("helloworld") { # Sanitizer configuration. Each item is optional, and set to false or left unspecified by default. sanitize = { # Sanitizer settings - cfi = [boolean] # Whether to enable the CFI check. + cfi = [boolean] # Whether to enable the control-flow integrity (CFI) check. cfi_cross_dso = [boolean] # Whether to enable the cross-DSO CFI check. integer_overflow = [boolean] # Whether to enable the integer overflow check. - boundary_sanitize = [boolean] # Whether to enable the bounds check. - ubsan = [boolean] # Whether to enable some UBSAN options. + boundary_sanitize = [boolean] # Whether to enable the boundary check. + ubsan = [boolean] # Whether to enable some Undefined Behavior Sanitizer (UBSAN) options. all_ubsan = [boolean] # Whether to enable all UBSAN options. ... @@ -139,7 +150,7 @@ ohos_static_library("helloworld") { ```shell import("//build/ohos.gni") ohos_executable("helloworld") { - configs = [] # Configuration. + configs = [] # Configuration. part_name = [string] # Component name. subsystem_name = [string] # Subsystem name. deps = [] # Define dependent modules that belong to the same component. @@ -153,10 +164,10 @@ ohos_executable("helloworld") { # Sanitizer configuration. Each item is optional, and set to false or left unspecified by default. sanitize = { # Sanitizer settings - cfi = [boolean] # Whether to enable the CFI check. + cfi = [boolean] # Whether to enable the control-flow integrity (CFI) check. cfi_cross_dso = [boolean] # Whether to enable the cross-DSO CFI check. integer_overflow = [boolean] # Whether to enable the integer overflow check. - boundary_sanitize = [boolean] # Whether to enable the bounds check. + boundary_sanitize = [boolean] # Whether to enable the boundary check. ubsan = [boolean] # Whether to enable some Undefined Behavior Sanitizer (UBSAN) options. all_ubsan = [boolean] # Whether to enable all UBSAN options. ... @@ -181,32 +192,32 @@ ohos_executable("helloworld") { } ``` -**ohos_source_set** example: +**ohos_source_set** example ```shell import("//build/ohos.gni") ohos_source_set("helloworld") { - sources = ["file"] # Source code in .c format. - include_dirs = [] # Directories to be included. - configs = [] # Configuration. - public = [] # Header files. + sources = ["file"] # .c files. + include_dirs = [] # Directories to be included. + configs = [] # Configuration. + public = [] # .h header files. defines = [] public_configs = [] - part_name = [string] # Component name. - subsystem_name = [string] # Subsystem name. - deps = [] # Define dependent modules that belong to the same component. + part_name = [string] # Component name. + subsystem_name = [string] # Subsystem name. + deps = [] # Define dependent modules that belong to the same component. - external_deps = [ # Define dependent modules that belong to different components. - "part_name:module_name", # The value is in the Component_name:Module_name format. - ] # The dependent modules must be declared in inner_kits by the dependent component. + external_deps = [ # Define dependent modules that belong to different components. + "part_name:module_name", # The value is in the Component_name:Module_name format. + ] # The dependent modules must be declared in inner_kits by the dependent component. # Sanitizer configuration. Each item is optional, and set to false or left unspecified by default. sanitize = { # Sanitizer settings - cfi = [boolean] # Whether to enable the CFI check. + cfi = [boolean] # Whether to enable the control-flow integrity (CFI) check. cfi_cross_dso = [boolean] # Whether to enable the cross-DSO CFI check. integer_overflow = [boolean] # Whether to enable the integer overflow check. - boundary_sanitize = [boolean] # Whether to enable the bounds check. + boundary_sanitize = [boolean] # Whether to enable the boundary check. ubsan = [boolean] # Whether to enable some Undefined Behavior Sanitizer (UBSAN) options. all_ubsan = [boolean] # Whether to enable all UBSAN options. ... @@ -226,72 +237,71 @@ ohos_source_set("helloworld") { } ``` -> **NOTE** -> -> - Only **sources** and **part_name** are mandatory. -> - For details about the Sanitizer configuration, see [Using Sanitizer](subsys-build-reference.md#using-sanitizer). +**NOTE**
+ - Only **sources** and **part_name** are mandatory. + - For details about the Sanitizer configuration, see [Using Sanitizer](subsys-build-reference.md#using-sanitizer). -### Prebuilt Template Examples +### Prebuilt Template Example The .gni file of the prebuilt templates is located in **openharmony/build/templates/cxx/prebuilt.gni**. -**ohos_prebuilt_executable** example: +**ohos_prebuilt_executable** example ```shell import("//build/ohos.gni") ohos_prebuilt_executable("helloworld") { - sources = ["file"] # Source. + sources = ["file"] # Source. output = [] - install_enable = [boolean] + install_enable = [boolean] - deps = [] # Define dependent modules that belong to the same component. + deps = [] # Define dependent modules that belong to the same component. public_configs = [] - subsystem_name = [string] # Subsystem name. - part_name = [string] # Component name. + subsystem_name = [string] # Subsystem name. + part_name = [string] # Component name. testonly = [boolean] visibility = [] install_images = [] - module_install_dir = [] # Module installation directory, starting from system/ or vendor/. - relative_install_dir = [] # Relative module installation directory (relative to system/etc). If module_install_dir is configured, the parameter does not take effect. + module_install_dir = [] # Module installation directory, starting from system/ or vendor/. + relative_install_dir = [] # Relative module installation directory (relative to system/etc). If module_install_dir is configured, the parameter does not take effect. symlink_target_name = [] - license_file = [] # A .txt file. + license_file = [] # A .txt file. license_as_sources = [] } ``` -**ohos_prebuilt_shared_library** example: +**ohos_prebuilt_shared_library** example ```shell import("//build/ohos.gni") ohos_prebuilt_shared_library("helloworld") { - sources = ["file"] # .so files. + sources = ["file"] # .so files. output = [] install_enable = [boolean] - deps = [] # Define dependent modules that belong to the same component. + deps = [] # Define dependent modules that belong to the same component. public_configs = [] - subsystem_name = [string] # Subsystem name. - part_name = [string] # Component name. + subsystem_name = [string] # Subsystem name. + part_name = [string] # Component name. testonly = [boolean] visibility = [] install_images = [] - module_install_dir = [] # Module installation directory, starting from system/ or vendor/. - relative_install_dir = [] # Relative module installation directory (relative to system/etc). If module_install_dir is configured, the parameter does not take effect. + module_install_dir = [] # Module installation directory, starting from system/ or vendor/. + relative_install_dir = [] # Relative module installation directory (relative to system/etc). If module_install_dir is configured, the parameter does not take effect. symlink_target_name = [string] - license_file = [string] # A .txt file. + license_file = [string] # A .txt file. license_as_sources = [] } ``` -**ohos_prebuilt_static_library** example: +**ohos_prebuilt_static_library** example ```shell import("//build/ohos.gni") @@ -312,13 +322,15 @@ ohos_prebuilt_static_library("helloworld") { } ``` ->**NOTE**
Only **sources** and **part_name** are mandatory. +![icon-note.gif](public_sys-resources/icon-note.gif) **NOTE**: Only **sources** and **part_name** are mandatory. ### HAP Templates -For details, see [HAP Build Guide](subsys-build-gn-hap-compilation-guide.md). +For details about the HAP templates, see [HAP Build Guide](subsys-build-gn-hap-compilation-guide.md). +### Rust Templates +For details about the Rust templates, see [Rust Module Configuration Rules and Guide](subsys-build-rust-compilation.md). ### Other Templates @@ -329,14 +341,14 @@ import("//build/ohos.gni") ohos_prebuilt_etc("helloworld") { # The most common attributes of the ohos_prebuilt_etc template. sources = ["file"] - module_install_dir = [] # Module installation directory, starting from system/ or vendor/. - subsystem_name = [string] # Subsystem name. - part_name = [string] # (Mandatory) Component name. + module_install_dir = [] # Module installation directory, starting from system/ or vendor/. + subsystem_name = [string] # Subsystem name. + part_name = [string] # (Mandatory) Component name. install_images = [] - relative_install_dir = [] # Relative module installation directory (relative to system/etc). If module_install_dir is configured, the parameter does not take effect. + relative_install_dir = [] # Relative module installation directory (relative to system/etc). If module_install_dir is configured, the parameter does not take effect. # Uncommon attributes of the ohos_prebuilt_etc template: - deps = [] # Define dependent modules that belong to the same component. + deps = [] # Define dependent modules that belong to the same component. testonly = [boolean] visibility = [] public_configs = [] @@ -357,7 +369,7 @@ ohos_sa_profile("helloworld") { } ``` -> **NOTE**
Only **sources** and **part_name** are mandatory. +![icon-note.gif](public_sys-resources/icon-note.gif) **NOTE**: Only **sources** and **part_name** are mandatory. ## Adding and Building a Module @@ -379,47 +391,49 @@ The following figure shows the logic for adding a module. Generally, you need to ```shell { - "name": "@ohos/, # HPM component name, in the "@Organization/Component_name" format. - "description": "xxxxxxxxxxxxxxxxxxx", # Description of the component functions. - "version": "3.1", # Version, which must be the same as the version of OpenHarmony. - "license": "MIT", # Component license. - "publishAs": "code-segment", # HPM package release mode. The default value is code-segment. + "name": "@ohos/, # HPM component name, in the "@Organization/Component_name" format. + "description": "xxxxxxxxxxxxxxxxxxx", # Description of the component functions. + "version": "3.1", # Version, which must be the same as the version of OpenHarmony. + "license": "MIT", # Component license. + "publishAs": "code-segment", # HPM package release mode. The default value is code-segment. "segment": { "destPath": "third_party/nghttp2" - }, # Code restoration path (source code path) set when publishAs is code-segment. - "dirs": {}, # Directory structure of the HPM package. This field is mandatory and can be left empty. - "scripts": {}, # Scripts to be executed. This field is mandatory and can be left empty. + }, # Code restoration path (source code path) set when publishAs is code-segment. + "dirs": {}, # Directory structure of the HPM package. This field is mandatory and can be left empty. + "scripts": {}, # Scripts to be executed. This field is mandatory and can be left empty. "licensePath": "COPYING", "readmePath": { "en": "README.rst" }, - "component": { # Component attributes. - "name": "", # Component name. - "subsystem": "", # Subsystem to which the component belongs. - "syscap": [], # System capabilities provided by the component for applications. - "features": [], # List of configurable features of the component. Generally, this parameter corresponds to sub_component in build. - "adapted_system_type": [], # Types of adapted systems. The value can be mini, small, standard, or their combinations. - "rom": "xxxKB" # ROM baseline. If there is no baseline, enter the current value. - "ram": "xxxKB", # RAM baseline. If there is no baseline, enter the current value. + "component": { # Component attributes. + "name": "", # Component name. + "subsystem": "", # Subsystem to which the component belongs. + "syscap": [], # System capabilities provided by the component for applications. + "features": [], # List of configurable features of the component. Generally, this parameter corresponds to sub_component in build. + "adapted_system_type": [], # Types of adapted systems. The value can be mini, small, standard, or their combinations. + "rom": "xxxKB" # ROM baseline. If there is no baseline, enter the current value. + "ram": "xxxKB", # RAM baseline. If there is no baseline, enter the current value. "deps": { - "components": [ # Other components on which this component depends. - "third_party": [ # Third-party open-source software on which this component depends. + "components": [ # Other components on which this component depends. + "third_party": [ # Third-party open-source software on which this component depends. }, - "build": { # Build-related configuration. + "build": { # Build-related configuration. "sub_component": [ - "//foundation/arkui/napi:napi_packages", # Existing module 1. - "//foundation/arkui/napi:napi_packages_ndk"# Existing module 2. - "//foundation/arkui/napi:new" # Module to add. - ], # Component build entry. Configure the module here. - "inner_kits": [], # APIs between components. - "test": [] # Entry for building the component's test cases. + "//foundation/arkui/napi:napi_packages", # Existing module 1. + "//foundation/arkui/napi:napi_packages_ndk" # Existing module 2. + "//foundation/arkui/napi:new" # Module to add. + ], # Component build entry. Configure the module here. + "inner_kits": [], # APIs between components. + "test": [] # Entry for building the component's test cases. } } } ``` - > **NOTE**
The **bundle.json** file must be in the folder of the corresponding subsystem. + > ![icon-note.gif](public_sys-resources/icon-note.gif) **NOTE** + > + > The **bundle.json** file must be in the folder of the corresponding subsystem. 3. Start the build and check whether a .so file or binary file is generated. @@ -429,16 +443,16 @@ The following figure shows the logic for adding a module. Generally, you need to 2. Create a **bundle.json** file in the folder of the corresponding subsystem. -3. Add the new component to the end of existing components in **vendor/{product_company}/{product_name}/config.json**. +3. Add the new component to the end of existing components in **vendor/{product_company}/{product-name}/config.json**. ```shell "subsystems": [ { "subsystem": "Subsystem to which the component belongs", "components": [ - {"component": "Component 1 name", "features":[]}, # Existing component 1 in the subsystem - { "component": "Component 2 name", "features":[] }, # Existing component 2 in the subsystem - {"component": "New component name", "features":[]} # New component in the subsystem + {"component": "Component 1 name", "features":[]}, # Existing component 1 in the subsystem + { "component": "Component 2 name", "features":[] }, # Existing component 2 in the subsystem + {"component": "New component name", "features":[]} # New component in the subsystem ] }, . @@ -476,7 +490,7 @@ The following figure shows the logic for adding a module. Generally, you need to The **subsystem_config.json** file defines the subsystems and their directories. When adding a subsystem, specify **path** and **name** for the subsystem. -4. If **product_name** in the **vendor/{product_company}/{product_name}** directory is **hispark_taurus_standard**, add the new component information to the end of existing components in the **config.json** file. +4. If **product-name** in the **vendor/{product_company}/{product-name}** directory is **hispark_taurus_standard**, add the new component information to the end of existing components in the **config.json** file. ```shell "subsystems": [ @@ -517,19 +531,19 @@ The following figure shows the logic for adding a module. Generally, you need to You can start the build by using the [CLI or hb tool](subsys-build-all.md#build-commands). The following uses the CLI as an example: -You can run the **--build-target** *Module_name* command to build a module separately. + You can run the **--build-target** *Module_name* command to build a module separately. ```shell ./build.sh --build-target Module_name ``` -You can also build a product. For example, to build hispark_taurus_standard, run the following command: + You can also build a product. For example, to build hispark_taurus_standard, run the following command: ```shell ./build.sh --product-name hispark_taurus_standard --build-target Module_name --ccache ``` -You can also build the component to which the module belongs. + You can also build the component to which the module belongs. ```shell ./build.sh --product-name hispark_taurus_standard --build-target musl --build-target Module_name --ccache diff --git a/en/device-dev/subsystems/subsys-build-rust-compilation.md b/en/device-dev/subsystems/subsys-build-rust-compilation.md new file mode 100644 index 0000000000000000000000000000000000000000..b02f52d3d27110bc29f29a3fc245e92af78ed0db --- /dev/null +++ b/en/device-dev/subsystems/subsys-build-rust-compilation.md @@ -0,0 +1,369 @@ +# Rust Module Configuration Rules and Guide + +## Introduction + +Rust 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. + +To 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. + +To 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. + +### Basic Concepts + +| Term | Description | +| ----- | ------------------------------------------------------------ | +| Cargo | Cargo is an official build tool used by Rust. It allows Rust projects to declare dependencies and ensures reproducible builds.| +| crate | Crate is a unit that can be independently compiled. | +| 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.| + + + +## Configure Rules +OpenHarmony provides a variety of GN templates for compiling Rust executables, dynamic libraries, and static libraries. The following table describes the templates. + +| GN Template | Description | Output | +| ------------------------ | ----------------- | ----------------------------------------------- | +| ohos_rust_executable | Rust executable file. | Rust executable file, without the file name extension. | +| ohos_rust_shared_library | Rust dynamic library. | Rust dylib dynamic library, with the default file name extension **.dylib.so**. | +| ohos_rust_static_library | Rust static library. | Rust rlib static library, with the default file name extension **.rlib**. | +| ohos_rust_proc_macro | Rust proc_macro library. | Rust proc_macro library, with the default file name extension **.so**. | +| 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**.| +| 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**. | +| ohos_rust_cargo_crate | Third-party Cargo crate.| Third-party Rust crates, which support rlib, dylib, and bin. | +| ohos_rust_systemtest | Rust system test cases. | Executable system test cases for Rust, without the file name extension. | +| ohos_rust_unittest | Rust unit test cases. | Executable unit test cases for Rust, without the file name extension. | + + + +## Configuration Guide +The 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. +### Configuring a Rust Static Library +The 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. + +The procedure is as follows: + +1. Create **build/rust/tests/test_rlib_crate/src/simple_printer.rs**. + + ```rust + //! simple_printer + + /// struct RustLogMessage + + pub struct RustLogMessage { + /// i32: id + pub id: i32, + /// String: msg + pub msg: String, + } + + /// function rust_log_rlib + pub fn rust_log_rlib(msg: RustLogMessage) { + println!("id:{} message:{:?}", msg.id, msg.msg) + } + ``` + +2. Create **build/rust/tests/test_rlib_crate/src/main.rs**. + + ```rust + //! rlib_crate example for Rust. + + extern crate simple_printer_rlib; + + use simple_printer_rlib::rust_log_rlib; + use simple_printer_rlib::RustLogMessage; + + fn main() { + let msg: RustLogMessage = RustLogMessage { + id: 0, + msg: "string in rlib crate".to_string(), + }; + rust_log_rlib(msg); + } + ``` + +3. Configure the GN build script **build/rust/tests/test_rlib_crate/BUILD.gn**. + + ``` + import("//build/ohos.gni") + + ohos_rust_executable("test_rlib_crate") { + sources = [ "src/main.rs" ] + deps = [ ":simple_printer_rlib" ] + } + + ohos_rust_static_library("simple_printer_rlib") { + sources = [ "src/simple_printer.rs" ] + crate_name = "simple_printer_rlib" + crate_type = "rlib" + features = [ "std" ] + } + ``` + +4. Run the **BUILD.gn** to generate the build targets. + + ![test_rlib_crate](./figures/test_rlib_crate.png) + +### Configuring a Third-Party Library +The following example shows how to use the **ohos_rust_executable** and **ohos_rust_cargo_crate** templates to build a third-party static rlib library whose code contains a prebuilt file **build.rs**. + +The procedure is as follows: + +1. Create **build/rust/tests/test_rlib_cargo_crate/crate/src/lib.rs**. + + ```rust + include!(concat!(env!("OUT_DIR"), "/generated/generated.rs")); + + pub fn say_hello_from_crate() { + assert_eq!(run_some_generated_code(), 45); + #[cfg(is_new_rustc)] + println!("Is new rustc"); + #[cfg(is_old_rustc)] + println!("Is old rustc"); + #[cfg(is_ohos)] + println!("Is ohos"); + #[cfg(is_mac)] + println!("Is darwin"); + #[cfg(has_feature_a)] + println!("Has feature_a"); + #[cfg(not(has_feature_a))] + panic!("Wasn't passed feature_a"); + #[cfg(not(has_feature_b))] + #[cfg(test_a_and_b)] + panic!("feature_b wasn't passed"); + #[cfg(has_feature_b)] + #[cfg(not(test_a_and_b))] + panic!("feature_b was passed"); + } + + #[cfg(test)] + mod tests { + /// Test features are passed through from BUILD.gn correctly. This test is the target configuration. + #[test] + #[cfg(test_a_and_b)] + fn test_features_passed_target1() { + #[cfg(not(has_feature_a))] + panic!("feature a was not passed"); + #[cfg(not(has_feature_b))] + panic!("feature b was not passed"); + } + + #[test] + fn test_generated_code_works() { + assert_eq!(crate::run_some_generated_code(), 45); + } + } + ``` + +2. Create **build/rust/tests/test_rlib_cargo_crate/crate/src/main.rs**. + + ```rust + pub fn main() { + test_rlib_crate::say_hello_from_crate(); + } + ``` + +3. Create **build/rust/tests/test_rlib_cargo_crate/crate/build.rs**. + + ```rust + use std::env; + use std::path::Path; + use std::io::Write; + use std::process::Command; + use std::str::{self, FromStr}; + + fn main() { + println!("cargo:rustc-cfg=build_script_ran"); + let my_minor = match rustc_minor_version() { + Some(my_minor) => my_minor, + None => return, + }; + + if my_minor >= 34 { + println!("cargo:rustc-cfg=is_new_rustc"); + } else { + println!("cargo:rustc-cfg=is_old_rustc"); + } + + let target = env::var("TARGET").unwrap(); + + if target.contains("ohos") { + println!("cargo:rustc-cfg=is_ohos"); + } + if target.contains("darwin") { + println!("cargo:rustc-cfg=is_mac"); + } + + let feature_a = env::var_os("CARGO_FEATURE_MY_FEATURE_A").is_some(); + if feature_a { + println!("cargo:rustc-cfg=has_feature_a"); + } + let feature_b = env::var_os("CARGO_FEATURE_MY_FEATURE_B").is_some(); + if feature_b { + println!("cargo:rustc-cfg=has_feature_b"); + } + + // Tests used to verify whether Cargo features are enabled. + assert!(Path::new("build.rs").exists()); + assert!(Path::new(&env::var_os("CARGO_MANIFEST_DIR").unwrap()).join("build.rs").exists()); + assert!(Path::new(&env::var_os("OUT_DIR").unwrap()).exists()); + + // Ensure that the following env var is set. + env::var_os("CARGO_CFG_TARGET_ARCH").unwrap(); + + generate_some_code().unwrap(); + } + + fn generate_some_code() -> std::io::Result<()> { + let test_output_dir = Path::new(&env::var_os("OUT_DIR").unwrap()).join("generated"); + let _ = std::fs::create_dir_all(&test_output_dir); + // Test that environment variables from .gn files are passed to build scripts. + let preferred_number = env::var("ENV_VAR_FOR_BUILD_SCRIPT").unwrap(); + let mut file = std::fs::File::create(test_output_dir.join("generated.rs"))?; + write!(file, "fn run_some_generated_code() -> u32 {{ {} }}", preferred_number)?; + Ok(()) + } + + fn rustc_minor_version() -> Option { + let rustc_bin = match env::var_os("RUSTC") { + Some(rustc_bin) => rustc_bin, + None => return None, + }; + + let output = match Command::new(rustc_bin).arg("--version").output() { + Ok(output) => output, + Err(_) => return None, + }; + + let rustc_version = match str::from_utf8(&output.stdout) { + Ok(rustc_version) => rustc_version, + Err(_) => return None, + }; + + let mut pieces = rustc_version.split('.'); + if pieces.next() != Some("rustc 1") { + return None; + } + + let next_var = match pieces.next() { + Some(next_var) => next_var, + None => return None, + }; + + u32::from_str(next_var).ok() + } + ``` + +4. Configure the GN build script **build/rust/tests/test_rlib_cargo_crate/BUILD.gn**. + + ``` + import("//build/templates/rust/ohos_cargo_crate.gni") + + ohos_cargo_crate("target") { + crate_name = "test_rlib_crate" + crate_root = "crate/src/lib.rs" + sources = [ "crate/src/lib.rs" ] + + #To generate the build_script binary + build_root = "crate/build.rs" + build_sources = [ "crate/build.rs" ] + build_script_outputs = [ "generated/generated.rs" ] + + features = [ + "my-feature_a", + "my-feature_b", + "std", + ] + rustflags = [ + "--cfg", + "test_a_and_b", + ] + rustenv = [ "ENV_VAR_FOR_BUILD_SCRIPT=45" ] + } + + # Exists to test the case that a single crate has both a library and a binary + ohos_cargo_crate("test_rlib_crate_associated_bin") { + crate_root = "crate/src/main.rs" + crate_type = "bin" + sources = [ "crate/src/main.rs" ] + + #To generate the build_script binary + build_root = "crate/build.rs" + build_sources = [ "crate/build.rs" ] + features = [ + "my-feature_a", + "my-feature_b", + "std", + ] + rustenv = [ "ENV_VAR_FOR_BUILD_SCRIPT=45" ] + deps = [ ":target" ] + } + ``` + +5. Run the **BUILD.gn** to generate the build target. + + ![test_rlib_cargo_crate](./figures/test_rlib_cargo_crate.png) + +### Other Configuration Examples +You can find the Rust module configuration examples in the **build/rust/tests** directory. +| Directory | Description | +| -------------------------------------------- | ------------------------------------------------------------ | +| 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. | +| build/rust/tests/test_static_link | Tests the static linking of an executable file to a standard library. | +| build/rust/tests/test_dylib_crate | Tests the build of a dynamic library and dynamic linking. | +| build/rust/tests/test_rlib_crate | Tests the build of a static library and static linking. | +| 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. | +| build/rust/tests/test_cdylib_crate | Tests the generation of Rust FFI bindings to a C/C++ dynamic library. | +| build/rust/tests/test_staticlib_crate | Tests the generation of Rust FFI bindings to a C/C++ static library. | +| build/rust/tests/test_rust_ut | Tests the Rust code unit test template. | +| build/rust/tests/test_rust_st | Tests the Rust code system test template. | +| build/rust/tests/test_bin_cargo_crate | Tests the build and running of a Rust third-party executable file. The third-party code contains the **build.rs**. | +| build/rust/tests/test_rlib_cargo_crate | Tests the build of a Rust third-party static library and static linking. The third-party code contains the **build.rs**. | +| build/rust/tests/test_proc_macro_cargo_crate | Tests the build of Rust third-party process macros and linking. The third-party code contains the **build.rs**. | + +## Reference + +### Feature Examples + +#### Linking a C/C++ library in Rust Source Code +By 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**. + +If 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. + +```rust +#[link(name = "hilog")] +``` +#### Using externs +If a module depends on the binary rlib library, you can use the **externs** attribute. +``` +executable("foo") { + sources = [ "main.rs" ] + externs = [{ # Convert it to `--extern bar=path/to/bar.rlib` during the compilation. + crate_name = "bar" + path = "path/to/bar.rlib" + }] +} +``` +### Lint Rules +The 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). + +When configuring the Rust module, you can specify the lint level in **rustc_lints** or **clippy_lints**. +If **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. + +#### Levels of rustc Lints and Clippy Lints +| **Lint Type**| **Module Attribute**| **Lint Level**| **Lint Level Flag**| **Lint Content** | +| ------------- | ------------ | ------------- | ----------------- | ------------------------------------------------------------ | +| rustc lints | rustc_lints | openharmony | RustOhosLints | "-A deprecated", "-D missing-docs", "-D warnings" | +| rustc lints | rustc_lints | vendor | RustcVendorLints | "-A deprecated", "-D warnings" | +| rustc lints | rustc_lints | none | allowAllLints | "-cap-lints allow" | +| Clippy lints | clippy_lints | openharmony | ClippyOhosLints | "-A clippy::type-complexity", "-A clippy::unnecessary-wraps", "-A clippy::unusual-byte-groupings", "-A clippy::upper-case-acronyms" | +| Clippy lints | clippy_lints | vendor | ClippyVendorLints | "-A clippy::complexity", "-A Clippy::perf", "-A clippy::style" | +| Clippy lints | clippy_lints | none | allowAllLints | "--cap-lints allow" | + +#### Mapping Between Code Paths and Lint Levels +| Path | Lint Level | +| ---------- | ----------- | +| thirdparty | none | +| prebuilts | none | +| vendor | vendor | +| device | vendor | +| others | openharmony |