diff --git a/.github/workflows/unittest_linux_32bit.yml b/.github/workflows/unittest_linux_32bit.yml new file mode 100644 index 00000000..ad2e35d4 --- /dev/null +++ b/.github/workflows/unittest_linux_32bit.yml @@ -0,0 +1,19 @@ +name: linux_32bit +on: + push: + paths-ignore: + - 'docs/**' + branches: [ master ] + pull_request: + branches: [ master ] + +jobs: + linux_32bit: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v1 + - uses: seanmiddleditch/gha-setup-ninja@master + - name: install 32-bit toolchain + run: sudo apt-get update && sudo apt-get install -y g++-multilib pkg-config + - name: make test + run: make -f makefile.dev test_atomic_32 diff --git a/CMake/CheckAtomic.cmake b/CMake/CheckAtomic.cmake new file mode 100644 index 00000000..b78a4935 --- /dev/null +++ b/CMake/CheckAtomic.cmake @@ -0,0 +1,30 @@ +# Some 32-bit targets need libatomic for the library's 64-bit counters. +function(ixwebsocket_check_atomic) + include(CheckCXXSourceCompiles) + include(CMakePushCheckState) + cmake_push_check_state(RESET) + # A static-library try_compile would hide unresolved atomic runtime symbols. + set(CMAKE_TRY_COMPILE_TARGET_TYPE EXECUTABLE) + set(atomic_test_source " + #include + #include + int main(int argc, char**) { + std::atomic counter(0); + counter.fetch_add(static_cast(argc)); + return static_cast(counter.load()); + } + ") + check_cxx_source_compiles("${atomic_test_source}" IXWEBSOCKET_HAVE_BUILTIN_ATOMICS) + if (NOT IXWEBSOCKET_HAVE_BUILTIN_ATOMICS) + set(CMAKE_REQUIRED_LIBRARIES atomic) + check_cxx_source_compiles("${atomic_test_source}" IXWEBSOCKET_HAVE_LIBATOMIC) + if (NOT IXWEBSOCKET_HAVE_LIBATOMIC) + message(FATAL_ERROR "Unable to link 64-bit C++ atomics, even with libatomic. Install libatomic for the target toolchain.") + endif() + target_link_libraries(ixwebsocket PRIVATE atomic) + set(IXWEBSOCKET_PRIVATE_LIBS "-latomic" PARENT_SCOPE) + endif() + cmake_pop_check_state() +endfunction() + +ixwebsocket_check_atomic() diff --git a/CMakeLists.txt b/CMakeLists.txt index 19ff644b..6be20b47 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -196,6 +196,9 @@ else() ) endif() +set(IXWEBSOCKET_PRIVATE_LIBS "") +include(CheckAtomic) + if (USE_TLS) target_compile_definitions(ixwebsocket PUBLIC IXWEBSOCKET_USE_TLS) if (USE_NETWORK_FRAMEWORK) diff --git a/docs/build.md b/docs/build.md index e9cd9464..3034955a 100644 --- a/docs/build.md +++ b/docs/build.md @@ -41,6 +41,12 @@ make install # will install to /usr/local on Unix, on macOS it is a good idea to Headers and a static library will be installed to the target dir. There is a unittest which can be executed by typing `make test`. +CMake checks whether 64-bit atomics need `libatomic` and links it when required, +including for consumers of the installed static library. Cross-compilation uses +a compile-and-link check without running target executables. On Linux with GCC +multilib installed, `make -f makefile.dev test_atomic_32` builds and runs the +32-bit installed-consumer checks. + Options for building: * `-DBUILD_SHARED_LIBS=ON` will build the unittest as a shared libary instead of a static library, which is the default diff --git a/ixwebsocket.pc.in b/ixwebsocket.pc.in index fb72ec85..b57f6ea3 100644 --- a/ixwebsocket.pc.in +++ b/ixwebsocket.pc.in @@ -7,5 +7,6 @@ Name: ixwebsocket Description: websocket and http client and server library, with TLS support and very few dependencies Version: @CMAKE_PROJECT_VERSION@ Libs: -L${libdir} -lixwebsocket +Libs.private: @IXWEBSOCKET_PRIVATE_LIBS@ Cflags: -I${includedir} Requires: @requires@ diff --git a/makefile.dev b/makefile.dev index 2881ca69..8468f63b 100644 --- a/makefile.dev +++ b/makefile.dev @@ -134,6 +134,17 @@ test: (cd build ; ninja) (cd build ; ninja -v test) +.PHONY: test_atomic_32 +test_atomic_32: + cmake -S . -B build -DCMAKE_BUILD_TYPE=Debug -DCMAKE_UNITY_BUILD=ON -DCMAKE_C_FLAGS="-m32 -march=i486" -DCMAKE_CXX_FLAGS="-m32 -march=i486" -DCMAKE_TRY_COMPILE_TARGET_TYPE=STATIC_LIBRARY -DUSE_TLS=OFF -DUSE_NETWORK_FRAMEWORK=OFF -DUSE_ZLIB=OFF -DUSE_TEST=OFF -DUSE_WS=OFF -DCMAKE_INSTALL_PREFIX="$(CURDIR)/build/atomic-install" -DCMAKE_INSTALL_LIBDIR=lib + cmake --build build --parallel + cmake --install build + cmake -S test/cmake/atomic -B build/atomic-consumer -DCMAKE_CXX_FLAGS="-m32 -march=i486" -DCMAKE_PREFIX_PATH="$(CURDIR)/build/atomic-install" + cmake --build build/atomic-consumer --parallel + ./build/atomic-consumer/atomic_consumer + $(CXX) -std=c++11 -m32 -march=i486 test/cmake/atomic/main.cpp -o build/atomic-pkg-consumer $$(PKG_CONFIG_PATH="$(CURDIR)/build/atomic-install/lib/pkgconfig" pkg-config --cflags --libs --static ixwebsocket) + ./build/atomic-pkg-consumer + test_asan: mkdir -p build && (cd build ; cmake -GNinja -DCMAKE_INSTALL_MESSAGE=LAZY -DCMAKE_UNITY_BUILD=ON -DCMAKE_BUILD_TYPE=Debug -DUSE_TLS=1 -DUSE_TEST=1 .. -DCMAKE_C_FLAGS="-fsanitize=address -fno-omit-frame-pointer" -DCMAKE_CXX_FLAGS="-fsanitize=address -fno-omit-frame-pointer") (cd build ; ninja) diff --git a/test/cmake/atomic/CMakeLists.txt b/test/cmake/atomic/CMakeLists.txt new file mode 100644 index 00000000..c1a9adc3 --- /dev/null +++ b/test/cmake/atomic/CMakeLists.txt @@ -0,0 +1,12 @@ +cmake_minimum_required(VERSION 3.4.1...3.17.2) +project(ixwebsocket_atomic_consumer LANGUAGES CXX) + +set(CMAKE_CXX_STANDARD 11) +set(CMAKE_CXX_STANDARD_REQUIRED ON) +set(CMAKE_CXX_EXTENSIONS OFF) + +# The installed target also carries the library's existing thread dependency. +find_package(Threads REQUIRED) +find_package(ixwebsocket CONFIG REQUIRED) +add_executable(atomic_consumer main.cpp) +target_link_libraries(atomic_consumer PRIVATE ixwebsocket::ixwebsocket) diff --git a/test/cmake/atomic/main.cpp b/test/cmake/atomic/main.cpp new file mode 100644 index 00000000..5b501ecc --- /dev/null +++ b/test/cmake/atomic/main.cpp @@ -0,0 +1,14 @@ +/* + * Check that installed consumers can link the library's 64-bit atomic users. + */ +#include +#include + +int main() +{ + ix::ConnectionState first; + ix::ConnectionState second; + ix::WebSocketTransport transport; + return first.getId() == second.getId() || + transport.getReadyState() != ix::WebSocketTransport::ReadyState::CLOSED; +}