diff --git a/iceoryx2-ffi/ffi-macros/src/lib.rs b/iceoryx2-ffi/ffi-macros/src/lib.rs index dab4ed51..2000023b 100644 --- a/iceoryx2-ffi/ffi-macros/src/lib.rs +++ b/iceoryx2-ffi/ffi-macros/src/lib.rs @@ -128,26 +128,26 @@ pub fn iceoryx2_ffi(args: TokenStream, input: TokenStream) -> TokenStream { #my_struct impl #struct_name { - pub(crate) fn as_handle(&mut self) -> #struct_h_name { + pub(super) fn as_handle(&mut self) -> #struct_h_name { self as *mut _ as _ } - pub(crate) fn as_ref_handle(&mut self) -> #struct_ref_h_name { + pub(super) fn as_ref_handle(&mut self) -> #struct_ref_h_name { self as *mut _ as _ } - pub(crate) fn take(&mut self) -> Option<#my_type> { + pub(super) fn take(&mut self) -> Option<#my_type> { unsafe { self.value.as_option_mut().take() } } - pub(crate) fn set(&mut self, value: #my_type) { + pub(super) fn set(&mut self, value: #my_type) { unsafe { *self.value.as_option_mut() = Some(value) } } - pub(crate) fn alloc() -> *mut #struct_name { + pub(super) fn alloc() -> *mut #struct_name { unsafe { ::std::alloc::alloc(::std::alloc::Layout::new::<#struct_name>()) as _ } } - pub(crate) fn dealloc(storage: *mut #struct_name) { + pub(super) fn dealloc(storage: *mut #struct_name) { unsafe { ::std::alloc::dealloc(storage as _, ::core::alloc::Layout::new::<#struct_name>()) } diff --git a/iceoryx2-ffi/ffi/README.md b/iceoryx2-ffi/ffi/README.md index 4d440a72..396cceee 100644 --- a/iceoryx2-ffi/ffi/README.md +++ b/iceoryx2-ffi/ffi/README.md @@ -69,3 +69,22 @@ outside of this crate. The opaque types additionally need to be manually forward - renaming is done in `[export.rename]` with `"Foo" = "iox2_foo_ptr_t"` - forward declaration is done in the `after_includes` section with `typedef struct iox2_foo_ptr_t iox2_foo_ptr_t;` + +## Why the folder structure with 'api' and 'test' + +As it turned out `cdylib`s do not play well with integration tests. The `cdylib` is build +with `panic="abort"` but the tests require `panic="unwind"`. This results in building the lib +twice if there are integration tests and leads to the following warning and eventually to build failures. + +> warning: output filename collision. +> The lib target `iceoryx2_ffi` in package `iceoryx2-ffi v0.3.0 (C:\Users\ekxide\iceoryx2\iceoryx2-ffi\ffi)` +> has the same output filename as the lib target `iceoryx2_ffi` in package +> `iceoryx2-ffi v0.3.0 (C:\Users\ekxide\iceoryx2\iceoryx2-ffi\ffi)`. +> Colliding filename is: C:\Users\ekxide\iceoryx2\target\release\deps\iceoryx2_ffi.lib +> The targets should have unique names. +> Consider changing their names to be unique or compiling them separately. +> This may become a hard error in the future; see . + +As a workaround, the integrationtests are placed in the module. This would give access to private API though. To circumvent this problem, +only `pub(super)` shall be used if an API needs to be available in other modules but not `pub(crate)`. With the chosen folder +structure the tests can again only be written as whitebox tests. diff --git a/iceoryx2-ffi/ffi/src/config.rs b/iceoryx2-ffi/ffi/src/api/config.rs similarity index 100% rename from iceoryx2-ffi/ffi/src/config.rs rename to iceoryx2-ffi/ffi/src/api/config.rs diff --git a/iceoryx2-ffi/ffi/src/api/mod.rs b/iceoryx2-ffi/ffi/src/api/mod.rs new file mode 100644 index 00000000..feb04bff --- /dev/null +++ b/iceoryx2-ffi/ffi/src/api/mod.rs @@ -0,0 +1,135 @@ +// Copyright (c) 2024 Contributors to the Eclipse Foundation +// +// See the NOTICE file(s) distributed with this work for additional +// information regarding copyright ownership. +// +// This program and the accompanying materials are made available under the +// terms of the Apache Software License 2.0 which is available at +// https://www.apache.org/licenses/LICENSE-2.0, or the MIT license +// which is available at https://opensource.org/licenses/MIT. +// +// SPDX-License-Identifier: Apache-2.0 OR MIT + +#![allow(non_camel_case_types)] + +use iceoryx2::prelude::*; +use iceoryx2_bb_container::semantic_string::SemanticStringError; +use iceoryx2_bb_log::set_log_level; + +use core::ffi::c_int; + +mod config; +mod node; +mod node_builder; +mod node_name; +mod publisher; +mod service; +mod service_builder; +mod subscriber; + +pub use config::*; +pub use node::*; +pub use node_builder::*; +pub use node_name::*; +pub use publisher::*; +pub use service::*; +pub use service_builder::*; +pub use subscriber::*; + +/// This constant signals an successful function call +pub const IOX2_OK: c_int = 0; + +#[repr(C)] +#[derive(Copy, Clone)] +pub enum iox2_callback_progression_e { + STOP = 0, + CONTINUE, +} + +impl From for CallbackProgression { + fn from(value: iox2_callback_progression_e) -> Self { + match value { + iox2_callback_progression_e::STOP => CallbackProgression::Stop, + iox2_callback_progression_e::CONTINUE => CallbackProgression::Continue, + } + } +} + +#[repr(C)] +#[derive(Copy, Clone)] +pub enum iox2_semantic_string_error_e { + INVALID_CONTENT = IOX2_OK as isize + 1, + EXCEEDS_MAXIMUM_LENGTH, +} + +impl IntoCInt for SemanticStringError { + fn into_c_int(self) -> c_int { + (match self { + SemanticStringError::InvalidContent => iox2_semantic_string_error_e::INVALID_CONTENT, + SemanticStringError::ExceedsMaximumLength => { + iox2_semantic_string_error_e::EXCEEDS_MAXIMUM_LENGTH + } + }) as c_int + } +} + +#[no_mangle] +pub extern "C" fn zero_copy_service_list() -> i32 { + set_log_level(iceoryx2_bb_log::LogLevel::Info); + + let callback = |service| { + println!("\n{:#?}", service?); + Ok(CallbackProgression::Continue) + }; + + match zero_copy::Service::list(Config::global_config(), callback) { + Ok(_) => 0, + Err(_) => -1, + } +} + +/// This is a trait to convert a Rust error enum into the corresponding C error enum and then to a c_int in one go +/// +/// # Example +/// +/// ```no_run +/// use core::ffi::c_int; +/// +/// trait IntoCInt { +/// fn into_c_int(self) -> c_int; +/// } +/// +/// enum FooError { +/// BAR, +/// BAZ +/// } +/// +/// #[repr(C)] +/// #[derive(Copy, Clone)] +/// pub enum iox2_foo_error_e { +/// BAR = 1, // start at 1 since IOX2_OK is already 0 +/// BAZ, +/// } +/// +/// impl IntoCInt for FooError { +/// fn into_c_int(self) -> c_int { +/// (match self { +/// FooError::BAR => iox2_foo_error_e::BAR, +/// FooError::BAZ => iox2_foo_error_e::BAZ, +/// }) as c_int +/// } +/// } +/// ``` +trait IntoCInt { + fn into_c_int(self) -> c_int; +} + +trait HandleToType { + type Target; + + // NOTE in this case, the handle `self` is already a `*mut`. Passing by value means a copy + // of the pointer; passing by reference make the implementation more error prone since one + // has to remember to de-reference `self` in order to get the `*mut` + #[allow(clippy::wrong_self_convention)] + fn as_type(self) -> Self::Target; +} diff --git a/iceoryx2-ffi/ffi/src/node.rs b/iceoryx2-ffi/ffi/src/api/node.rs similarity index 97% rename from iceoryx2-ffi/ffi/src/node.rs rename to iceoryx2-ffi/ffi/src/api/node.rs index 0d67fa3a..54cbe30b 100644 --- a/iceoryx2-ffi/ffi/src/node.rs +++ b/iceoryx2-ffi/ffi/src/api/node.rs @@ -12,7 +12,7 @@ #![allow(non_camel_case_types)] -use crate::{ +use crate::api::{ iox2_callback_progression_e, iox2_config_ptr, iox2_node_name_ptr, iox2_service_builder_h, iox2_service_builder_t, iox2_service_name_h, iox2_service_type_e, HandleToType, IntoCInt, IOX2_OK, @@ -48,18 +48,18 @@ impl IntoCInt for NodeListFailure { } } -pub(crate) union NodeUnion { +pub(super) union NodeUnion { ipc: ManuallyDrop>, local: ManuallyDrop>, } impl NodeUnion { - pub(crate) fn new_ipc(node: Node) -> Self { + pub(super) fn new_ipc(node: Node) -> Self { Self { ipc: ManuallyDrop::new(node), } } - pub(crate) fn new_local(node: Node) -> Self { + pub(super) fn new_local(node: Node) -> Self { Self { local: ManuallyDrop::new(node), } @@ -75,13 +75,13 @@ pub struct iox2_node_storage_t { #[repr(C)] #[iceoryx2_ffi(NodeUnion)] pub struct iox2_node_t { - pub(crate) service_type: iox2_service_type_e, - pub(crate) value: iox2_node_storage_t, - pub(crate) deleter: fn(*mut iox2_node_t), + pub(super) service_type: iox2_service_type_e, + pub(super) value: iox2_node_storage_t, + pub(super) deleter: fn(*mut iox2_node_t), } impl iox2_node_t { - pub(crate) fn init( + pub(super) fn init( &mut self, service_type: iox2_service_type_e, value: NodeUnion, diff --git a/iceoryx2-ffi/ffi/src/node_builder.rs b/iceoryx2-ffi/ffi/src/api/node_builder.rs similarity index 99% rename from iceoryx2-ffi/ffi/src/node_builder.rs rename to iceoryx2-ffi/ffi/src/api/node_builder.rs index 3b5af15c..a679b384 100644 --- a/iceoryx2-ffi/ffi/src/node_builder.rs +++ b/iceoryx2-ffi/ffi/src/api/node_builder.rs @@ -12,7 +12,7 @@ #![allow(non_camel_case_types)] -use crate::{ +use crate::api::{ iox2_node_h, iox2_node_name_drop, iox2_node_name_h, iox2_node_t, iox2_service_type_e, HandleToType, IntoCInt, NodeUnion, IOX2_OK, }; diff --git a/iceoryx2-ffi/ffi/src/node_name.rs b/iceoryx2-ffi/ffi/src/api/node_name.rs similarity index 98% rename from iceoryx2-ffi/ffi/src/node_name.rs rename to iceoryx2-ffi/ffi/src/api/node_name.rs index 9bd3252e..e064ff84 100644 --- a/iceoryx2-ffi/ffi/src/node_name.rs +++ b/iceoryx2-ffi/ffi/src/api/node_name.rs @@ -12,7 +12,7 @@ #![allow(non_camel_case_types)] -use crate::{iox2_semantic_string_error_e, HandleToType, IntoCInt, IOX2_OK}; +use crate::api::{iox2_semantic_string_error_e, HandleToType, IntoCInt, IOX2_OK}; use iceoryx2::prelude::*; use iceoryx2_bb_elementary::static_assert::*; @@ -32,7 +32,7 @@ pub struct iox2_node_name_storage_t { #[repr(C)] #[iceoryx2_ffi(NodeName)] pub struct iox2_node_name_t { - value: iox2_node_name_storage_t, + pub value: iox2_node_name_storage_t, deleter: fn(*mut iox2_node_name_t), } diff --git a/iceoryx2-ffi/ffi/src/publisher.rs b/iceoryx2-ffi/ffi/src/api/publisher.rs similarity index 100% rename from iceoryx2-ffi/ffi/src/publisher.rs rename to iceoryx2-ffi/ffi/src/api/publisher.rs diff --git a/iceoryx2-ffi/ffi/src/service.rs b/iceoryx2-ffi/ffi/src/api/service.rs similarity index 100% rename from iceoryx2-ffi/ffi/src/service.rs rename to iceoryx2-ffi/ffi/src/api/service.rs diff --git a/iceoryx2-ffi/ffi/src/service_builder.rs b/iceoryx2-ffi/ffi/src/api/service_builder.rs similarity index 100% rename from iceoryx2-ffi/ffi/src/service_builder.rs rename to iceoryx2-ffi/ffi/src/api/service_builder.rs diff --git a/iceoryx2-ffi/ffi/src/subscriber.rs b/iceoryx2-ffi/ffi/src/api/subscriber.rs similarity index 100% rename from iceoryx2-ffi/ffi/src/subscriber.rs rename to iceoryx2-ffi/ffi/src/api/subscriber.rs diff --git a/iceoryx2-ffi/ffi/src/lib.rs b/iceoryx2-ffi/ffi/src/lib.rs index f0429897..eb2d5f99 100644 --- a/iceoryx2-ffi/ffi/src/lib.rs +++ b/iceoryx2-ffi/ffi/src/lib.rs @@ -12,124 +12,8 @@ #![allow(non_camel_case_types)] -use iceoryx2::prelude::*; -use iceoryx2_bb_container::semantic_string::SemanticStringError; -use iceoryx2_bb_log::set_log_level; +mod api; +pub use api::*; -use core::ffi::c_int; - -mod config; -mod node; -mod node_builder; -mod node_name; -mod publisher; -mod service; -mod service_builder; -mod subscriber; - -pub use config::*; -pub use node::*; -pub use node_builder::*; -pub use node_name::*; -pub use publisher::*; -pub use service::*; -pub use service_builder::*; -pub use subscriber::*; - -/// This constant signals an successful function call -pub const IOX2_OK: c_int = 0; - -#[repr(C)] -#[derive(Copy, Clone)] -pub enum iox2_callback_progression_e { - STOP = 0, - CONTINUE, -} - -impl From for CallbackProgression { - fn from(value: iox2_callback_progression_e) -> Self { - match value { - iox2_callback_progression_e::STOP => CallbackProgression::Stop, - iox2_callback_progression_e::CONTINUE => CallbackProgression::Continue, - } - } -} - -#[repr(C)] -#[derive(Copy, Clone)] -pub enum iox2_semantic_string_error_e { - INVALID_CONTENT = IOX2_OK as isize + 1, - EXCEEDS_MAXIMUM_LENGTH, -} - -impl IntoCInt for SemanticStringError { - fn into_c_int(self) -> c_int { - (match self { - SemanticStringError::InvalidContent => iox2_semantic_string_error_e::INVALID_CONTENT, - SemanticStringError::ExceedsMaximumLength => { - iox2_semantic_string_error_e::EXCEEDS_MAXIMUM_LENGTH - } - }) as c_int - } -} - -#[no_mangle] -pub extern "C" fn zero_copy_service_list() -> i32 { - set_log_level(iceoryx2_bb_log::LogLevel::Info); - - let callback = |service| { - println!("\n{:#?}", service?); - Ok(CallbackProgression::Continue) - }; - - match zero_copy::Service::list(Config::global_config(), callback) { - Ok(_) => 0, - Err(_) => -1, - } -} - -/// This is a trait to convert a Rust error enum into the corresponding C error enum and then to a c_int in one go -/// -/// # Example -/// -/// ```no_run -/// use core::ffi::c_int; -/// -/// trait IntoCInt { -/// fn into_c_int(self) -> c_int; -/// } -/// -/// enum FooError { -/// BAR, -/// BAZ -/// } -/// -/// #[repr(C)] -/// #[derive(Copy, Clone)] -/// pub enum iox2_foo_error_e { -/// BAR = 1, // start at 1 since IOX2_OK is already 0 -/// BAZ, -/// } -/// -/// impl IntoCInt for FooError { -/// fn into_c_int(self) -> c_int { -/// (match self { -/// FooError::BAR => iox2_foo_error_e::BAR, -/// FooError::BAZ => iox2_foo_error_e::BAZ, -/// }) as c_int -/// } -/// } -/// ``` -trait IntoCInt { - fn into_c_int(self) -> c_int; -} - -pub trait HandleToType { - type Target; - - // NOTE in this case, the handle `self` is already a `*mut`. Passing by value means a copy - // of the pointer; passing by reference make the implementation more error prone since one - // has to remember to de-reference `self` in order to get the `*mut` - #[allow(clippy::wrong_self_convention)] - fn as_type(self) -> Self::Target; -} +#[cfg(test)] +mod tests; diff --git a/iceoryx2-ffi/ffi/tests/common.rs b/iceoryx2-ffi/ffi/src/tests/mod.rs similarity index 73% rename from iceoryx2-ffi/ffi/tests/common.rs rename to iceoryx2-ffi/ffi/src/tests/mod.rs index e20232da..55594601 100644 --- a/iceoryx2-ffi/ffi/tests/common.rs +++ b/iceoryx2-ffi/ffi/src/tests/mod.rs @@ -10,13 +10,15 @@ // // SPDX-License-Identifier: Apache-2.0 OR MIT -#![allow(dead_code)] +mod node_builder_tests; +mod node_name_tests; +mod node_tests; +use crate::*; use iceoryx2::prelude::*; use iceoryx2_bb_testing::assert_that; -use iceoryx2_ffi::*; -pub(crate) trait ServiceTypeMapping { +trait ServiceTypeMapping { fn service_type() -> iox2_service_type_e; } @@ -32,30 +34,29 @@ impl ServiceTypeMapping for iceoryx2::service::process_local::Service { } } -#[cfg(test)] -pub(crate) fn create_node(node_name: &str) -> iox2_node_h { +fn create_node(node_name: &str) -> iox2_node_h { unsafe { let node_builder_handle = iox2_node_builder_new(std::ptr::null_mut()); let mut node_name_handle = std::ptr::null_mut(); let ret_val = iox2_node_name_new( std::ptr::null_mut(), - node_name.as_ptr() as *const _, - node_name.len() as _, - &mut node_name_handle, + node_name.as_ptr() as *const _, + node_name.len() as _, + &mut node_name_handle, ); assert_that!(ret_val, eq(IOX2_OK)); iox2_node_builder_set_name( iox2_cast_node_builder_ref_h(node_builder_handle), - node_name_handle, + node_name_handle, ); let mut node_handle: iox2_node_h = std::ptr::null_mut(); let ret_val = iox2_node_builder_create( node_builder_handle, std::ptr::null_mut(), - S::service_type(), - &mut node_handle as *mut iox2_node_h, + S::service_type(), + &mut node_handle as *mut iox2_node_h, ); assert_that!(ret_val, eq(IOX2_OK)); diff --git a/iceoryx2-ffi/ffi/tests/node_builder_tests.rs b/iceoryx2-ffi/ffi/src/tests/node_builder_tests.rs similarity index 90% rename from iceoryx2-ffi/ffi/tests/node_builder_tests.rs rename to iceoryx2-ffi/ffi/src/tests/node_builder_tests.rs index 5497c725..1980eaea 100644 --- a/iceoryx2-ffi/ffi/tests/node_builder_tests.rs +++ b/iceoryx2-ffi/ffi/src/tests/node_builder_tests.rs @@ -10,15 +10,9 @@ // // SPDX-License-Identifier: Apache-2.0 OR MIT -mod common; - #[generic_tests::define] mod node_builder { - - use crate::common::*; - use iceoryx2::prelude::*; - use iceoryx2_bb_testing::assert_that; - use iceoryx2_ffi::*; + use crate::tests::*; #[test] fn basic_node_builder_api_test() { diff --git a/iceoryx2-ffi/ffi/tests/node_name_tests.rs b/iceoryx2-ffi/ffi/src/tests/node_name_tests.rs similarity index 93% rename from iceoryx2-ffi/ffi/tests/node_name_tests.rs rename to iceoryx2-ffi/ffi/src/tests/node_name_tests.rs index dfb49308..3c19b509 100644 --- a/iceoryx2-ffi/ffi/tests/node_name_tests.rs +++ b/iceoryx2-ffi/ffi/src/tests/node_name_tests.rs @@ -10,9 +10,7 @@ // // SPDX-License-Identifier: Apache-2.0 OR MIT -use iceoryx2::prelude::*; -use iceoryx2_bb_testing::assert_that; -use iceoryx2_ffi::*; +use crate::tests::*; use core::{slice, str}; @@ -43,6 +41,8 @@ fn basic_node_name_test() -> Result<(), Box> { iox2_node_name_drop(node_name_handle); + let _foo = &(*(node_name_handle as *mut _ as *mut iox2_node_name_t)).value; + Ok(()) } } diff --git a/iceoryx2-ffi/ffi/tests/node_tests.rs b/iceoryx2-ffi/ffi/src/tests/node_tests.rs similarity index 96% rename from iceoryx2-ffi/ffi/tests/node_tests.rs rename to iceoryx2-ffi/ffi/src/tests/node_tests.rs index 8a90f46c..93b80bda 100644 --- a/iceoryx2-ffi/ffi/tests/node_tests.rs +++ b/iceoryx2-ffi/ffi/src/tests/node_tests.rs @@ -10,16 +10,9 @@ // // SPDX-License-Identifier: Apache-2.0 OR MIT -mod common; - #[generic_tests::define] mod node { - - use crate::common::*; - - use iceoryx2::prelude::*; - use iceoryx2_bb_testing::assert_that; - use iceoryx2_ffi::*; + use crate::tests::*; use core::{slice, str};