From 1daa25ecc2f09db7291d28088b18f803657745ce Mon Sep 17 00:00:00 2001 From: Geoff Martin Date: Mon, 9 Dec 2024 14:35:44 +0000 Subject: [PATCH 1/5] Symbol prefixing now supported on all supported platforms. --- .github/workflows/rust.yml | 28 +- Cargo.toml | 7 +- build.rs | 538 +++++++++++++++++++++++++++---------- examples/disco.rs | 2 +- nm/CMakeLists.txt | 21 +- nm/check_errors.sh | 10 - objcopy/CMakeLists.txt | 21 +- objcopy/check_errors.sh | 10 - 8 files changed, 439 insertions(+), 198 deletions(-) delete mode 100755 nm/check_errors.sh delete mode 100755 objcopy/check_errors.sh diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 71a87fc..ca10164 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-latest, macos-latest, [ubuntu-22.04, ARM64]] - runs-on: ${{ 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,11 +48,15 @@ jobs: with: command: clippy args: -- -D warnings - - name: Build (without Iceoryx) + - name: Build (default features) run: cargo build --verbose - name: Build (with Iceoryx) 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) 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 badfd31..b1169d2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,17 +1,17 @@ [package] name = "cyclors" -version = "0.2.3" +version = "0.2.4" authors = ["kydos "] license = "Apache-2.0" readme = "README.md" keywords = ["DDS", "CycloneDDS"] -repository = "https://github.com/kydos/cyclors" +repository = "https://github.com/ZettaScaleLabs/cyclors" documentation = "https://docs.rs/cyclors" description = """ Low-level API for the native CycloneDDS bindings (libddsc-sys). """ categories = ["api-bindings"] -edition = "2018" +edition = "2021" [lib] name = "cyclors" @@ -30,3 +30,4 @@ cmake = "0.1" [features] iceoryx = [] +prefix_symbols = [] diff --git a/build.rs b/build.rs index 1015e67..99b3295 100644 --- a/build.rs +++ b/build.rs @@ -1,84 +1,221 @@ 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); + } - // Create initial bindings builder - let mut bindings = bindgen::Builder::default(); + // 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); + } - #[cfg(feature = "iceoryx")] - { - // 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"); - - // 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"); - - let iceoryx = iceoryx - .define("BUILD_SHARED_LIBS", "OFF") - .out_dir(iceoryx_dir) - .build(); - - let iceoryx_lib = iceoryx.join("lib"); - let iceoryx_include = iceoryx.join("include/iceoryx/v2.0.3"); + // 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(); + } - // Add iceoryx lib to link - println!("cargo:rustc-link-search=native={}", iceoryx_lib.display()); - println!("cargo:rustc-link-lib=static=iceoryx_binding_c"); - println!("cargo:rustc-link-lib=static=iceoryx_hoofs"); - println!("cargo:rustc-link-lib=static=iceoryx_posh"); - println!("cargo:rustc-link-lib=static=iceoryx_platform"); + // 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(); + } - let iceoryx_install_path = iceoryx.as_os_str(); + // Configure bindings build + let cyclonedds_include = cyclonedds.join("include"); + let cyclocut_include = cyclocut.join("include"); - cyclonedds = cyclonedds - .env("iceoryx_binding_c_DIR", iceoryx_install_path) - .env("iceoryx_hoofs_DIR", iceoryx_install_path) - .env("iceoryx_posh_DIR", iceoryx_install_path) - .define("ENABLE_SHM", "YES"); + 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); + if iceoryx_enabled { + let iceoryx_include = iceoryx.join("include/iceoryx/v2.0.3"); bindings = bindings .clang_arg(format!("-I{}", iceoryx_include.to_str().unwrap())) .clang_arg("-DDDS_HAS_SHM=1"); + } - #[cfg(target_os = "linux")] - println!("cargo:rustc-link-lib=acl"); + // Set link name if prefix enabled + if prefix_symbols_enabled { + #[derive(Debug)] + struct PrefixLinkNameCallback { + prefix: String, + symbols: HashSet, + } - #[cfg(not(any(target_os = "windows", target_os = "macos")))] - println!("cargo:rustc-link-lib=stdc++"); + 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, + } + } + } - #[cfg(target_os = "macos")] - println!("cargo:rustc-link-lib=c++"); + 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")] + { + #[cfg(target_os = "windows")] + { + print!("cargo:warning=Cyclone DDS Iceoryx PSMX plugin is not supported on Windows!"); + std::process::exit(1); + } + true } #[cfg(not(feature = "iceoryx"))] { - cyclonedds = cyclonedds.define("ENABLE_SHM", "NO"); + false + } +} + +fn is_prefix_symbols_enabled() -> bool { + #[cfg(feature = "prefix_symbols")] + { + true + } + #[cfg(not(feature = "prefix_symbols"))] + { + false } +} - // Finish configuration of cyclonedds build +fn build_iceoryx(src_dir: &Path, out_dir: &Path) -> PathBuf { + let mut iceoryx = cmake::Config::new(src_dir); + + // 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"); + + let iceoryx_path = iceoryx + .define("BUILD_SHARED_LIBS", "OFF") + .out_dir(out_dir) + .build(); + + // 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_binding_c"); + 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"); + println!("cargo:rustc-link-lib=stdc++"); + } + + #[cfg(target_os = "macos")] + println!("cargo:rustc-link-lib=c++"); + + iceoryx_path +} + +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); + + // Configure cyclonedds build cyclonedds = cyclonedds .define("BUILD_SHARED_LIBS", "OFF") .define("BUILD_IDLC", "OFF") @@ -88,18 +225,26 @@ fn main() { .define("ENABLE_SECURITY", "NO") .define("CMAKE_INSTALL_LIBDIR", "lib"); + if !iceoryx_path.is_empty() { + cyclonedds = cyclonedds + .env("iceoryx_binding_c_DIR", iceoryx_path) + .env("iceoryx_hoofs_DIR", iceoryx_path) + .env("iceoryx_posh_DIR", iceoryx_path) + .define("ENABLE_SHM", "YES"); + } else { + cyclonedds = cyclonedds.define("ENABLE_SHM", "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() @@ -108,129 +253,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); @@ -241,6 +409,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)), @@ -258,7 +431,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, @@ -277,9 +484,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!( @@ -298,10 +518,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!( @@ -311,6 +537,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 From 94b898593349c71c30c4468c54de1f71a6e27af7 Mon Sep 17 00:00:00 2001 From: Geoff Martin Date: Mon, 9 Dec 2024 14:39:57 +0000 Subject: [PATCH 2/5] Trigger CI build. From 734f5c405531ebf08424e1451c7386edadae634c Mon Sep 17 00:00:00 2001 From: Geoff Martin Date: Mon, 9 Dec 2024 14:47:44 +0000 Subject: [PATCH 3/5] Iceoryx is supported on Windows for 0.2.x. --- build.rs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/build.rs b/build.rs index 99b3295..9d647fc 100644 --- a/build.rs +++ b/build.rs @@ -153,11 +153,6 @@ fn main() { fn is_iceoryx_enabled() -> bool { #[cfg(feature = "iceoryx")] { - #[cfg(target_os = "windows")] - { - print!("cargo:warning=Cyclone DDS Iceoryx PSMX plugin is not supported on Windows!"); - std::process::exit(1); - } true } #[cfg(not(feature = "iceoryx"))] From 15cccfc136245cfc28265efd9216f0019b118f6e Mon Sep 17 00:00:00 2001 From: Geoff Martin Date: Mon, 9 Dec 2024 18:14:47 +0000 Subject: [PATCH 4/5] Documented optional features. --- README.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/README.md b/README.md index 4cd4038..5fe39e4 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,9 @@ # cyclors Low level RUST APIs for cyclone + +## Supported Features + +* ```iceoryx```: Enable support for Iceoryx shared memory in Cyclone DDS. +* ```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. From 915a2bf59f4acafbf3ea59f7b0b3a7d5b865374a Mon Sep 17 00:00:00 2001 From: Geoff Martin Date: Mon, 9 Dec 2024 18:17:28 +0000 Subject: [PATCH 5/5] Documented optional features. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 5fe39e4..850099f 100644 --- a/README.md +++ b/README.md @@ -6,4 +6,4 @@ Low level RUST APIs for cyclone * ```iceoryx```: Enable support for Iceoryx shared memory in Cyclone DDS. * ```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. +**Note:** The ```iceoryx``` and ```prefix_symbols``` features are optional and cannot be enabled at the same time.