From be8a4407e3ae6b6a414a520842891856ce59b367 Mon Sep 17 00:00:00 2001 From: Arthur LE MOIGNE Date: Mon, 27 Nov 2023 10:31:53 +0100 Subject: [PATCH 1/3] =?UTF-8?q?=E2=9C=A8=20Add=20support=20for=20SQL=20mes?= =?UTF-8?q?sage=20store?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CMakeLists.txt | 16 +++++++ quickfix-bind/CMakeLists.txt | 12 +++++- quickfix-bind/include/quickfix_bind.h | 12 ++++++ quickfix-bind/src/quickfix_bind.cpp | 36 +++++++++++++--- quickfix-ffi/Cargo.toml | 2 + quickfix-ffi/build.rs | 43 ++++++++++++++----- quickfix-ffi/src/lib.rs | 10 +++++ quickfix/Cargo.toml | 2 + quickfix/src/lib.rs | 4 ++ quickfix/src/message_store_factory.rs | 6 +++ quickfix/src/message_store_factory/mysql.rs | 30 +++++++++++++ .../src/message_store_factory/postgres.rs | 30 +++++++++++++ quickfix/tests/test_lifecycle.rs | 11 ----- quickfix/tests/test_message_store.rs | 26 +++++++++++ 14 files changed, 210 insertions(+), 30 deletions(-) create mode 100644 quickfix/src/message_store_factory/mysql.rs create mode 100644 quickfix/src/message_store_factory/postgres.rs create mode 100644 quickfix/tests/test_message_store.rs diff --git a/CMakeLists.txt b/CMakeLists.txt index 3cc03cb..81f24ba 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,8 +3,24 @@ project(QuickFixBind) set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED True) +set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/libquickfix/cmake/") + +option(HAVE_MYSQL "Build with MySQL" OFF) +option(HAVE_POSTGRESQL "Build with PostgreSQL" OFF) option(QUICKFIX_BIND_EXAMPLES "Build quickfix C binding examples" ON) option(WITH_PRINT_EX_STDOUT, "Enable exception printing to stdout" OFF) +if (HAVE_MYSQL) + find_package(MySQL REQUIRED) + include_directories(${MYSQL_INCLUDE_DIR}) + message("-- Building with MySQL") +endif() + +if (HAVE_POSTGRESQL) + find_package(PostgreSQL REQUIRED) + include_directories(${PostgreSQL_INCLUDE_DIRS}) + message("-- Building with POSTGRESQL") +endif() + add_subdirectory(quickfix-bind) diff --git a/quickfix-bind/CMakeLists.txt b/quickfix-bind/CMakeLists.txt index bf80e7d..ddd1cb3 100644 --- a/quickfix-bind/CMakeLists.txt +++ b/quickfix-bind/CMakeLists.txt @@ -4,19 +4,27 @@ include_directories(include) add_library(quickfixbind STATIC src/quickfix_bind.cpp ) -target_link_libraries(quickfixbind PRIVATE quickfix) +target_link_libraries(quickfixbind ${MYSQL_CLIENT_LIBS} ${PostgreSQL_LIBRARIES} quickfix) # Add option if asked if (WITH_PRINT_EX_STDOUT) target_compile_definitions(quickfixbind PRIVATE "PRINT_QUICKFIX_EX_STDOUT=1") endif() +if (HAVE_MYSQL) + target_compile_definitions(quickfixbind PRIVATE "HAVE_MYSQL=1") +endif() + +if (HAVE_POSTGRESQL) + target_compile_definitions(quickfixbind PRIVATE "HAVE_POSTGRESQL=1") +endif() + # Add example if asked if(QUICKFIX_BIND_EXAMPLES) add_executable(demo_basic_binding examples/demo_basic_binding.c ) -target_link_libraries(demo_basic_binding PRIVATE quickfixbind) +target_link_libraries(demo_basic_binding ${MYSQL_CLIENT_LIBS} ${PostgreSQL_LIBRARIES} quickfixbind) endif() # Configure install target diff --git a/quickfix-bind/include/quickfix_bind.h b/quickfix-bind/include/quickfix_bind.h index c633bc9..0bc4941 100644 --- a/quickfix-bind/include/quickfix_bind.h +++ b/quickfix-bind/include/quickfix_bind.h @@ -8,6 +8,9 @@ #define PRINT_QUICKFIX_EX_STDOUT 0 #endif // PRINT_QUICKFIX_EX_STDOUT +// #define HAVE_MYSQL +// #define HAVE_POSTGRESQL + #define ERRNO_INVAL -1 #define ERRNO_EXCEPTION -2 #define ERRNO_BUFFER_TO_SMALL -3 @@ -76,6 +79,15 @@ void FixDataDictionary_delete(FixDataDictionary_t *obj); FixMessageStoreFactory_t *FixFileMessageStoreFactory_new(const FixSessionSettings_t *settings); FixMessageStoreFactory_t *FixMemoryMessageStoreFactory_new(); + +#ifdef HAVE_MYSQL +FixMessageStoreFactory_t *FixMysqlMessageStoreFactory_new(const FixSessionSettings_t *settings); +#endif // HAVE_MYSQL + +#ifdef HAVE_POSTGRESQL +FixMessageStoreFactory_t *FixPostgresMessageStoreFactory_new(const FixSessionSettings_t *settings); +#endif // HAVE_POSTGRESQL + void FixMessageStoreFactory_delete(FixMessageStoreFactory_t *obj); FixLogFactory_t *FixLogFactory_new(const void *data, const FixLogCallbacks_t *callbacks); diff --git a/quickfix-bind/src/quickfix_bind.cpp b/quickfix-bind/src/quickfix_bind.cpp index 0ef76c8..45f1271 100644 --- a/quickfix-bind/src/quickfix_bind.cpp +++ b/quickfix-bind/src/quickfix_bind.cpp @@ -15,6 +15,14 @@ #include #include +#ifdef HAVE_MYSQL +#include +#endif // HAVE_MYSQL + +#ifdef HAVE_POSTGRESQL +#include +#endif // HAVE_POSTGRESQL + #define RETURN_IF_NULL(_OBJ_) \ if ((_OBJ_) == nullptr) \ return; @@ -394,15 +402,31 @@ void FixDataDictionary_delete(FixDataDictionary_t *obj) { FixMessageStoreFactory_t *FixFileMessageStoreFactory_new(const FixSessionSettings_t *settings) { RETURN_VAL_IF_NULL(settings, NULL); - CATCH_OR_RETURN_NULL({ - auto fix_settings = (FIX::SessionSettings *)(settings); - return (FixMessageStoreFactory_t *)(new FIX::FileStoreFactory(*fix_settings)); - }); + + auto fix_settings = (FIX::SessionSettings *)(settings); + CATCH_OR_RETURN_NULL({ return (FixMessageStoreFactory_t *)(new FIX::FileStoreFactory(*fix_settings)); }); } -FixMessageStoreFactory_t *FixMemoryMessageStoreFactory_new() { - CATCH_OR_RETURN_NULL({ return (FixMessageStoreFactory_t *)(new FIX::MemoryStoreFactory()); }) +FixMessageStoreFactory_t *FixMemoryMessageStoreFactory_new(){ + CATCH_OR_RETURN_NULL({ return (FixMessageStoreFactory_t *)(new FIX::MemoryStoreFactory()); })} + +#ifdef HAVE_MYSQL +FixMessageStoreFactory_t *FixMysqlMessageStoreFactory_new(const FixSessionSettings_t *settings) { + RETURN_VAL_IF_NULL(settings, NULL); + + auto fix_settings = (FIX::SessionSettings *)(settings); + CATCH_OR_RETURN_NULL({ return (FixMessageStoreFactory_t *)(new FIX::MySQLStoreFactory(*fix_settings)); }); +} +#endif // HAVE_MYSQL + +#ifdef HAVE_POSTGRESQL +FixMessageStoreFactory_t *FixPostgresMessageStoreFactory_new(const FixSessionSettings_t *settings) { + RETURN_VAL_IF_NULL(settings, NULL); + + auto fix_settings = (FIX::SessionSettings *)(settings); + CATCH_OR_RETURN_NULL({ return (FixMessageStoreFactory_t *)(new FIX::PostgreSQLStoreFactory(*fix_settings)); }); } +#endif // HAVE_POSTGRESQL void FixMessageStoreFactory_delete(FixMessageStoreFactory_t *obj) { RETURN_IF_NULL(obj); diff --git a/quickfix-ffi/Cargo.toml b/quickfix-ffi/Cargo.toml index 2e937b8..010ad94 100644 --- a/quickfix-ffi/Cargo.toml +++ b/quickfix-ffi/Cargo.toml @@ -11,3 +11,5 @@ cmake = "0.1.50" [features] default = [] print-ex = [] +build-with-mysql = [] +build-with-postgres = [] diff --git a/quickfix-ffi/build.rs b/quickfix-ffi/build.rs index 228cbd5..41632f2 100644 --- a/quickfix-ffi/build.rs +++ b/quickfix-ffi/build.rs @@ -2,6 +2,22 @@ use std::env; use cmake::Config; +fn have_feature(flag: &str) -> bool { + env::var(&format!( + "CARGO_FEATURE_{}", + flag.replace('-', "_").to_uppercase() + )) + .is_ok() +} + +fn read_cmake_opt(flag: &str) -> &'static str { + if have_feature(flag) { + "ON" + } else { + "OFF" + } +} + fn main() { // Tell Cargo that if the given file changes, to rerun this build script. println!("cargo:rerun-if-changed=../CMakeLists.txt"); @@ -11,6 +27,9 @@ fn main() { // Build quickfix as a static library let quickfix_dst = Config::new("../libquickfix") + .define("HAVE_SSL", "OFF") + .define("HAVE_MYSQL", read_cmake_opt("build-with-mysql")) + .define("HAVE_POSTGRESQL", read_cmake_opt("build-with-postgres")) .define("HAVE_PYTHON", "OFF") .define("HAVE_PYTHON3", "OFF") .define("QUICKFIX_SHARED_LIBS", "OFF") @@ -24,19 +43,14 @@ fn main() { // Build quickfix C bind also as a static library. env::set_var("CMAKE_LIBRARY_PATH", [quickfix_lib_path].join(";")); - let mut config = Config::new(".."); - config + let quickfix_bind_dst = Config::new("..") .cflag(format!("-I{quickfix_include_path}")) .cxxflag(format!("-I{quickfix_include_path}")) - .define("QUICKFIX_BIND_EXAMPLES", "OFF"); - - // Enable optional features - if env::var("CARGO_FEATURE_PRINT_EX").is_ok() { - config.define("WITH_PRINT_EX_STDOUT", "ON"); - } - - // Trigger build - let quickfix_bind_dst = config.build(); + .define("QUICKFIX_BIND_EXAMPLES", "OFF") + .define("HAVE_MYSQL", read_cmake_opt("build-with-mysql")) + .define("HAVE_POSTGRESQL", read_cmake_opt("build-with-postgres")) + .define("WITH_PRINT_EX_STDOUT", read_cmake_opt("print-ex")) + .build(); // Configure rustc. println!( @@ -50,4 +64,11 @@ fn main() { println!("cargo:rustc-link-lib=static=quickfix"); println!("cargo:rustc-link-lib=static=quickfixbind"); println!("cargo:rustc-link-lib=stdc++"); + + if have_feature("build-with-mysql") { + println!("cargo:rustc-link-lib=mysqlclient"); + } + if have_feature("build-with-postgres") { + println!("cargo:rustc-link-lib=pq"); + } } diff --git a/quickfix-ffi/src/lib.rs b/quickfix-ffi/src/lib.rs index 9fc740c..7ecfb13 100644 --- a/quickfix-ffi/src/lib.rs +++ b/quickfix-ffi/src/lib.rs @@ -183,6 +183,16 @@ extern "C" { pub fn FixMemoryMessageStoreFactory_new() -> Option; + #[cfg(feature = "build-with-mysql")] + pub fn FixMysqlMessageStoreFactory_new( + settings: FixSessionSettings_t, + ) -> Option; + + #[cfg(feature = "build-with-postgres")] + pub fn FixPostgresMessageStoreFactory_new( + settings: FixSessionSettings_t, + ) -> Option; + pub fn FixMessageStoreFactory_delete(obj: FixMessageStoreFactory_t); // Log factory diff --git a/quickfix/Cargo.toml b/quickfix/Cargo.toml index 89103d4..4cf363c 100644 --- a/quickfix/Cargo.toml +++ b/quickfix/Cargo.toml @@ -14,4 +14,6 @@ colored = "2.0.4" [features] default = ["log"] print-ex = ["quickfix-ffi/print-ex"] +build-with-mysql = ["quickfix-ffi/build-with-mysql"] +build-with-postgres = ["quickfix-ffi/build-with-postgres"] log = ["dep:log"] diff --git a/quickfix/src/lib.rs b/quickfix/src/lib.rs index 4c44ca9..1b6caa1 100644 --- a/quickfix/src/lib.rs +++ b/quickfix/src/lib.rs @@ -44,6 +44,10 @@ pub use trailer::Trailer; #[cfg(feature = "log")] pub use log_factory::RustLogger; +#[cfg(feature = "build-with-mysql")] +pub use message_store_factory::mysql::MySqlMessageStoreFactory; +#[cfg(feature = "build-with-postgres")] +pub use message_store_factory::postgres::PostgresMessageStoreFactory; /// Permit control of an underlying socket connection. pub trait ConnectionHandler { diff --git a/quickfix/src/message_store_factory.rs b/quickfix/src/message_store_factory.rs index e87446b..4824cb8 100644 --- a/quickfix/src/message_store_factory.rs +++ b/quickfix/src/message_store_factory.rs @@ -5,6 +5,12 @@ use quickfix_ffi::{ use crate::{QuickFixError, SessionSettings}; +#[cfg(feature = "build-with-mysql")] +pub mod mysql; + +#[cfg(feature = "build-with-postgres")] +pub mod postgres; + /// Object can be converted as a foreign object representing a `MessageStore`. pub trait FfiMessageStoreFactory { /// Get a representation of the message store as a FFI pointer. diff --git a/quickfix/src/message_store_factory/mysql.rs b/quickfix/src/message_store_factory/mysql.rs new file mode 100644 index 0000000..df7d3d0 --- /dev/null +++ b/quickfix/src/message_store_factory/mysql.rs @@ -0,0 +1,30 @@ +use quickfix_ffi::{ + FixMessageStoreFactory_delete, FixMessageStoreFactory_t, FixMysqlMessageStoreFactory_new, +}; + +use crate::{FfiMessageStoreFactory, QuickFixError, SessionSettings}; + +/// MySQL based implementation of `MessageStore`. +#[derive(Debug)] +pub struct MySqlMessageStoreFactory(FixMessageStoreFactory_t); + +impl MySqlMessageStoreFactory { + /// Try to create new struct from settings. + pub fn try_new(settings: &SessionSettings) -> Result { + unsafe { FixMysqlMessageStoreFactory_new(settings.0) } + .map(Self) + .ok_or(QuickFixError::NullFunctionReturn) + } +} + +impl FfiMessageStoreFactory for MySqlMessageStoreFactory { + fn as_ffi_ptr(&self) -> FixMessageStoreFactory_t { + self.0 + } +} + +impl Drop for MySqlMessageStoreFactory { + fn drop(&mut self) { + unsafe { FixMessageStoreFactory_delete(self.0) } + } +} diff --git a/quickfix/src/message_store_factory/postgres.rs b/quickfix/src/message_store_factory/postgres.rs new file mode 100644 index 0000000..8098c88 --- /dev/null +++ b/quickfix/src/message_store_factory/postgres.rs @@ -0,0 +1,30 @@ +use quickfix_ffi::{ + FixMessageStoreFactory_delete, FixMessageStoreFactory_t, FixPostgresMessageStoreFactory_new, +}; + +use crate::{FfiMessageStoreFactory, QuickFixError, SessionSettings}; + +/// PostgreSQL based implementation of `MessageStore`. +#[derive(Debug)] +pub struct PostgresMessageStoreFactory(FixMessageStoreFactory_t); + +impl PostgresMessageStoreFactory { + /// Try to create new struct from settings. + pub fn try_new(settings: &SessionSettings) -> Result { + unsafe { FixPostgresMessageStoreFactory_new(settings.0) } + .map(Self) + .ok_or(QuickFixError::NullFunctionReturn) + } +} + +impl FfiMessageStoreFactory for PostgresMessageStoreFactory { + fn as_ffi_ptr(&self) -> FixMessageStoreFactory_t { + self.0 + } +} + +impl Drop for PostgresMessageStoreFactory { + fn drop(&mut self) { + unsafe { FixMessageStoreFactory_delete(self.0) } + } +} diff --git a/quickfix/tests/test_lifecycle.rs b/quickfix/tests/test_lifecycle.rs index 46ff646..9c6f3d6 100644 --- a/quickfix/tests/test_lifecycle.rs +++ b/quickfix/tests/test_lifecycle.rs @@ -13,14 +13,3 @@ fn test_extra_log_factory() { let _file_log_factory = LogFactory::try_new(&RustLogger).unwrap(); } - -#[test] -fn test_file_message_store_factory() { - let settings = SessionSettings::try_from_path("../configs/settings.ini").unwrap(); - let _message_store_factory = FileMessageStoreFactory::try_new(&settings).unwrap(); -} - -#[test] -fn test_memory_message_store_factory() { - let _message_store_factory = MemoryMessageStoreFactory::new(); -} diff --git a/quickfix/tests/test_message_store.rs b/quickfix/tests/test_message_store.rs new file mode 100644 index 0000000..ebbce28 --- /dev/null +++ b/quickfix/tests/test_message_store.rs @@ -0,0 +1,26 @@ +use quickfix::*; + +#[test] +fn test_file() { + let settings = SessionSettings::new(); + let _message_store_factory = FileMessageStoreFactory::try_new(&settings).unwrap(); +} + +#[test] +#[cfg(feature = "build-with-mysql")] +fn test_mysql() { + let settings = SessionSettings::new(); + let _message_store_factory = MySqlMessageStoreFactory::try_new(&settings).unwrap(); +} + +#[test] +#[cfg(feature = "build-with-postgres")] +fn test_postgres() { + let settings = SessionSettings::new(); + let _message_store_factory = PostgresMessageStoreFactory::try_new(&settings).unwrap(); +} + +#[test] +fn test_memory() { + let _message_store_factory = MemoryMessageStoreFactory::new(); +} From c7fa3f1b44bf6a8ba828e7835919084d93424c7d Mon Sep 17 00:00:00 2001 From: Arthur LE MOIGNE Date: Mon, 27 Nov 2023 10:38:46 +0100 Subject: [PATCH 2/3] =?UTF-8?q?=F0=9F=91=B7=20Add=20new=20target=20to=20CI?= =?UTF-8?q?=20to=20build=20with=20SQL=20libraries?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/ci.yml | 30 ++++++++++++++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ce8ddc7..70abf76 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -17,7 +17,6 @@ jobs: with: submodules: true - uses: Swatinem/rust-cache@v2 - - name: Build run: cargo build --verbose - name: Run tests no default features @@ -25,14 +24,41 @@ jobs: - name: Run tests run: cargo test - lint: + test_mysql: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 with: submodules: true - uses: Swatinem/rust-cache@v2 + - name: Install dependencies + run: | + sudo apt-get update + sudo apt-get install --yes libmysqlclient-dev + - name: Run tests + run: cargo test -F build-with-mysql + test_postgresql: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + submodules: true + - uses: Swatinem/rust-cache@v2 + - name: Install dependencies + run: | + sudo apt-get update + sudo apt-get install --yes libpq-dev + - name: Run tests + run: cargo test -F build-with-postgres + + lint: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + submodules: true + - uses: Swatinem/rust-cache@v2 - name: Lint run: cargo clippy - name: Format From 54a3518a5269d4c0cfab5e6de575d76de55fb513 Mon Sep 17 00:00:00 2001 From: Arthur LE MOIGNE Date: Mon, 27 Nov 2023 14:00:38 +0100 Subject: [PATCH 3/3] =?UTF-8?q?=E2=AC=86=EF=B8=8F=20Update=20submodule?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitmodules | 2 +- libquickfix | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.gitmodules b/.gitmodules index ee056e7..960d5cd 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,3 @@ [submodule "libquickfix"] path = libquickfix - url = https://github.com/quickfix/quickfix.git + url = https://github.com/arthurlm/quickfix.git diff --git a/libquickfix b/libquickfix index 0b88788..b30cc23 160000 --- a/libquickfix +++ b/libquickfix @@ -1 +1 @@ -Subproject commit 0b88788710b6b9767440cd430bf24c6b6e2080a2 +Subproject commit b30cc23ecbaee57b50b8f8808be9de338608ae62