diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 130df83..ed7e9ff 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -21,7 +21,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - build: [1, 2, 3, 4, 5, 6] + build: [1, 2, 3, 4] include: # ------------------------------------------------------------------- # CLANG, Release @@ -75,36 +75,6 @@ jobs: compiler-desc: gcc os: ubuntu-latest - - # ------------------------------------------------------------------- - # CLANG, Release header only - # ------------------------------------------------------------------- - - build: 5 - build-type: Release - build-shared: 'ON' - header-only: 'ON' - cxx-standard: 17 - cxx-compiler: clang++ - cxx-flags: '' - cc-compiler: clang - compiler-desc: clang - os: ubuntu-latest - - # ------------------------------------------------------------------- - # gcc, Release header only - # ------------------------------------------------------------------- - - build: 6 - build-type: Release - build-shared: 'ON' - header-only: 'ON' - cxx-standard: 17 - cxx-compiler: g++ - cxx-flags: '' - cc-compiler: gcc - compiler-desc: gcc - os: ubuntu-latest - - env: CXX: ${{ matrix.cxx-compiler }} CC: ${{ matrix.cc-compiler }} @@ -124,7 +94,6 @@ jobs: -DCMAKE_CXX_FLAGS=${{ matrix.cxx-flags }} \ -DCMAKE_VERBOSE_MAKEFILE:BOOL='OFF' \ -DBUILD_SHARED_LIBS=${{ matrix.build-shared }} \ - -DPYSTRING_HEADER_ONLY=${{ matrix.header-only }} working-directory: _build - name: Build run: | @@ -145,7 +114,7 @@ jobs: runs-on: macos-latest strategy: matrix: - build: [1, 2, 3] + build: [1, 2] include: # Release @@ -165,16 +134,6 @@ jobs: cxx-flags: '' os: macos-latest - - # Release header only - - build: 3 - build-type: Release - build-shared: 'ON' - header-only: 'ON' - cxx-standard: 17 - cxx-flags: '' - os: macos-latest - steps: - name: Checkout uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 @@ -192,7 +151,6 @@ jobs: -DCMAKE_CXX_FLAGS=${{ matrix.cxx-flags }} \ -DCMAKE_VERBOSE_MAKEFILE:BOOL='OFF' \ -DBUILD_SHARED_LIBS=${{ matrix.build-shared }} \ - -DPYSTRING_HEADER_ONLY=${{ matrix.header-only }} working-directory: _build - name: Build run: | @@ -232,15 +190,6 @@ jobs: cxx-flags: '' os: windows-latest - # Release header only - - build: 3 - build-type: Release - build-shared: 'ON' - header-only: 'ON' - cxx-standard: 17 - cxx-flags: '' - os: windows-latest - steps: - name: Checkout @@ -261,7 +210,6 @@ jobs: -DCMAKE_CXX_FLAGS=${{ matrix.cxx-flags }} \ -DCMAKE_VERBOSE_MAKEFILE:BOOL='OFF' \ -DBUILD_SHARED_LIBS=${{ matrix.build-shared }} \ - -DPYSTRING_HEADER_ONLY=${{ matrix.header-only }} shell: bash working-directory: _build - name: Build diff --git a/.github/workflows/meson.yml b/.github/workflows/meson.yml index e020145..bca9437 100644 --- a/.github/workflows/meson.yml +++ b/.github/workflows/meson.yml @@ -7,16 +7,13 @@ on: jobs: meson-build-and-tests: runs-on: ${{ matrix.platform }} - name: ${{ matrix.platform }}, ${{ matrix.mode.name }} ${{ matrix.flavor }} ${{ matrix.library_mode }} + name: ${{ matrix.platform }}, ${{ matrix.mode.name }} ${{ matrix.flavor }} strategy: fail-fast: false matrix: flavor: - debug - release - library_mode: - - compiled - - header-only mode: - name: default extra_envs: {} @@ -64,29 +61,6 @@ jobs: - macos-latest exclude: - # Only test header-only with a subset of configurations to reduce CI time - # Test header-only only with default compiler in release mode - - library_mode: header-only - flavor: debug - - library_mode: header-only - mode: - name: gcc - - library_mode: header-only - mode: - name: clang - - library_mode: header-only - mode: - name: sanitize - - library_mode: header-only - mode: - name: sanitize+asanonly - - library_mode: header-only - mode: - name: clang+sanitize - - library_mode: header-only - mode: - name: clang-cl+sanitize - # clang-cl only makes sense on windows. - platform: ubuntu-22.04 mode: @@ -151,21 +125,21 @@ jobs: if: ${{ matrix.platform == 'windows-2022' }} env: ${{ matrix.mode.extra_envs }} run: | - meson setup build-${{ matrix.flavor }}-${{ matrix.library_mode }} --buildtype=${{ matrix.flavor }} -Ddefault_library=static -Dheader_only=${{ matrix.library_mode == 'header-only' && 'true' || 'false' }} ${{ matrix.mode.args }} --vsenv + meson setup build-${{ matrix.flavor }} --buildtype=${{ matrix.flavor }} -Ddefault_library=static ${{ matrix.mode.args }} --vsenv - name: Configuring if: ${{ matrix.platform != 'windows-2022' }} env: ${{ matrix.mode.extra_envs }} run: | - meson setup build-${{ matrix.flavor }}-${{ matrix.library_mode }} --buildtype=${{ matrix.flavor }} -Dheader_only=${{ matrix.library_mode == 'header-only' && 'true' || 'false' }} ${{ matrix.mode.args }} + meson setup build-${{ matrix.flavor }} --buildtype=${{ matrix.flavor }} ${{ matrix.mode.args }} - name: Building run: | - meson compile -C build-${{ matrix.flavor }}-${{ matrix.library_mode }} + meson compile -C build-${{ matrix.flavor }} - name: Running tests env: ${{ matrix.mode.extra_envs }} run: | - meson test -C build-${{ matrix.flavor }}-${{ matrix.library_mode }} --timeout-multiplier 0 + meson test -C build-${{ matrix.flavor }} --timeout-multiplier 0 - uses: actions/upload-artifact@v4 if: failure() with: - name: ${{ matrix.platform }}-${{ matrix.mode.name }}-${{ matrix.flavor }}-${{ matrix.library_mode }}-logs - path: build-${{ matrix.flavor }}-${{ matrix.library_mode }}/meson-logs \ No newline at end of file + name: ${{ matrix.platform }}-${{ matrix.mode.name }}-${{ matrix.flavor }}-logs + path: build-${{ matrix.flavor }}/meson-logs diff --git a/CMakeLists.txt b/CMakeLists.txt index a0318b9..be8c306 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,12 +1,11 @@ cmake_minimum_required(VERSION 3.10) -project(pystring LANGUAGES CXX VERSION 1.1.4) +project(pystring LANGUAGES CXX VERSION 1.2.0) set(CMAKE_CXX_STANDARD 11) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_EXTENSIONS OFF) option (BUILD_SHARED_LIBS "Build shared libraries (set to OFF to build static libs)" ON) -option(PYSTRING_HEADER_ONLY "Build as header-only library" OFF) # If the user hasn't configured cmake with an explicit # -DCMAKE_INSTALL_PREFIX=..., then set it to safely install into ./dist, to @@ -19,52 +18,86 @@ endif() message (STATUS "Installation path will be ${CMAKE_INSTALL_PREFIX}") include(GNUInstallDirs) -if(PYSTRING_HEADER_ONLY) - message(STATUS "Building pystring as header-only library") - add_library(pystring INTERFACE) - - target_compile_definitions(pystring INTERFACE PYSTRING_HEADER_ONLY) - - target_include_directories(pystring INTERFACE - $ - $ - ) - - # Install both headers for header-only mode - install(FILES pystring.h pystring_impl.h - DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/${PROJECT_NAME} - ) -else() - message(STATUS "Building pystring as compiled library") - - add_library(pystring - pystring.cpp - pystring.h - ) - - set_target_properties(pystring PROPERTIES - VERSION ${PROJECT_VERSION} - SOVERSION ${PROJECT_VERSION_MAJOR} - ) - - install(TARGETS pystring - LIBRARY DESTINATION lib - RUNTIME DESTINATION bin - ARCHIVE DESTINATION lib - ) - - install (FILES pystring.h - DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/${PROJECT_NAME} - COMPONENT developer - ) - -endif() - -# Test executable - -add_executable (pystring_test test.cpp) -TARGET_LINK_LIBRARIES (pystring_test pystring) +# --- Compiled library target: pystring::pystring --- +add_library(pystring + pystring.cpp + pystring.h +) + +set_target_properties(pystring PROPERTIES + VERSION ${PROJECT_VERSION} + SOVERSION ${PROJECT_VERSION_MAJOR} +) + +target_include_directories(pystring PUBLIC + $ + $ +) + +install(TARGETS pystring + EXPORT pystringTargets + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} +) + + +# --- Header-only target: pystring::pystring_header_only --- +add_library(pystring_header_only INTERFACE) + +target_compile_definitions(pystring_header_only INTERFACE PYSTRING_HEADER_ONLY) + +target_include_directories(pystring_header_only INTERFACE + $ + $ +) + +install(TARGETS pystring_header_only + EXPORT pystringTargets +) + +install(FILES pystring.h pystring_impl.h + DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/${PROJECT_NAME} +) + +# --- Export & package config --- +install(EXPORT pystringTargets + FILE pystringTargets.cmake + NAMESPACE pystring:: + DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/pystring +) + +include(CMakePackageConfigHelpers) + +configure_package_config_file( + cmake/pystringConfig.cmake.in + pystringConfig.cmake + INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/pystring +) + +write_basic_package_version_file( + pystringConfigVersion.cmake + VERSION ${PROJECT_VERSION} + COMPATIBILITY SameMajorVersion +) + +install(FILES + ${CMAKE_CURRENT_BINARY_DIR}/pystringConfig.cmake + ${CMAKE_CURRENT_BINARY_DIR}/pystringConfigVersion.cmake + DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/pystring +) + +# --- Tests --- +add_executable(pystring_test test.cpp) +target_link_libraries(pystring_test pystring) + +add_executable(pystring_test_header_only test.cpp) +target_link_libraries(pystring_test_header_only pystring_header_only) + +# Compile-time check that PYSTRING_HEADER_ONLY propagates correctly +add_executable(pystring_test_header_only_define test_header_only_define.cpp) +target_link_libraries(pystring_test_header_only_define pystring_header_only) enable_testing() add_test(NAME PyStringTest COMMAND pystring_test) - +add_test(NAME PyStringTestHeaderOnly COMMAND pystring_test_header_only) diff --git a/cmake/pystringConfig.cmake.in b/cmake/pystringConfig.cmake.in new file mode 100644 index 0000000..b62792d --- /dev/null +++ b/cmake/pystringConfig.cmake.in @@ -0,0 +1,8 @@ +@PACKAGE_INIT@ + +# Prevent double-definition errors +if(NOT TARGET pystring::pystring AND NOT TARGET pystring::pystring_header_only) + include("${CMAKE_CURRENT_LIST_DIR}/pystringTargets.cmake") +endif() + +check_required_components(pystring) diff --git a/meson.build b/meson.build index 5dab5b8..d0da73d 100644 --- a/meson.build +++ b/meson.build @@ -1,65 +1,51 @@ project( 'pystring', 'cpp', - version: '1.1.4', + version: '1.2.0', license: 'BSD-3-Clause', license_files: 'LICENSE', meson_version: '>=1.3', default_options: ['cpp_std=c++17,c++11', 'warning_level=3'], ) -# Option to build as header-only library -header_only = get_option('header_only') - inc = include_directories('.') hdrs = files('pystring.h') -if header_only - # Header-only mode: create a header-only dependency - message('Building pystring as header-only library') - - pystring_dep = declare_dependency( - include_directories: inc, - compile_args: ['-DPYSTRING_HEADER_ONLY'], - ) - - # Install headers for header-only mode - install_headers(hdrs, files('pystring_impl.h'), subdir: 'pystring') - -else - # Compiled mode: build as normal library - message('Building pystring as compiled library') +# --- Compiled library target --- +pystring_lib = library( + 'pystring', + files('pystring.cpp'), + implicit_include_directories: false, + include_directories: inc, + version: meson.project_version(), + install: true, +) - srcs = files('pystring.cpp') +pystring_dep = declare_dependency( + link_with: pystring_lib, + include_directories: inc, +) - pystring_lib = library( - 'pystring', - srcs, - implicit_include_directories: false, - include_directories: inc, - version: meson.project_version(), - install: true, - ) +install_headers(hdrs, subdir: 'pystring') - pystring_dep = declare_dependency( - link_with: pystring_lib, - include_directories: inc, - ) +pkgconfig = import('pkgconfig') +pkgconfig.generate( + pystring_lib, + description: 'C++ functions matching the interface and behavior of python string methods with std::string', +) - # Install headers for compiled mode - install_headers(hdrs, subdir: 'pystring') +# --- Header-only target --- +pystring_header_only_dep = declare_dependency( + include_directories: inc, + compile_args: ['-DPYSTRING_HEADER_ONLY'], +) - # Generate pkg-config file - pkgconfig = import('pkgconfig') - pkgconfig.generate( - pystring_lib, - description: 'C++ functions matching the interface and behavior of python string methods with std::string', - ) -endif +install_headers(hdrs, files('pystring_impl.h'), subdir: 'pystring') +# --- Override default dependency --- meson.override_dependency('pystring', pystring_dep) -# Build and run tests +# --- Tests --- test( 'PyStringTest', executable( @@ -69,3 +55,21 @@ test( build_by_default: false, ), ) + +test( + 'PyStringTestHeaderOnly', + executable( + 'pystring_test_header_only', + 'test.cpp', + dependencies: pystring_header_only_dep, + build_by_default: false, + ), +) + +# Compile-time check that PYSTRING_HEADER_ONLY propagates correctly +executable( + 'pystring_test_header_only_define', + 'test_header_only_define.cpp', + dependencies: pystring_header_only_dep, + build_by_default: true, +) diff --git a/meson_options.txt b/meson_options.txt deleted file mode 100644 index 77be15e..0000000 --- a/meson_options.txt +++ /dev/null @@ -1 +0,0 @@ -option('header_only', type: 'boolean', value: false, description: 'Build as header-only library') diff --git a/test_header_only_define.cpp b/test_header_only_define.cpp new file mode 100644 index 0000000..5a364ce --- /dev/null +++ b/test_header_only_define.cpp @@ -0,0 +1,5 @@ +// test_header_only_mode.cpp +#ifndef PYSTRING_HEADER_ONLY +#error "PYSTRING_HEADER_ONLY must be defined when using the header-only target" +#endif +int main() { return 0; }