Skip to content

Commit

Permalink
Allow multiple versions of the Cyclors library to be loaded simultane…
Browse files Browse the repository at this point in the history
…ously on Linux systems. (#26)
  • Loading branch information
gmartin82 authored Oct 16, 2024
1 parent b499388 commit 700d028
Show file tree
Hide file tree
Showing 5 changed files with 234 additions and 40 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/rust.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ name: Rust

on:
push:
branches: [master]
branches: [master, dev/0.2.1]
pull_request:
branches: [master]
branches: [master, dev/0.2.1]

env:
CARGO_TERM_COLOR: always
Expand Down
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "cyclors"
version = "0.2.1"
version = "0.2.2"
authors = ["kydos <angelo@icorsaro.net>"]
license = "Apache-2.0"
readme = "README.md"
Expand Down
190 changes: 188 additions & 2 deletions build.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,14 @@
extern crate bindgen;

use std::env;
use std::path::PathBuf;
#[allow(unused_imports)]
use std::collections::HashSet;
use std::fs::File;
#[allow(unused_imports)]
use std::io::{LineWriter, Write};
use std::path::{Path, PathBuf};
#[allow(unused_imports)]
use std::process::Command;
use std::{env, fs};

fn main() {
let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap());
Expand Down Expand Up @@ -104,6 +111,8 @@ fn main() {
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");

// Build cyclocut
let cyclocut_dir = out_dir.join("cyclocut-build");
Expand Down Expand Up @@ -139,17 +148,194 @@ fn main() {
.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_<version>_
prefix = env::var("CARGO_PKG_VERSION").unwrap().replace('.', "_");
prefix.insert_str(0, "cyclors_");
prefix.push('_');

let mut symbols = HashSet::new();

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();

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();

#[derive(Debug)]
struct PrefixLinkNameCallback {
prefix: String,
symbols: HashSet<String>,
}

impl bindgen::callbacks::ParseCallbacks for PrefixLinkNameCallback {
fn generated_link_name_override(
&self,
item_info: bindgen::callbacks::ItemInfo<'_>,
) -> Option<String> {
match self.symbols.contains(item_info.name) {
true => {
let mut prefix = self.prefix.clone();
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 wrapStringper 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!");
}

#[cfg(all(target_os = "linux", not(feature = "iceoryx")))]
fn get_defined_symbols(lib_dir: &Path, lib_name: &str) -> Result<HashSet<String>, String> {
let lib_path = lib_dir.to_path_buf().join(lib_name);

let rc = Command::new("nm")
.arg("--defined-only")
.arg("--print-file-name")
.arg(lib_path)
.output();

match rc {
Ok(output) => {
let stdout = String::from_utf8_lossy(&output.stdout);
let stderr = String::from_utf8_lossy(&output.stderr);

match stderr.is_empty() {
true => {
let mut result: HashSet<String> = HashSet::new();
for line in stdout.lines() {
let tokens: Vec<&str> = line.split_whitespace().collect();
let symbol = *tokens.last().unwrap();
result.insert(String::from(symbol));
}
Ok(result)
}
false => Err(format!(
"Failed to run nm on library {} (stderr: {})",
lib_name,
String::from(stderr)
)),
}
}
Err(_) => Err(format!("Failed to run nm on library {}", lib_name)),
}
}

#[cfg(all(target_os = "linux", not(feature = "iceoryx")))]
fn prefix_symbols(
lib_dir: &Path,
lib_name: &str,
prefix: &str,
symbols: &HashSet<String>,
) -> Result<(), String> {
let mut objcopy_file_name = lib_name.to_owned();
objcopy_file_name.push_str(".objcopy");

let lib_file_path = lib_dir.to_path_buf().join(lib_name);
let symbol_file_path = lib_dir.to_path_buf().join(objcopy_file_name);

match File::create(symbol_file_path.clone()) {
Ok(symbol_file) => {
let mut symbol_file = LineWriter::new(symbol_file);

for symbol in symbols {
let mut symbol_arg = symbol.clone();
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!(
"Failed to write symbol file for library {}",
lib_name
));
}
}

if symbol_file.flush().is_err() {
return Err(format!(
"Failed to write symbol file for library {}",
lib_name
));
}
let arg = format!("--redefine-syms={}", symbol_file_path.to_str().unwrap());
match Command::new("objcopy").arg(arg).arg(lib_file_path).output() {
Ok(_) => Ok(()),
Err(_) => Err(format!("Failed to run objcopy on library {}", lib_name)),
}
}
Err(_) => Err(format!(
"Failed to create symbol file for library {}",
lib_name
)),
}
}

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");

match fs::read_to_string(src_path) {
Ok(mut contents) => {
contents = contents.replace("<prefix>", prefix);

match File::create(&dst_path) {
Ok(mut file) => {
if file.write_all(contents.as_bytes()).is_err() {
let path = dst_path.to_str().unwrap();
return Err(format!(
"Failed to write the modified content to the destination file {}",
path
));
}

println!("cargo:rerun-if-changed=src/lib.rs");
println!("cargo:rerun-if-changed=src/functions.template");
Ok(())
}
Err(_) => {
let path = dst_path.to_str().unwrap();
Err(format!(
"Failed to open the destination file ({}) for writing",
path
))
}
}
}
Err(_) => Err(String::from(
"Failed to read the source file src/functions.template",
)),
}
}
41 changes: 41 additions & 0 deletions src/functions.template
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/* Additional wrapper functions for select exported inline functions */

extern "C" {
#[link_name = "<prefix>ddsi_serdata_size"]
pub fn ddsi_serdata_size(d: *const ddsi_serdata) -> u32;
}
extern "C" {
#[link_name = "<prefix>ddsi_serdata_to_ser_ref"]
pub fn ddsi_serdata_to_ser_ref(
d: *const ddsi_serdata,
off: usize,
sz: usize,
ref_: *mut ddsrt_iovec_t,
) -> *mut ddsi_serdata;
}
extern "C" {
#[link_name = "<prefix>ddsi_serdata_unref"]
pub fn ddsi_serdata_unref(serdata: *mut ddsi_serdata);
}
extern "C" {
#[link_name = "<prefix>ddsi_serdata_to_ser_unref"]
pub fn ddsi_serdata_to_ser_unref(d: *mut ddsi_serdata, ref_: *const ddsrt_iovec_t);
}
extern "C" {
#[link_name = "<prefix>ddsi_serdata_from_ser_iov"]
pub fn ddsi_serdata_from_ser_iov(
type_: *const ddsi_sertype,
kind: ddsi_serdata_kind,
niov: ddsrt_msg_iovlen_t,
iov: *const ddsrt_iovec_t,
size: usize,
) -> *mut ddsi_serdata;
}
extern "C" {
#[link_name = "<prefix>ddsi_serdata_from_sample"]
pub fn ddsi_serdata_from_sample(
type_: *const ddsi_sertype,
kind: ddsi_serdata_kind,
sample: *const ::std::os::raw::c_void,
) -> *mut ddsi_serdata;
}
37 changes: 2 additions & 35 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,38 +47,5 @@ mod bindings {
}
pub use bindings::*;

/* Additional wrapper functions for select exported inline functions */

extern "C" {
pub fn ddsi_serdata_size(d: *const ddsi_serdata) -> u32;
}
extern "C" {
pub fn ddsi_serdata_to_ser_ref(
d: *const ddsi_serdata,
off: usize,
sz: usize,
ref_: *mut ddsrt_iovec_t,
) -> *mut ddsi_serdata;
}
extern "C" {
pub fn ddsi_serdata_unref(serdata: *mut ddsi_serdata);
}
extern "C" {
pub fn ddsi_serdata_to_ser_unref(d: *mut ddsi_serdata, ref_: *const ddsrt_iovec_t);
}
extern "C" {
pub fn ddsi_serdata_from_ser_iov(
type_: *const ddsi_sertype,
kind: ddsi_serdata_kind,
niov: ddsrt_msg_iovlen_t,
iov: *const ddsrt_iovec_t,
size: usize,
) -> *mut ddsi_serdata;
}
extern "C" {
pub fn ddsi_serdata_from_sample(
type_: *const ddsi_sertype,
kind: ddsi_serdata_kind,
sample: *const ::std::os::raw::c_void,
) -> *mut ddsi_serdata;
}
// Include the generated additional wrapper functions from OUT_DIR
include!(concat!(env!("OUT_DIR"), "/functions.rs"));

0 comments on commit 700d028

Please sign in to comment.