diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index ab3f9c3..19b893a 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -14,16 +14,30 @@ jobs: strategy: fail-fast: true matrix: - os: [ubuntu-latest, windows-11, macos-ventura, [ubuntu-22.04, ARM64]] - runs-on: [self-hosted, "${{ matrix.os }}"] + os-arch: + - { os: "ubuntu-latest", arch: "X64" } + - { os: "windows-11", arch: "X64" } + - { os: "macos-ventura", arch: "ARM64" } + - { os: "ubuntu-22.04", arch: "ARM64" } + runs-on: ["${{ matrix.os-arch.os }}", "${{ matrix.os-arch.arch }}"] steps: - uses: actions/checkout@v2 with: submodules: recursive - name: Install ACL - if: startsWith(matrix.os,'ubuntu') + if: startsWith(matrix.os-arch.os,'ubuntu') run: sudo apt-get -y install acl-dev + - name: Install LLVM toolchain + if: startsWith(matrix.os-arch.os,'macos') + run: | + brew install llvm@19 + ls /opt/homebrew/opt/llvm@19/bin + echo "/opt/homebrew/opt/llvm@19/bin" >> $GITHUB_PATH + - name: Install Rust toolchain + run: | + rustup show + rustup component add rustfmt clippy - name: Code format check uses: actions-rs/cargo@v1 with: @@ -34,10 +48,17 @@ jobs: with: command: clippy args: -- -D warnings - - name: Build (without Iceoryx) + - name: Build (default features) run: cargo build --verbose - name: Build (with Iceoryx) - if: ${{ ! startsWith(matrix.os,'window') }} + if: ${{ ! startsWith(matrix.os-arch.os,'window') }} run: cargo build --features iceoryx --verbose - - name: Run tests (without Iceoryx) + - name: Build (with symbol prefixing) + run: cargo build --features prefix_symbols --verbose + - name: Run tests (default features) run: cargo test --verbose + - name: Run tests (with Iceoryx) + if: ${{ ! startsWith(matrix.os-arch.os,'window') }} + run: cargo test --features iceoryx --verbose + - name: Run tests (with symbol prefixing) + run: cargo test --features prefix_symbols --verbose diff --git a/Cargo.toml b/Cargo.toml index 947cfc2..408279b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cyclors" -version = "0.3.3" +version = "0.3.4" authors = ["kydos "] license = "Apache-2.0" readme = "README.md" @@ -30,3 +30,4 @@ cmake = "0.1" [features] iceoryx = [] +prefix_symbols = [] diff --git a/Cross.toml b/Cross.toml new file mode 100644 index 0000000..8f0ad31 --- /dev/null +++ b/Cross.toml @@ -0,0 +1,17 @@ +[target.x86_64-unknown-linux-musl] +image = "jenoch/rust-cross:x86_64-unknown-linux-musl" + +[target.arm-unknown-linux-gnueabi] +image = "jenoch/rust-cross:arm-unknown-linux-gnueabi" + +[target.arm-unknown-linux-gnueabihf] +image = "jenoch/rust-cross:arm-unknown-linux-gnueabihf" + +[target.armv7-unknown-linux-gnueabihf] +image = "jenoch/rust-cross:armv7-unknown-linux-gnueabihf" + +[target.aarch64-unknown-linux-gnu] +image = "jenoch/rust-cross:aarch64-unknown-linux-gnu" + +[target.aarch64-unknown-linux-musl] +image = "jenoch/rust-cross:aarch64-unknown-linux-musl" diff --git a/README.md b/README.md index 4cd4038..7758952 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,9 @@ # cyclors Low level RUST APIs for cyclone + +## Supported Features + +* ```iceoryx```: Enable support for the Iceoryx PSMX plugin in Cyclone DDS (Linux and macOS only). +* ```prefix_symbols```: Prefix the symbols in the Cyclone DDS and Cyclocut libraries with the version of the cyclors crate. This allows for different versions of the crate to be loaded together statically. On macOS and Windows platforms ```llvm-nm``` and ```llvm-objcopy``` are required. + +**Note:** The ```iceoryx``` and ```prefix_symbols``` features are optional and cannot be enabled at the same time. diff --git a/build.rs b/build.rs index af0e5bf..228b813 100644 --- a/build.rs +++ b/build.rs @@ -1,86 +1,216 @@ extern crate bindgen; -#[allow(unused_imports)] use std::collections::HashSet; -use std::fs::File; -#[allow(unused_imports)] -use std::io::{LineWriter, Write}; +use std::io::{BufRead, BufReader, LineWriter, Write}; use std::path::{Path, PathBuf}; -#[allow(unused_imports)] -use std::process::Command; use std::{env, fs}; +use std::{ffi::OsStr, fs::metadata, fs::File}; fn main() { let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap()); let mut dir_builder = std::fs::DirBuilder::new(); dir_builder.recursive(true); - // Create Cyclone DDS build directory and initial config - let cyclonedds_dir = out_dir.join("cyclonedds-build"); - dir_builder.create(&cyclonedds_dir).unwrap(); + // Check features + let iceoryx_enabled = is_iceoryx_enabled(); + let prefix_symbols_enabled = is_prefix_symbols_enabled(); - let mut cyclonedds = cmake::Config::new("cyclonedds"); - let mut cyclonedds = cyclonedds.out_dir(cyclonedds_dir); + if iceoryx_enabled && prefix_symbols_enabled { + print!("cargo:warning=iceoryx and prefix_symbols features cannot both be enabled!"); + std::process::exit(1); + } + + // Determine symbol prefix + let prefix = match prefix_symbols_enabled { + true => { + let mut prefix = env::var("CARGO_PKG_VERSION").unwrap().replace('.', "_"); + prefix.insert_str(0, "cyclors_"); + prefix.push('_'); + prefix + } + false => String::new(), + }; + + // Build Iceoryx (if enabled) + let mut iceoryx = PathBuf::new(); + if iceoryx_enabled { + let iceoryx_src_dir = Path::new("iceoryx/iceoryx_meta"); + let iceoryx_out_dir = out_dir.join("iceoryx-build"); + dir_builder.create(&iceoryx_out_dir).unwrap(); + iceoryx = build_iceoryx(iceoryx_src_dir, &iceoryx_out_dir); + } + + // Build Cyclone DDS + let cyclonedds_src_dir = prepare_cyclonedds_src("cyclonedds", &out_dir, &prefix); + let cyclonedds_out_dir = out_dir.join("cyclonedds-build"); + dir_builder.create(&cyclonedds_out_dir).unwrap(); + let cyclonedds = build_cyclonedds( + &cyclonedds_src_dir, + &cyclonedds_out_dir, + iceoryx.as_os_str(), + ); + + // Prefix Cyclone DDS library symbols if enabled + let mut symbols = HashSet::new(); + if prefix_symbols_enabled { + let cyclonedds_lib = cyclonedds.join("lib"); + let ddsc_lib_name = get_library_name("ddsc").unwrap(); + let cyclone_symbols = get_defined_symbols(&cyclonedds_lib, &ddsc_lib_name) + .expect("Failed to get symbols from ddsc library!"); + symbols.extend(cyclone_symbols); + prefix_symbols(&cyclonedds_lib, &ddsc_lib_name, &prefix, &symbols).unwrap(); + } + + // Build cyclocut + let cyclocut_src_dir = Path::new("cyclocut"); + let cyclocut_out_dir = out_dir.join("cyclocut-build"); + dir_builder.create(&cyclocut_out_dir).unwrap(); + let cyclocut = build_cyclocut(cyclocut_src_dir, &cyclocut_out_dir, &cyclonedds); + + // Prefix Cyclocut library symbols if enabled + if prefix_symbols_enabled { + let cyclocut_lib = cyclocut.join("lib"); + let cyclocut_lib_name = get_library_name("cdds-util").unwrap(); + let cyclocut_symbols = get_defined_symbols(&cyclocut_lib, &cyclocut_lib_name) + .expect("Failed to get symbols from cdds-util library!"); + symbols.extend(cyclocut_symbols); + prefix_symbols(&cyclocut_lib, &cyclocut_lib_name, &prefix, &symbols).unwrap(); + } + + // Configure bindings build + let cyclonedds_include = cyclonedds.join("include"); + let cyclocut_include = cyclocut.join("include"); + + // C++ library added here to avoid link line ordering issue on some platforms + if iceoryx_enabled { + #[cfg(target_os = "linux")] + println!("cargo:rustc-link-lib=stdc++"); + + #[cfg(target_os = "macos")] + println!("cargo:rustc-link-lib=c++"); + } - // Create initial bindings builder let mut bindings = bindgen::Builder::default(); + bindings = bindings + .header("wrapper.h") + .clang_arg(format!("-I{}", cyclonedds_include.to_str().unwrap())) + .clang_arg(format!("-I{}", cyclocut_include.to_str().unwrap())) + .generate_comments(false); + + // Set link name if prefix enabled + if prefix_symbols_enabled { + #[derive(Debug)] + struct PrefixLinkNameCallback { + prefix: String, + symbols: HashSet, + } + + impl bindgen::callbacks::ParseCallbacks for PrefixLinkNameCallback { + fn generated_link_name_override( + &self, + item_info: bindgen::callbacks::ItemInfo<'_>, + ) -> Option { + let mut item = String::from(""); + #[cfg(target_os = "macos")] + item.push('_'); + item.push_str(item_info.name); + match self.symbols.contains(&item) { + true => { + let mut prefix = String::from(""); + #[cfg(target_os = "macos")] + prefix.push('_'); + prefix.push_str(&self.prefix); + prefix.push_str(item_info.name); + Some(prefix) + } + false => None, + } + } + } + bindings = bindings.parse_callbacks(Box::new(PrefixLinkNameCallback { + prefix: prefix.clone(), + symbols: symbols.clone(), + })); + } + + // Add *IMAGE_TLS_DIRECTORY* to blocklist on Windows due to + // https://github.com/rust-lang/rust-bindgen/issues/2179 + #[cfg(target_os = "windows")] + let bindings = bindings + .clang_arg("-Wno-invalid-token-paste") + .blocklist_type("^(.*IMAGE_TLS_DIRECTORY.*)$"); + + // Set link name prefix on additional wrapper functions + generate_template_src(&prefix, &out_dir).unwrap(); + + // Generate bindings + let bindings = bindings.generate().expect("Unable to generate bindings"); + + bindings + .write_to_file(out_dir.join("bindings.rs")) + .expect("Couldn't write bindings!"); +} + +fn is_iceoryx_enabled() -> bool { #[cfg(feature = "iceoryx")] { - let supported; #[cfg(target_os = "windows")] { print!("cargo:warning=Cyclone DDS Iceoryx PSMX plugin is not supported on Windows!"); - supported = false; - } - #[cfg(not(target_os = "windows"))] - { - supported = true; - } - - if !supported { std::process::exit(1); } + true + } + #[cfg(not(feature = "iceoryx"))] + { + false + } +} - // Build iceoryx - let iceoryx_dir = out_dir.join("iceoryx-build"); - dir_builder.create(&iceoryx_dir).unwrap(); - let mut iceoryx = cmake::Config::new("iceoryx/iceoryx_meta"); +fn is_prefix_symbols_enabled() -> bool { + #[cfg(feature = "prefix_symbols")] + { + true + } + #[cfg(not(feature = "prefix_symbols"))] + { + false + } +} - let iceoryx = iceoryx - .define("BUILD_SHARED_LIBS", "OFF") - .out_dir(iceoryx_dir) - .build(); +fn build_iceoryx(src_dir: &Path, out_dir: &Path) -> PathBuf { + let mut iceoryx = cmake::Config::new(src_dir); - let iceoryx_install_path = iceoryx.as_os_str(); - let iceoryx_lib = iceoryx.join("lib"); + // Force compilation of Iceoryx in release mode on Windows due to + // https://github.com/rust-lang/rust/issues/39016 + #[cfg(all(debug_assertions, target_os = "windows"))] + let iceoryx = iceoryx.profile("Release"); - // Add iceoryx lib to link - println!("cargo:rustc-link-search=native={}", iceoryx_lib.display()); - println!("cargo:rustc-link-lib=static=iceoryx_hoofs"); - println!("cargo:rustc-link-lib=static=iceoryx_posh"); - println!("cargo:rustc-link-lib=static=iceoryx_platform"); + let iceoryx_path = iceoryx + .define("BUILD_SHARED_LIBS", "OFF") + .out_dir(out_dir) + .build(); - cyclonedds = cyclonedds - .env("iceoryx_hoofs_DIR", iceoryx_install_path) - .env("iceoryx_posh_DIR", iceoryx_install_path) - .define("ENABLE_ICEORYX", "YES"); + // Add iceoryx lib to link + let iceoryx_lib = iceoryx_path.join("lib"); + println!("cargo:rustc-link-search=native={}", iceoryx_lib.display()); + println!("cargo:rustc-link-lib=static=iceoryx_hoofs"); + println!("cargo:rustc-link-lib=static=iceoryx_posh"); + println!("cargo:rustc-link-lib=static=iceoryx_platform"); - #[cfg(target_os = "linux")] - println!("cargo:rustc-link-lib=acl"); + #[cfg(target_os = "linux")] + println!("cargo:rustc-link-lib=acl"); - #[cfg(not(any(target_os = "windows", target_os = "macos")))] - println!("cargo:rustc-link-lib=stdc++"); + iceoryx_path +} - #[cfg(target_os = "macos")] - println!("cargo:rustc-link-lib=c++"); - } - #[cfg(not(feature = "iceoryx"))] - { - cyclonedds = cyclonedds.define("ENABLE_ICEORYX", "NO"); - } +fn build_cyclonedds(src_dir: &Path, out_dir: &Path, iceoryx_path: &OsStr) -> PathBuf { + // Create Cyclone DDS build initial config + let mut cyclonedds = cmake::Config::new(src_dir); + let mut cyclonedds = cyclonedds.out_dir(out_dir); - // Finish configuration of cyclonedds build + // Configure cyclonedds build cyclonedds = cyclonedds .define("BUILD_SHARED_LIBS", "OFF") .define("BUILD_IDLC", "OFF") @@ -90,18 +220,25 @@ fn main() { .define("ENABLE_SECURITY", "NO") .define("CMAKE_INSTALL_LIBDIR", "lib"); + if !iceoryx_path.is_empty() { + cyclonedds = cyclonedds + .env("iceoryx_hoofs_DIR", iceoryx_path) + .env("iceoryx_posh_DIR", iceoryx_path) + .define("ENABLE_ICEORYX", "YES"); + } else { + cyclonedds = cyclonedds.define("ENABLE_ICEORYX", "NO"); + } + // Force compilation of Cyclone DDS in release mode on Windows due to // https://github.com/rust-lang/rust/issues/39016 #[cfg(all(debug_assertions, target_os = "windows"))] let cyclonedds = cyclonedds.profile("Release"); // Build cyclonedds - let cyclonedds = cyclonedds.build(); - - let cyclonedds_include = cyclonedds.join("include"); - let cyclonedds_lib = cyclonedds.join("lib"); + let cyclonedds_path = cyclonedds.build(); // Add cyclonedds lib to link + let cyclonedds_lib = cyclonedds_path.join("lib"); println!( "cargo:rustc-link-search=native={}", cyclonedds_lib.display() @@ -110,129 +247,152 @@ fn main() { // Add Windows libraries required by Cyclone to link #[cfg(target_os = "windows")] - println!("cargo:rustc-link-lib=Iphlpapi"); - #[cfg(target_os = "windows")] - println!("cargo:rustc-link-lib=DbgHelp"); - #[cfg(target_os = "windows")] - println!("cargo:rustc-link-lib=Bcrypt"); + { + println!("cargo:rustc-link-lib=Iphlpapi"); + println!("cargo:rustc-link-lib=DbgHelp"); + println!("cargo:rustc-link-lib=Bcrypt"); + } - // Build cyclocut - let cyclocut_dir = out_dir.join("cyclocut-build"); - dir_builder.create(&cyclocut_dir).unwrap(); - let mut cyclocut = cmake::Config::new("cyclocut"); + cyclonedds_path +} + +fn build_cyclocut(src_dir: &Path, out_dir: &Path, cyclonedds_dir: &Path) -> PathBuf { + let mut cyclocut = cmake::Config::new(src_dir); // Force compilation of Cyclocut in release mode on Windows due to // https://github.com/rust-lang/rust/issues/39016 #[cfg(all(debug_assertions, target_os = "windows"))] let cyclocut = cyclocut.profile("Release"); - let cyclocut = cyclocut + let cyclonedds_include = cyclonedds_dir.join("include"); + let cyclonedds_lib = cyclonedds_dir.join("lib"); + + let cyclocut_path = cyclocut .env("CYCLONE_INCLUDE", &cyclonedds_include) .env("CYCLONE_LIB", &cyclonedds_lib) .define("CYCLONE_INCLUDE", cyclonedds_include.clone()) .define("CYCLONE_LIB", cyclonedds_lib.clone()) .define("BUILD_SHARED_LIBS", "OFF") .define("CMAKE_INSTALL_LIBDIR", "lib") - .out_dir(cyclocut_dir) + .out_dir(out_dir) .build(); - let cyclocut_include = cyclocut.join("include"); - let cyclocut_lib = cyclocut.join("lib"); - // Add cyclocut lib to link + let cyclocut_lib = cyclocut_path.join("lib"); println!("cargo:rustc-link-search=native={}", cyclocut_lib.display()); println!("cargo:rustc-link-lib=static=cdds-util"); - // Finish configuration of bindings build - bindings = bindings - .header("wrapper.h") - .clang_arg(format!("-I{}", cyclonedds_include.to_str().unwrap())) - .clang_arg(format!("-I{}", cyclocut_include.to_str().unwrap())) - .generate_comments(false); - - #[allow(unused)] - let mut prefix = String::from(""); - - // Prefix symbols in Cyclone DDS and Cyclocut libraries to ensure uniqueness - #[cfg(all(target_os = "linux", not(feature = "iceoryx")))] - { - // Prefix = cyclors_ - prefix = env::var("CARGO_PKG_VERSION").unwrap().replace('.', "_"); - prefix.insert_str(0, "cyclors_"); - prefix.push('_'); - - let mut symbols = HashSet::new(); + cyclocut_path +} - let cyclone_symbols = get_defined_symbols(&cyclonedds_lib, "libddsc.a") - .expect("Failed to get symbols from libddsc.a!"); - symbols.extend(cyclone_symbols); - prefix_symbols(&cyclonedds_lib, "libddsc.a", &prefix, &symbols).unwrap(); +#[allow(unused_variables)] +fn prepare_cyclonedds_src(src_dir: &str, out_dir: &Path, prefix: &str) -> PathBuf { + #[cfg(target_os = "windows")] + if !prefix.is_empty() { + let mut dst_dir = src_dir.to_string(); + dst_dir.push_str("-src"); + let dst_dir = out_dir.join(dst_dir); + + // Delete copied source directory if it already exists + if dst_dir.exists() { + fs::remove_dir_all(dst_dir.clone()).unwrap(); + } + copy_dir_recursive(&PathBuf::from(src_dir), &dst_dir).unwrap(); + + // Prefix tls_callback_func in cyclonedds-src/src/ddsrt/src/cdtors.c + let mut prefixed_func = prefix.to_string(); + prefixed_func.push_str("tls_callback_func"); + let cdtors = dst_dir + .join("src") + .join("ddsrt") + .join("src") + .join("cdtors.c"); + replace_in_file(&cdtors, "tls_callback_func", &prefixed_func).unwrap(); + + return dst_dir; + } + PathBuf::from(src_dir) +} - let cyclocut_symbols = get_defined_symbols(&cyclocut_lib, "libcdds-util.a") - .expect("Failed to get symbols from libcdds-util.a!"); - symbols.extend(cyclocut_symbols); - prefix_symbols(&cyclocut_lib, "libcdds-util.a", &prefix, &symbols).unwrap(); +#[allow(unused)] +fn copy_dir_recursive(src: &Path, dst: &Path) -> std::io::Result<()> { + println!( + "src = {}, dir = {}", + src.to_str().unwrap(), + dst.to_str().unwrap() + ); + if !dst.exists() { + fs::create_dir_all(dst)?; + } - #[derive(Debug)] - struct PrefixLinkNameCallback { - prefix: String, - symbols: HashSet, - } + for entry in fs::read_dir(src)? { + let entry = entry?; + let path = entry.path(); + let dest_path = dst.join(entry.file_name()); - impl bindgen::callbacks::ParseCallbacks for PrefixLinkNameCallback { - fn generated_link_name_override( - &self, - item_info: bindgen::callbacks::ItemInfo<'_>, - ) -> Option { - match self.symbols.contains(item_info.name) { - true => { - let mut prefix = self.prefix.clone(); - prefix.push_str(item_info.name); - Some(prefix) - } - false => None, - } - } + if path.is_dir() { + copy_dir_recursive(&path, &dest_path)?; + } else { + fs::copy(&path, &dest_path)?; } - - bindings = bindings.parse_callbacks(Box::new(PrefixLinkNameCallback { - prefix: prefix.clone(), - symbols: symbols.clone(), - })); } + Ok(()) +} - // Add *IMAGE_TLS_DIRECTORY* to blocklist on Windows due to - // https://github.com/rust-lang/rust-bindgen/issues/2179 - #[cfg(target_os = "windows")] - let bindings = bindings - .clang_arg("-Wno-invalid-token-paste") - .blocklist_type("^(.*IMAGE_TLS_DIRECTORY.*)$"); +#[allow(unused)] +fn replace_in_file(file_path: &Path, from: &str, to: &str) -> std::io::Result<()> { + // Read the file content into a string + let content = fs::read_to_string(file_path)?; - // Set link name prefix on additional wrapStringper functions - generate_template_src(&prefix, &out_dir).unwrap(); + // Replace all occurrences of `from` with `to` + let new_content = content.replace(from, to); - // Generate bindings - let bindings = bindings.generate().expect("Unable to generate bindings"); + // Write the modified content back to the file + let mut file = fs::OpenOptions::new() + .write(true) + .truncate(true) // Clear the file before writing + .open(file_path)?; - bindings - .write_to_file(out_dir.join("bindings.rs")) - .expect("Couldn't write bindings!"); + file.write_all(new_content.as_bytes())?; + Ok(()) } -#[cfg(all(target_os = "linux", not(feature = "iceoryx")))] -fn get_defined_symbols(lib_dir: &Path, lib_name: &str) -> Result, String> { - use std::io::{BufRead, BufReader}; +#[allow(unused_variables)] +fn get_library_name(lib_name: &str) -> Option { + #[cfg(any(target_os = "linux", target_os = "macos"))] + { + let mut file_name = String::from("lib"); + file_name.push_str(lib_name); + file_name.push_str(".a"); + Some(file_name) + } + #[cfg(target_os = "windows")] + { + let mut file_name = String::from(lib_name); + file_name.push_str(".lib"); + Some(file_name) + } + #[cfg(not(any(target_os = "linux", target_os = "windows", target_os = "macos")))] + None +} +fn get_defined_symbols(lib_dir: &Path, lib_name: &str) -> Result, String> { let lib_path = lib_dir.to_path_buf().join(lib_name); let mut nm_file_name = lib_name.to_owned(); nm_file_name.push_str(".nm"); let symbol_file_path = lib_dir.to_path_buf().join(nm_file_name); let mut nm = cmake::Config::new("nm"); - nm.build_target("all") + nm.build_target("read_symbols") .define("LIB_PATH", lib_path.clone()) .build(); + // Check for unexpected errors in stderr.txt + let mut stderr_file_name = lib_name.to_owned(); + stderr_file_name.push_str(".nm.stderr"); + let stderr_file_path = lib_dir.to_path_buf().join(stderr_file_name); + check_nm_stderr(&stderr_file_path).unwrap(); + match File::open(symbol_file_path.clone()) { Ok(symbol_file) => { let reader = BufReader::new(symbol_file); @@ -243,6 +403,11 @@ fn get_defined_symbols(lib_dir: &Path, lib_name: &str) -> Result Ok(line) => { let tokens: Vec<&str> = line.split_whitespace().collect(); let symbol = *tokens.last().unwrap(); + #[cfg(target_os = "windows")] + if !symbol.ends_with("tls_callback_func") { + result.insert(String::from(symbol)); + } + #[cfg(not(target_os = "windows"))] result.insert(String::from(symbol)); } Err(_) => return Err(format!("Failed to run nm on library {}", lib_name)), @@ -260,7 +425,41 @@ fn get_defined_symbols(lib_dir: &Path, lib_name: &str) -> Result } } -#[cfg(all(target_os = "linux", not(feature = "iceoryx")))] +fn check_nm_stderr(stderr: &Path) -> Result<(), String> { + match File::open(stderr) { + Ok(file) => { + let reader = BufReader::new(file); + + for line in reader.lines() { + match line { + Ok(line) => { + // Some object files within the library may report no symbols - this is okay + if !line.ends_with(": no symbols") { + return Err(format!( + "nm completed with errors - see {} for details", + stderr.to_str().unwrap() + )); + } + } + Err(_) => { + return Err(format!( + "Failed to read nm stderr file: {}", + stderr.to_str().unwrap() + )) + } + } + } + } + Err(_) => { + return Err(format!( + "Failed to open nm stderr file: {}", + stderr.to_str().unwrap() + )); + } + } + Ok(()) +} + fn prefix_symbols( lib_dir: &Path, lib_name: &str, @@ -279,9 +478,22 @@ fn prefix_symbols( for symbol in symbols { let mut symbol_arg = symbol.clone(); - symbol_arg.push(' '); - symbol_arg.push_str(prefix); - symbol_arg.push_str(symbol); + + #[cfg(target_os = "macos")] + { + let mut symbol_stripped = symbol.clone(); + symbol_stripped.remove(0); + symbol_arg.push(' '); + symbol_arg.push('_'); + symbol_arg.push_str(prefix); + symbol_arg.push_str(&symbol_stripped); + } + #[cfg(not(target_os = "macos"))] + { + symbol_arg.push(' '); + symbol_arg.push_str(prefix); + symbol_arg.push_str(symbol); + } symbol_arg.push('\n'); if symbol_file.write_all(symbol_arg.as_bytes()).is_err() { return Err(format!( @@ -300,10 +512,16 @@ fn prefix_symbols( let mut objcopy = cmake::Config::new("objcopy"); objcopy - .build_target("all") + .build_target("mangle_library") .define("LIB_PATH", lib_file_path.clone()) .define("SYMBOL_FILE_PATH", symbol_file_path.clone()) .build(); + + // Check for unexpected errors in stderr.txt + let mut stderr_file_name = lib_name.to_owned(); + stderr_file_name.push_str(".objcopy.stderr"); + let stderr_file_path = lib_dir.to_path_buf().join(stderr_file_name); + check_objcopy_stderr(&stderr_file_path).unwrap(); Ok(()) } Err(_) => Err(format!( @@ -313,6 +531,32 @@ fn prefix_symbols( } } +fn check_objcopy_stderr(stderr: &Path) -> Result<(), String> { + if let Ok(metadata) = metadata(stderr) { + if metadata.is_file() { + if metadata.len() > 0 { + println!("File exists and has a size greater than 0."); + Err(format!( + "Objcopy command failed with errors - see {} for details", + stderr.to_str().unwrap() + )) + } else { + Ok(()) + } + } else { + Err(format!( + "Objcopy stderr file is not a file: {}", + stderr.to_str().unwrap() + )) + } + } else { + Err(format!( + "Failed to read objcopy stderr file metadata: {}", + stderr.to_str().unwrap() + )) + } +} + fn generate_template_src(prefix: &str, out_dir: &Path) -> Result<(), String> { let src_path = Path::new("src/functions.template"); let dst_path = out_dir.join("functions.rs"); diff --git a/examples/disco.rs b/examples/disco.rs index b6c768d..378ad3f 100644 --- a/examples/disco.rs +++ b/examples/disco.rs @@ -151,7 +151,7 @@ unsafe extern "C" fn on_data(dr: dds_entity_t, arg: *mut std::os::raw::c_void) { samples.as_mut_ptr() as *mut *mut libc::c_void, MAX_SAMPLES as i32, ); - Box::into_raw(btx); + let _ = Box::into_raw(btx); } fn main() { unsafe { diff --git a/nm/CMakeLists.txt b/nm/CMakeLists.txt index 300d5ce..e0ff6cd 100644 --- a/nm/CMakeLists.txt +++ b/nm/CMakeLists.txt @@ -1,8 +1,12 @@ cmake_minimum_required(VERSION 3.12) project(NM) -# Include the CMakeFindBinUtils module to find nm -include(CMakeFindBinUtils) +if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux") + # Include the CMakeFindBinUtils module to find nm + include(CMakeFindBinUtils) +else() + find_program(CMAKE_NM llvm-nm) +endif() # Ensure nm is available if(NOT CMAKE_NM) @@ -24,16 +28,7 @@ endif() # Custom target to run nm on the library add_custom_target(read_symbols ALL - COMMAND ${CMAKE_COMMAND} -E echo "Running nm on ${LIB_PATH}..." - # Run nm and redirect stderr to a file - COMMAND ${CMAKE_NM} --defined-only --print-file-name ${LIB_PATH} 1> ${LIB_PATH}.nm 2> ${LIB_PATH}.stderr - - # Check if stderr is empty (i.e., no errors were produced) - COMMAND ${CMAKE_COMMAND} -E echo "Checking for nm errors..." - COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/check_errors.sh ${LIB_PATH}.stderr - - # Clean up stderr.txt after checking for errors - COMMAND ${CMAKE_COMMAND} -E remove ${LIB_PATH}.stderr - COMMENT "Reading library symbols with nm..." + COMMAND ${CMAKE_COMMAND} -E echo "Running nm on ${LIB_PATH}..." + COMMAND ${CMAKE_NM} --defined-only --print-file-name ${LIB_PATH} 1> ${LIB_PATH}.nm 2> ${LIB_PATH}.nm.stderr ) diff --git a/nm/check_errors.sh b/nm/check_errors.sh deleted file mode 100755 index f5b1166..0000000 --- a/nm/check_errors.sh +++ /dev/null @@ -1,10 +0,0 @@ -#!/bin/sh - -if [ ! -s $1 ]; then - echo 'Command succeeded with no stderr output.' - exit 0 -else - echo 'Command failed with errors:' - cat stderr.txt - exit 1 -fi diff --git a/objcopy/CMakeLists.txt b/objcopy/CMakeLists.txt index 5589699..e7fc699 100644 --- a/objcopy/CMakeLists.txt +++ b/objcopy/CMakeLists.txt @@ -1,8 +1,12 @@ cmake_minimum_required(VERSION 3.12) project(Objcopy) -# Include the CMakeFindBinUtils module to find objcopy -include(CMakeFindBinUtils) +if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux") + # Include the CMakeFindBinUtils module to find objcopy + include(CMakeFindBinUtils) +else() + find_program(CMAKE_OBJCOPY llvm-objcopy) +endif() # Ensure objcopy is available if(NOT CMAKE_OBJCOPY) @@ -35,16 +39,7 @@ endif() # Custom target to mangle the library add_custom_target(mangle_library ALL - COMMAND ${CMAKE_COMMAND} -E echo "Running objcopy --redefine-syms on ${LIB_PATH} with symbols from ${SYMBOL_FILE_PATH}..." - # Run objcopy and redirect stderr to a file - COMMAND ${CMAKE_OBJCOPY} --redefine-syms=${SYMBOL_FILE_PATH} ${LIB_PATH} 2> ${LIB_PATH}.stderr - - # Check if stderr is empty (i.e., no errors were produced) - COMMAND ${CMAKE_COMMAND} -E echo "Checking for objcopy errors..." - COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/check_errors.sh ${LIB_PATH}.stderr - - # Clean up stderr.txt after checking for errors - COMMAND ${CMAKE_COMMAND} -E remove ${LIB_PATH}.stderr - COMMENT "Mangling library with objcopy..." + COMMAND ${CMAKE_COMMAND} -E echo "Running objcopy --redefine-syms on ${LIB_PATH} with symbols from ${SYMBOL_FILE_PATH}..." + COMMAND ${CMAKE_OBJCOPY} --redefine-syms=${SYMBOL_FILE_PATH} ${LIB_PATH} 2> ${LIB_PATH}.objcopy.stderr ) diff --git a/objcopy/check_errors.sh b/objcopy/check_errors.sh deleted file mode 100755 index f5b1166..0000000 --- a/objcopy/check_errors.sh +++ /dev/null @@ -1,10 +0,0 @@ -#!/bin/sh - -if [ ! -s $1 ]; then - echo 'Command succeeded with no stderr output.' - exit 0 -else - echo 'Command failed with errors:' - cat stderr.txt - exit 1 -fi diff --git a/rust-toolchain.toml b/rust-toolchain.toml new file mode 100644 index 0000000..292fe49 --- /dev/null +++ b/rust-toolchain.toml @@ -0,0 +1,2 @@ +[toolchain] +channel = "stable"