diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 87b1706..204e5b2 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -47,7 +47,7 @@ jobs: uses: vmactions/freebsd-vm@v1 with: prepare: | - pkg install -y cmake ninja bash capnproto + pkg install -y cmake ninja bash capnproto git sync: 'rsync' copyback: false @@ -79,7 +79,7 @@ jobs: strategy: fail-fast: false matrix: - config: [default, llvm, gnu32, sanitize, olddeps] + config: [default, llvm, gnu32, sanitize, olddeps, newdeps] name: build • ${{ matrix.config }} diff --git a/CMakeLists.txt b/CMakeLists.txt index a36023b..4e21e82 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,7 +2,33 @@ # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. -cmake_minimum_required(VERSION 3.12) +# Call cmake_minimum_required() only if it was not already called, so parent +# projects set the policy version when this project is included via +# add_subdirectory(). +# +# Rationale: Different projects have different practices for choosing policy +# versions. For example, the Bitcoin Core project sets an old policy version, +# causing CMake to use deprecated behaviors instead of new behaviors and +# maximize compatibility with a single old version of CMake, reducing variance +# between builds with newer CMake versions. By contrast CMake documentation +# recommends setting the policy version to the latest supported version of +# CMake, as an upgrading mechanism rather than a pinning mechanism, to let +# project authors fix problems before enabling newer policies, while not opting +# into deprecated policies on a longer term basis. +if(NOT DEFINED CMAKE_MINIMUM_REQUIRED_VERSION + OR CMAKE_MINIMUM_REQUIRED_VERSION VERSION_LESS 3.12) + # The left number in the range below is the minimum CMake version required to + # run this project. The right number is the CMake policy version. + # + # The purpose of the minimum version is to trigger a helpful error if a version + # of CMake is being used that is too old to work. If this number is changed, + # the version in ci/configs/olddeps.bash should be changed to match. + # + # The purpose of the policy version is to opt out of policies introduced in + # newer versions of CMake until they have been tested. If this number is + # changed, the version in ci/configs/newdeps.bash should be changed to match. + cmake_minimum_required(VERSION 3.12...4.1 FATAL_ERROR) +endif() project("Libmultiprocess" CXX) if(CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR) @@ -10,9 +36,21 @@ if(CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR) set(CMAKE_CXX_STANDARD_REQUIRED YES) endif() +# Disable automatic C++20 module dependency scanning. +# CMake >=3.28 tries to use `clang-scan-deps` by default, which may not +# be installed on all platforms. We don't use named modules, so turn this off. +set(CMAKE_CXX_SCAN_FOR_MODULES OFF) + include("cmake/compat_find.cmake") -find_package(Threads REQUIRED) +add_library(mpdeps INTERFACE) + +find_package(Threads) + +if(Threads_FOUND) + target_link_libraries(mpdeps INTERFACE Threads::Threads) +endif() + find_package(CapnProto 0.7 QUIET NO_MODULE) if(NOT CapnProto_FOUND) message(FATAL_ERROR @@ -202,7 +240,7 @@ target_link_libraries(mpgen PRIVATE CapnProto::capnp) target_link_libraries(mpgen PRIVATE CapnProto::capnp-rpc) target_link_libraries(mpgen PRIVATE CapnProto::capnpc) target_link_libraries(mpgen PRIVATE CapnProto::kj) -target_link_libraries(mpgen PRIVATE Threads::Threads) +target_link_libraries(mpgen PRIVATE mpdeps) set_target_properties(mpgen PROPERTIES INSTALL_RPATH_USE_LINK_PATH TRUE) set_target_properties(mpgen PROPERTIES diff --git a/ci/README.md b/ci/README.md index 2ddaa9e..25c0a7e 100644 --- a/ci/README.md +++ b/ci/README.md @@ -21,6 +21,7 @@ CI_CONFIG=ci/configs/llvm.bash ci/scripts/run.sh CI_CONFIG=ci/configs/gnu32.bash ci/scripts/run.sh CI_CONFIG=ci/configs/sanitize.bash ci/scripts/run.sh CI_CONFIG=ci/configs/olddeps.bash ci/scripts/run.sh +CI_CONFIG=ci/configs/newdeps.bash ci/scripts/run.sh ``` By default CI jobs will reuse their build directories. `CI_CLEAN=1` can be specified to delete them before running instead. diff --git a/ci/configs/newdeps.bash b/ci/configs/newdeps.bash new file mode 100644 index 0000000..c59a76d --- /dev/null +++ b/ci/configs/newdeps.bash @@ -0,0 +1,7 @@ +CI_DESC="CI job using newest Cap'n Proto and cmake versions" +CI_DIR=build-newdeps +export CXXFLAGS="-Werror -Wall -Wextra -Wpedantic -Wno-unused-parameter -Wno-error=array-bounds" +CAPNP_CHECKOUT=master +# cmakeVersion here should match policy version in CMakeLists.txt +NIX_ARGS=(--argstr capnprotoVersion "none" --argstr cmakeVersion "4.1.1") +BUILD_ARGS=(-k) diff --git a/ci/configs/olddeps.bash b/ci/configs/olddeps.bash index 95f4412..2a6329c 100644 --- a/ci/configs/olddeps.bash +++ b/ci/configs/olddeps.bash @@ -1,5 +1,6 @@ CI_DESC="CI job using old Cap'n Proto and cmake versions" CI_DIR=build-olddeps export CXXFLAGS="-Werror -Wall -Wextra -Wpedantic -Wno-unused-parameter -Wno-error=array-bounds" +# cmakeVersion here should match minimum version in CMakeLists.txt NIX_ARGS=(--argstr capnprotoVersion "0.7.1" --argstr cmakeVersion "3.12.4") BUILD_ARGS=(-k) diff --git a/ci/configs/sanitize.bash b/ci/configs/sanitize.bash index ce920f4..fc24960 100644 --- a/ci/configs/sanitize.bash +++ b/ci/configs/sanitize.bash @@ -3,5 +3,5 @@ CI_DIR=build-sanitize export CXX=clang++ export CXXFLAGS="-ggdb -Werror -Wall -Wextra -Wpedantic -Wthread-safety-analysis -Wno-unused-parameter -fsanitize=thread" CMAKE_ARGS=() -BUILD_ARGS=(-k -j4) +BUILD_ARGS=(-k) BUILD_TARGETS=(mptest) diff --git a/ci/scripts/ci.sh b/ci/scripts/ci.sh index d989e9f..58b8d34 100755 --- a/ci/scripts/ci.sh +++ b/ci/scripts/ci.sh @@ -21,17 +21,43 @@ cmake --version cmake_ver=$(cmake --version | awk '/version/{print $3; exit}') ver_ge() { [ "$(printf '%s\n' "$2" "$1" | sort -V | head -n1)" = "$2" ]; } +# If CAPNP_CHECKOUT was requested, clone and install requested Cap'n Proto branch or tag +capnp_prefix= +if [ -n "${CAPNP_CHECKOUT-}" ]; then + capnp_prefix="$PWD/capnp-install" + [ -e "capnp" ] || git clone -b "${CAPNP_CHECKOUT}" "https://github.com/capnproto/capnproto" capnp + mkdir -p capnp/build + ( + cd capnp/build + git --no-pager log -1 || true + CXXFLAGS="-std=c++20" cmake .. "-DCMAKE_INSTALL_PREFIX=${capnp_prefix}" -DBUILD_TESTING=OFF -DWITH_OPENSSL=OFF -DWITH_ZLIB=OFF + cmake --build . + cmake --install . + ) + export CMAKE_PREFIX_PATH="${capnp_prefix}:${CMAKE_PREFIX_PATH-}" +fi + src_dir=$PWD mkdir -p "$CI_DIR" cd "$CI_DIR" -cmake "$src_dir" "${CMAKE_ARGS[@]+"${CMAKE_ARGS[@]}"}" +git --no-pager log -1 || true +cmake_args=("${CMAKE_ARGS[@]+"${CMAKE_ARGS[@]}"}") +if ! cmake "$src_dir" "${cmake_args[@]}"; then + # If cmake failed, try it again with debug options. + # Could add --trace / --trace-expand here too but they are very verbose. + cmake_args+=(--debug-find --debug-output --debug-trycompile --log-level=DEBUG) + cmake "$src_dir" "${cmake_args[@]}" || : "cmake exited with $?" + cat CMakeFiles/CMakeConfigureLog.yaml || true + find . -ls || true + false +fi if ver_ge "$cmake_ver" "3.15"; then - cmake --build . -t "${BUILD_TARGETS[@]}" -- "${BUILD_ARGS[@]+"${BUILD_ARGS[@]}"}" + cmake --build . --parallel -t "${BUILD_TARGETS[@]}" -- "${BUILD_ARGS[@]+"${BUILD_ARGS[@]}"}" else # Older versions of cmake can only build one target at a time with --target, # and do not support -t shortcut for t in "${BUILD_TARGETS[@]}"; do - cmake --build . --target "$t" -- "${BUILD_ARGS[@]+"${BUILD_ARGS[@]}"}" + cmake --build . --parallel --target "$t" -- "${BUILD_ARGS[@]+"${BUILD_ARGS[@]}"}" done fi ctest --output-on-failure diff --git a/cmake/pthread_checks.cmake b/cmake/pthread_checks.cmake index 241978d..a8b59c2 100644 --- a/cmake/pthread_checks.cmake +++ b/cmake/pthread_checks.cmake @@ -9,7 +9,7 @@ include(CMakePushCheckState) include(CheckCXXSourceCompiles) cmake_push_check_state() -set(CMAKE_REQUIRED_LIBRARIES Threads::Threads) +set(CMAKE_REQUIRED_LIBRARIES mpdeps) check_cxx_source_compiles(" #include int main(int argc, char** argv) diff --git a/example/CMakeLists.txt b/example/CMakeLists.txt index 7da049c..a8961c5 100644 --- a/example/CMakeLists.txt +++ b/example/CMakeLists.txt @@ -9,20 +9,20 @@ add_executable(mpcalculator ) target_capnp_sources(mpcalculator ${CMAKE_CURRENT_SOURCE_DIR} init.capnp calculator.capnp printer.capnp) target_include_directories(mpcalculator PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}) -target_link_libraries(mpcalculator PRIVATE Threads::Threads) +target_link_libraries(mpcalculator PRIVATE mpdeps) add_executable(mpprinter printer.cpp ) target_capnp_sources(mpprinter ${CMAKE_CURRENT_SOURCE_DIR} init.capnp calculator.capnp printer.capnp) target_include_directories(mpprinter PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}) -target_link_libraries(mpprinter PRIVATE Threads::Threads) +target_link_libraries(mpprinter PRIVATE mpdeps) add_executable(mpexample example.cpp ) target_capnp_sources(mpexample ${CMAKE_CURRENT_SOURCE_DIR} init.capnp calculator.capnp printer.capnp) target_include_directories(mpexample PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}) -target_link_libraries(mpexample PRIVATE Threads::Threads) +target_link_libraries(mpexample PRIVATE mpdeps) add_custom_target(mpexamples DEPENDS mpexample mpcalculator mpprinter) diff --git a/shell.nix b/shell.nix index 24d3820..6d7552e 100644 --- a/shell.nix +++ b/shell.nix @@ -40,6 +40,7 @@ let clang-tools = llvm.clang-tools.override { inherit enableLibcxx; }; cmakeHashes = { "3.12.4" = "sha256-UlVYS/0EPrcXViz/iULUcvHA5GecSUHYS6raqbKOMZQ="; + "4.1.1" = "sha256-sp9vGXM6oiS3djUHoQikJ+1Ixojh+vIrKcROHDBUkoI="; }; cmakeBuild = if cmakeVersion == null then pkgs.cmake else (pkgs.cmake.overrideAttrs (old: { version = cmakeVersion; @@ -50,11 +51,12 @@ let patches = []; })).override { isMinimalBuild = true; }; in crossPkgs.mkShell { - buildInputs = [ + buildInputs = lib.optionals (capnprotoVersion != "none") [ capnproto ]; nativeBuildInputs = with pkgs; [ cmakeBuild + git include-what-you-use ninja ] ++ lib.optionals (!minimal) [ @@ -64,4 +66,7 @@ in crossPkgs.mkShell { # Tell IWYU where its libc++ mapping lives IWYU_MAPPING_FILE = if enableLibcxx then "${llvm.libcxx.dev}/include/c++/v1/libcxx.imp" else null; + + # Avoid "SSL certificate problem: unable to get local issuer certificate" error during git clone in ci/scripts/ci.sh + NIX_SSL_CERT_FILE = "${pkgs.cacert}/etc/ssl/certs/ca-bundle.crt"; } diff --git a/src/mp/util.cpp b/src/mp/util.cpp index b712462..71263aa 100644 --- a/src/mp/util.cpp +++ b/src/mp/util.cpp @@ -2,14 +2,14 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include +#include // IWYU pragma: keep #include #include #include #include #include -#include +#include // IWYU pragma: keep #include #include #include diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 2a1a7e9..2cdb317 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -32,7 +32,7 @@ if(BUILD_TESTING AND TARGET CapnProto::kj-test) target_capnp_sources(mptest ${CMAKE_CURRENT_SOURCE_DIR} mp/test/foo.capnp) target_include_directories(mptest PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}) target_link_libraries(mptest PRIVATE CapnProto::kj-test) - target_link_libraries(mptest PRIVATE Threads::Threads) + target_link_libraries(mptest PRIVATE mpdeps) add_dependencies(mptests mptest) add_test(NAME mptest COMMAND mptest)