Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow multiple versions of the Cyclors library to be loaded simultaneously on Linux systems. #25

Merged
merged 5 commits into from
Oct 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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.3.1"
version = "0.3.2"
authors = ["kydos <angelo@icorsaro.net>"]
license = "Apache-2.0"
readme = "README.md"
Expand Down
188 changes: 186 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 @@ -143,17 +150,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 @@ -48,38 +48,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"));
Loading