diff --git a/.gitignore b/.gitignore index 3c8c8a91f832..9051fadb81fd 100644 --- a/.gitignore +++ b/.gitignore @@ -16,6 +16,7 @@ include/* share/* bin/* thirdparty_build/* +thirdparty_sources/* .vscode/* distr/* *.log @@ -58,3 +59,4 @@ compile_commands.json /scripts/mrbind/msys2_packages /scripts/mrbind/msys2_pacman_install.log /vcpkg_installed +/installed diff --git a/cmake/Modules/ConfigureEmscripten.cmake b/cmake/Modules/ConfigureEmscripten.cmake index f7020efdfa14..d8901aa898bd 100644 --- a/cmake/Modules/ConfigureEmscripten.cmake +++ b/cmake/Modules/ConfigureEmscripten.cmake @@ -1,5 +1,5 @@ # reference: https://github.com/emscripten-core/emscripten/blob/main/src/settings.js -string(JOIN " " MESHLIB_EMSCRIPTEN_CXX_FLAGS +string(JOIN " " EXTRA_CXX_FLAGS # I would use the new-style flag spelling here, but it doesn't work on the old EMSDK 3.1.38 that we # have to support for Unity compatibility. #"--use-port=boost_headers" @@ -12,18 +12,7 @@ string(JOIN " " MESHLIB_EMSCRIPTEN_CXX_FLAGS "-sUSE_ZLIB" # TODO: make optional ) -option(MR_EMSCRIPTEN_WASM2023 "Enable Unity's WebAssembly 2023 target (a set of general-purpose optimizations, including SIMD)" ON) -IF(MR_EMSCRIPTEN_WASM2023) - # Those flags come from here: https://docs.unity3d.com/6000.7/Documentation/Manual/webgl-native-plugins-with-emscripten.html - # Skipping `-fwasm-exceptions` because we don't use exceptions. - # Skipping `-sSUPPORT_LONGJMP=wasm` because that conflicts with our `-s NO_DISABLE_EXCEPTION_CATCHING=1`, and also prevents CMake from finding FreeType during configuration. - # In theory, this flag is supposed to be implemented in terms of `-fwasm-exceptions`, so I'm not sure how it works without that one, but it seems to work (other than the issues above). - # Either way, we don't use `longjmp()`, so it doesn't seem terribly useful. - string(JOIN " " MESHLIB_EMSCRIPTEN_CXX_FLAGS ${MESHLIB_EMSCRIPTEN_CXX_FLAGS} - "-msimd128 -mbulk-memory -mnontrapping-fptoint -msse4.2" - ) -ENDIF() -string(JOIN " " MESHLIB_EMSCRIPTEN_EXE_LINKER_FLAGS +string(JOIN " " EXTRA_EXE_LINKER_FLAGS "-s ALLOW_MEMORY_GROWTH=1" "-s LLD_REPORT_UNDEFINED=1" "-s STACK_SIZE=1048576" # required for GDCM @@ -31,17 +20,14 @@ string(JOIN " " MESHLIB_EMSCRIPTEN_EXE_LINKER_FLAGS option(MR_EMSCRIPTEN_MIMALLOC "Use mimalloc allocator (-s MALLOC=mimalloc) for Emscripten builds" ON) IF(MR_EMSCRIPTEN_MIMALLOC) - set(MESHLIB_EMSCRIPTEN_EXE_LINKER_FLAGS "${MESHLIB_EMSCRIPTEN_EXE_LINKER_FLAGS} -s MALLOC=mimalloc") + set(EXTRA_EXE_LINKER_FLAGS "${EXTRA_EXE_LINKER_FLAGS} -s MALLOC=mimalloc") ENDIF() IF(MR_EMSCRIPTEN_SINGLETHREAD) - set(MESHLIB_EMSCRIPTEN_EXE_LINKER_FLAGS "${MESHLIB_EMSCRIPTEN_EXE_LINKER_FLAGS} -s ENVIRONMENT=web,node") + set(EXTRA_EXE_LINKER_FLAGS "${EXTRA_EXE_LINKER_FLAGS} -s ENVIRONMENT=web,node") ELSE() - string(JOIN " " MESHLIB_EMSCRIPTEN_CXX_FLAGS ${MESHLIB_EMSCRIPTEN_CXX_FLAGS} - "-pthread" - # look https://github.com/emscripten-core/emscripten/issues/8287 - "-Wno-pthreads-mem-growth" - ) + # look https://github.com/emscripten-core/emscripten/issues/8287 + string(JOIN " " EXTRA_CXX_FLAGS ${EXTRA_CXX_FLAGS} "-Wno-pthreads-mem-growth") # uncomment to enable source map for debugging in browsers (slow) #set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -gsource-map") @@ -51,7 +37,7 @@ ELSE() ELSE() set(MAXIMUM_MEMORY 4GB) ENDIF() - string(JOIN " " MESHLIB_EMSCRIPTEN_EXE_LINKER_FLAGS ${MESHLIB_EMSCRIPTEN_EXE_LINKER_FLAGS} + string(JOIN " " EXTRA_EXE_LINKER_FLAGS ${EXTRA_EXE_LINKER_FLAGS} "-s ENVIRONMENT=web,worker,node" #"-pthread" "-s PTHREAD_POOL_SIZE_STRICT=0" @@ -61,7 +47,7 @@ ELSE() ENDIF() IF(MESHLIB_BUILD_MRVIEWER) - string(JOIN " " MESHLIB_EMSCRIPTEN_EXE_LINKER_FLAGS ${MESHLIB_EMSCRIPTEN_EXE_LINKER_FLAGS} + string(JOIN " " EXTRA_EXE_LINKER_FLAGS ${EXTRA_EXE_LINKER_FLAGS} "-s EXPORTED_RUNTIME_METHODS=[ccall]" "-s USE_WEBGL2=1" "-s USE_GLFW=3" @@ -69,7 +55,7 @@ IF(MESHLIB_BUILD_MRVIEWER) ) IF(NOT MR_DISABLE_EMSCRIPTEN_ASYNCIFY) - string(JOIN " " MESHLIB_EMSCRIPTEN_EXE_LINKER_FLAGS ${MESHLIB_EMSCRIPTEN_EXE_LINKER_FLAGS} + string(JOIN " " EXTRA_EXE_LINKER_FLAGS ${EXTRA_EXE_LINKER_FLAGS} "-s ASYNCIFY" # FIXME: comment required "-Wno-limited-postlink-optimizations" @@ -77,5 +63,10 @@ IF(MESHLIB_BUILD_MRVIEWER) ENDIF() ENDIF() -set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${MESHLIB_EMSCRIPTEN_CXX_FLAGS}") -set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${MESHLIB_EMSCRIPTEN_EXE_LINKER_FLAGS}") +string(APPEND CMAKE_CXX_FLAGS " ${EXTRA_CXX_FLAGS}") +string(APPEND CMAKE_EXE_LINKER_FLAGS " ${EXTRA_EXE_LINKER_FLAGS}") +string(APPEND MESHLIB_EMSCRIPTEN_CXX_FLAGS " ${EXTRA_CXX_FLAGS}") +string(APPEND MESHLIB_EMSCRIPTEN_EXE_LINKER_FLAGS " ${EXTRA_EXE_LINKER_FLAGS}") + +unset(EXTRA_CXX_FLAGS) +unset(EXTRA_EXE_LINKER_FLAGS) diff --git a/cmake/Modules/DefaultEmscriptenOptions.cmake b/cmake/Modules/DefaultEmscriptenOptions.cmake new file mode 100644 index 000000000000..744fb16326a5 --- /dev/null +++ b/cmake/Modules/DefaultEmscriptenOptions.cmake @@ -0,0 +1,37 @@ +# this file must be included BEFORE the `project' command: MEMORY64 has to be in effect while CMake probes the compiler + +if(MR_EMSCRIPTEN) + if(MR_EMSCRIPTEN_WASM64) + string(JOIN " " MESHLIB_EMSCRIPTEN_CXX_FLAGS ${MESHLIB_EMSCRIPTEN_CXX_FLAGS} + "-s MEMORY64=1" + ) + string(JOIN " " MESHLIB_EMSCRIPTEN_EXE_LINKER_FLAGS ${MESHLIB_EMSCRIPTEN_EXE_LINKER_FLAGS} + "-s MEMORY64=1" + ) + endif() + + if(NOT MR_EMSCRIPTEN_SINGLETHREAD) + string(JOIN " " MESHLIB_EMSCRIPTEN_CXX_FLAGS ${MESHLIB_EMSCRIPTEN_CXX_FLAGS} + "-pthread" + ) + endif() + + option(MR_EMSCRIPTEN_WASM2023 "Enable Unity's WebAssembly 2023 target (a set of general-purpose optimizations, including SIMD)" ON) + if(MR_EMSCRIPTEN_WASM2023) + # Those flags come from here: https://docs.unity3d.com/6000.7/Documentation/Manual/webgl-native-plugins-with-emscripten.html + # Skipping `-fwasm-exceptions` because we don't use exceptions. + # Skipping `-sSUPPORT_LONGJMP=wasm` because that conflicts with our `-s NO_DISABLE_EXCEPTION_CATCHING=1`, and also prevents CMake from finding FreeType during configuration. + # In theory, this flag is supposed to be implemented in terms of `-fwasm-exceptions`, so I'm not sure how it works without that one, but it seems to work (other than the issues above). + # Either way, we don't use `longjmp()`, so it doesn't seem terribly useful. + string(JOIN " " MESHLIB_EMSCRIPTEN_CXX_FLAGS ${MESHLIB_EMSCRIPTEN_CXX_FLAGS} + "-msimd128" + "-mbulk-memory" + "-mnontrapping-fptoint" + "-msse4.2" + ) + endif() + + string(APPEND CMAKE_C_FLAGS " ${MESHLIB_EMSCRIPTEN_CXX_FLAGS}") + string(APPEND CMAKE_CXX_FLAGS " ${MESHLIB_EMSCRIPTEN_CXX_FLAGS}") + string(APPEND CMAKE_EXE_LINKER_FLAGS " ${MESHLIB_EMSCRIPTEN_EXE_LINKER_FLAGS}") +endif() diff --git a/cmake/Modules/DefaultOptions.cmake b/cmake/Modules/DefaultOptions.cmake index 5e6368f3f321..880c16e843ec 100644 --- a/cmake/Modules/DefaultOptions.cmake +++ b/cmake/Modules/DefaultOptions.cmake @@ -9,17 +9,14 @@ ENDIF() set(CMAKE_CXX_STANDARD ${MR_CXX_STANDARD}) set(CMAKE_CXX_STANDARD_REQUIRED ON) -IF(MR_EMSCRIPTEN AND MR_EMSCRIPTEN_WASM64) - # required to be set before `project()' command for correct platform detection - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -s MEMORY64=1") - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -s MEMORY64=1") - set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -s MEMORY64=1") -ENDIF() - add_compile_definitions(MR_USE_CMAKE_CONFIGURE_FILE) # MSVC debug information format IF(POLICY CMP0141) cmake_policy(SET CMP0141 NEW) set(CMAKE_MSVC_DEBUG_INFORMATION_FORMAT "$<$:Embedded>") -ENDIF() \ No newline at end of file +ENDIF() + +if(MR_EMSCRIPTEN) + include(DefaultEmscriptenOptions) +endif() diff --git a/scripts/build_cpm_thirdparty.sh b/scripts/build_cpm_thirdparty.sh new file mode 100755 index 000000000000..5c60ce2da3ce --- /dev/null +++ b/scripts/build_cpm_thirdparty.sh @@ -0,0 +1,138 @@ +#!/bin/bash + +# This script builds thirdparty with CPM from `thirdparty/cpm` +# Usage: ./scripts/build_cpm_thirdparty.sh [INSTALL_PREFIX] + +# exit if any command failed +set -eo pipefail + +dt=$(date '+%d-%m-%Y_%H:%M:%S'); +logfile="`pwd`/install_thirdparty_${dt}.log" +echo "Thirdparty build script started." +echo "You could find output in ${logfile}" + +# NOTE: realpath is not supported on older macOS versions +BASE_DIR=$( cd "$( dirname "$0" )"/.. ; pwd -P ) +SCRIPT_DIR=${BASE_DIR}/scripts/ + +MESHLIB_THIRDPARTY_DIR=${BASE_DIR}/thirdparty/cpm/ +MESHLIB_THIRDPARTY_BUILD_DIR="${MESHLIB_THIRDPARTY_BUILD_DIR:-${BASE_DIR}/thirdparty_build/}" + +MESHLIB_THIRDPARTY_ROOT_DIR="${1:-./installed}" +mkdir -p "${MESHLIB_THIRDPARTY_ROOT_DIR}" +MESHLIB_THIRDPARTY_ROOT_DIR=$( cd "${MESHLIB_THIRDPARTY_ROOT_DIR}" ; pwd -P ) + +if [[ $OSTYPE == 'darwin'* ]]; then + echo "Host system: MacOS" + INSTALL_REQUIREMENTS="install_brew_requirements.sh" +elif [[ $OSTYPE == 'linux'* ]]; then + source /etc/os-release + echo "Host system: ${NAME} ${DISTRIB_RELEASE}" + if [[ "${ID}" == "ubuntu" ]] || [[ "${ID_LIKE}" == *"ubuntu"* ]]; then + INSTALL_REQUIREMENTS="install_apt_requirements.sh" + fi +else + echo "Host system: ${OSTYPE}" +fi + +. "$SCRIPT_DIR/ask_emscripten_mode.src" + +if [ $MR_EMSCRIPTEN == "ON" ]; then + true # Nothing. +elif [ -n "${INSTALL_REQUIREMENTS}" ]; then + echo "Check requirements. Running ${INSTALL_REQUIREMENTS} ..." + ${SCRIPT_DIR}/$INSTALL_REQUIREMENTS +else + echo "Unsupported system. Installing dependencies is your responsibility." +fi + +# FIXME: make it optional +rm -rf "${MESHLIB_THIRDPARTY_BUILD_DIR}" +mkdir -p "${MESHLIB_THIRDPARTY_BUILD_DIR}" +# FIXME: make it optional +for SUBDIR in lib include share ; do + rm -rf "${MESHLIB_THIRDPARTY_ROOT_DIR}"/${SUBDIR} + mkdir -p "${MESHLIB_THIRDPARTY_ROOT_DIR}"/${SUBDIR} +done + +MR_CMAKE_OPTIONS="${MR_CMAKE_OPTIONS} \ + -D CMAKE_INSTALL_PREFIX=${MESHLIB_THIRDPARTY_ROOT_DIR} \ + -D CMAKE_BUILD_TYPE=Release \ +" + +if [ "${MR_EMSCRIPTEN}" != "ON" ] ; then + CMAKE_C_COMPILER="${CMAKE_C_COMPILER:-${CC}}" + if [ -n "${CMAKE_C_COMPILER}" ] ; then + MR_CMAKE_OPTIONS="${MR_CMAKE_OPTIONS} -D CMAKE_C_COMPILER=${CMAKE_C_COMPILER}" + fi + + CMAKE_CXX_COMPILER="${CMAKE_CXX_COMPILER:-${CXX}}" + if [ -n "${CMAKE_CXX_COMPILER}" ] ; then + MR_CMAKE_OPTIONS="${MR_CMAKE_OPTIONS} -D CMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER}" + + # special conditions for Clang on macOS + if [[ $OSTYPE == 'darwin'* ]] && command -v brew >/dev/null 2>&1; then + HOMEBREW_PREFIX=$(brew --prefix) + if [[ "${CMAKE_CXX_COMPILER}" == "${HOMEBREW_PREFIX}"* ]] ; then + # use system libc++ instead of Clang's one + MACOS_SDK_PATH=$(xcrun --show-sdk-path | xargs) # trim trailing whitespace + CXXFLAGS="-nostdinc++ -isystem ${MACOS_SDK_PATH}/usr/include/c++/v1 -isysroot ${MACOS_SDK_PATH}" + LDFLAGS="-nostdlib++ -L${MACOS_SDK_PATH}/usr/lib -lc++ -lc++abi" + # use Homebrew zlib instead of system one + MR_CMAKE_OPTIONS="${MR_CMAKE_OPTIONS} -D ZLIB_ROOT=$(brew --prefix zlib)" + fi + fi + fi +fi + +if command -v ninja >/dev/null 2>&1 ; then + MR_CMAKE_OPTIONS="${MR_CMAKE_OPTIONS} -G Ninja" +fi + +if [ "${MR_EMSCRIPTEN}" == "ON" ]; then + if [ -z "${EMSDK}" ] ; then + echo "Emscripten SDK not found" + exit 1 + fi + EMSCRIPTEN_ROOT="${EMSDK}/upstream/emscripten" + + export CFLAGS="" + export CXXFLAGS="" + export LDFLAGS="" + [[ ${MR_EMSCRIPTEN_WASM2023:=} ]] || export MR_EMSCRIPTEN_WASM2023=1 + MR_CMAKE_OPTIONS="${MR_CMAKE_OPTIONS} \ + -D CMAKE_TOOLCHAIN_FILE=${EMSCRIPTEN_ROOT}/cmake/Modules/Platform/Emscripten.cmake \ + -D CMAKE_FIND_ROOT_PATH=${MESHLIB_THIRDPARTY_ROOT_DIR} \ + -D MR_EMSCRIPTEN=1 \ + -D MR_EMSCRIPTEN_SINGLETHREAD=${MR_EMSCRIPTEN_SINGLETHREAD} \ + -D MR_EMSCRIPTEN_WASM64=${MR_EMSCRIPTEN_WASM64} \ + -D MR_EMSCRIPTEN_WASM2023=${MR_EMSCRIPTEN_WASM2023} \ + " +fi + +if [[ $OSTYPE == 'darwin'* ]]; then + NPROC=$(sysctl -n hw.logicalcpu) +else + NPROC=$(nproc) +fi + +# CPM downloads dependency sources here. Kept outside MESHLIB_THIRDPARTY_BUILD_DIR, which is +# wiped above, so a rebuild re-configures without re-downloading. +export CPM_SOURCE_CACHE="${CPM_SOURCE_CACHE:-${BASE_DIR}/thirdparty_sources}" + +# build +echo "Starting build..." +pushd "${MESHLIB_THIRDPARTY_BUILD_DIR}" +for STAGE in stage1 stage2 ; do + cmake -S ${MESHLIB_THIRDPARTY_DIR}/${STAGE} -B ${STAGE} ${MR_CMAKE_OPTIONS} + cmake --build ${STAGE} -j ${NPROC} + cmake --install ${STAGE} +done + +if [ "${MR_EMSCRIPTEN}" == "ON" ]; then + # remove excess header files as they're distributed by Emscripten + find ${MESHLIB_THIRDPARTY_ROOT_DIR}/include/boost -mindepth 1 -maxdepth 1 -not -name 'locale*' -exec rm -r "{}" \; +fi +popd + +printf "\rThirdparty build script successfully finished. Required libs located in ${MESHLIB_THIRDPARTY_ROOT_DIR} folder. You could run ./scripts/build_source.sh\n\n" diff --git a/scripts/fetch_cpm_thirdparty.sh b/scripts/fetch_cpm_thirdparty.sh new file mode 100755 index 000000000000..b9ad72050c56 --- /dev/null +++ b/scripts/fetch_cpm_thirdparty.sh @@ -0,0 +1,30 @@ +#!/bin/bash + +# This script downloads thirdparty sources with CPM from `thirdparty/cpm` without building them +# Usage: ./scripts/fetch_cpm_thirdparty.sh [PACKAGE...] +# Fetches the given packages (all of thirdparty/cpm/package-lock.cmake by default) into CPM_SOURCE_CACHE +# and prints one `SRC_=` line per package. + +set -eo pipefail + +# NOTE: realpath is not supported on older macOS versions +BASE_DIR=$( cd "$( dirname "$0" )"/.. ; pwd -P ) + +MESHLIB_THIRDPARTY_DIR=${BASE_DIR}/thirdparty/cpm/ +MESHLIB_THIRDPARTY_FETCH_DIR=${BASE_DIR}/build/thirdparty_fetch/ +MESHLIB_FETCH_OUTPUT=${MESHLIB_THIRDPARTY_FETCH_DIR}/thirdparty_sources.sh + +export CPM_SOURCE_CACHE="${CPM_SOURCE_CACHE:-${BASE_DIR}/thirdparty_sources}" + +if [ $# -gt 0 ] ; then + PACKAGES=$( IFS=';' ; echo "$*" ) +else + PACKAGES=$( sed -n 's/^set(MESHLIB_PACKAGE_\(.*\)$/\1/p' "${MESHLIB_THIRDPARTY_DIR}/package-lock.cmake" | paste -sd ';' ) +fi + +cmake -S "${MESHLIB_THIRDPARTY_DIR}/fetch" -B "${MESHLIB_THIRDPARTY_FETCH_DIR}" \ + -D CPM_SOURCE_CACHE="${CPM_SOURCE_CACHE}" \ + -D MESHLIB_FETCH_PACKAGES="${PACKAGES}" \ + -D MESHLIB_FETCH_OUTPUT="${MESHLIB_FETCH_OUTPUT}" \ + 1>&2 +cat "${MESHLIB_FETCH_OUTPUT}" diff --git a/source/MRIOExtras/CMakeLists.txt b/source/MRIOExtras/CMakeLists.txt index 7daa71194716..04b749d7f480 100644 --- a/source/MRIOExtras/CMakeLists.txt +++ b/source/MRIOExtras/CMakeLists.txt @@ -44,6 +44,9 @@ IF(NOT MRIOEXTRAS_NO_CTM) ENDIF() IF(NOT MRIOEXTRAS_NO_E57) + find_package(E57Format CONFIG REQUIRED + HINTS "${MESHLIB_THIRDPARTY_ROOT_DIR}" + ) target_link_libraries(${PROJECT_NAME} PRIVATE E57Format) IF(MSVC AND DEFINED VCPKG_INSTALLED_DIR AND DEFINED VCPKG_TARGET_TRIPLET) diff --git a/thirdparty/cpm/cmake/CPM.cmake b/thirdparty/cpm/cmake/CPM.cmake new file mode 100644 index 000000000000..e7bb536a48ed --- /dev/null +++ b/thirdparty/cpm/cmake/CPM.cmake @@ -0,0 +1,1379 @@ +# CPM.cmake - CMake's missing package manager +# =========================================== +# See https://github.com/cpm-cmake/CPM.cmake for usage and update instructions. +# +# MIT License +# ----------- +#[[ + Copyright (c) 2019-2023 Lars Melchior and contributors + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. +]] + +cmake_minimum_required(VERSION 3.14 FATAL_ERROR) + +# Initialize logging prefix +if(NOT CPM_INDENT) + set(CPM_INDENT + "CPM:" + CACHE INTERNAL "" + ) +endif() + +if(NOT COMMAND cpm_message) + function(cpm_message) + message(${ARGV}) + endfunction() +endif() + +if(DEFINED EXTRACTED_CPM_VERSION) + set(CURRENT_CPM_VERSION "${EXTRACTED_CPM_VERSION}${CPM_DEVELOPMENT}") +else() + set(CURRENT_CPM_VERSION 0.43.1) +endif() + +get_filename_component(CPM_CURRENT_DIRECTORY "${CMAKE_CURRENT_LIST_DIR}" REALPATH) +if(CPM_DIRECTORY) + if(NOT CPM_DIRECTORY STREQUAL CPM_CURRENT_DIRECTORY) + if(CPM_VERSION VERSION_LESS CURRENT_CPM_VERSION) + message( + AUTHOR_WARNING + "${CPM_INDENT} \ +A dependency is using a more recent CPM version (${CURRENT_CPM_VERSION}) than the current project (${CPM_VERSION}). \ +It is recommended to upgrade CPM to the most recent version. \ +See https://github.com/cpm-cmake/CPM.cmake for more information." + ) + endif() + if(${CMAKE_VERSION} VERSION_LESS "3.17.0") + include(FetchContent) + endif() + return() + endif() + + get_property( + CPM_INITIALIZED GLOBAL "" + PROPERTY CPM_INITIALIZED + SET + ) + if(CPM_INITIALIZED) + return() + endif() +endif() + +if(CURRENT_CPM_VERSION MATCHES "development-version") + message( + WARNING "${CPM_INDENT} Your project is using an unstable development version of CPM.cmake. \ +Please update to a recent release if possible. \ +See https://github.com/cpm-cmake/CPM.cmake for details." + ) +endif() + +set_property(GLOBAL PROPERTY CPM_INITIALIZED true) + +macro(cpm_set_policies) + # the policy allows us to change options without caching + cmake_policy(SET CMP0077 NEW) + set(CMAKE_POLICY_DEFAULT_CMP0077 NEW) + + # the policy allows us to change set(CACHE) without caching + if(POLICY CMP0126) + cmake_policy(SET CMP0126 NEW) + set(CMAKE_POLICY_DEFAULT_CMP0126 NEW) + endif() + + # The policy uses the download time for timestamp, instead of the timestamp in the archive. This + # allows for proper rebuilds when a projects url changes + if(POLICY CMP0135) + cmake_policy(SET CMP0135 NEW) + set(CMAKE_POLICY_DEFAULT_CMP0135 NEW) + endif() + + # treat relative git repository paths as being relative to the parent project's remote + if(POLICY CMP0150) + cmake_policy(SET CMP0150 NEW) + set(CMAKE_POLICY_DEFAULT_CMP0150 NEW) + endif() +endmacro() +cpm_set_policies() + +option(CPM_USE_LOCAL_PACKAGES "Always try to use `find_package` to get dependencies" + $ENV{CPM_USE_LOCAL_PACKAGES} +) +option(CPM_LOCAL_PACKAGES_ONLY "Only use `find_package` to get dependencies" + $ENV{CPM_LOCAL_PACKAGES_ONLY} +) +option(CPM_DOWNLOAD_ALL "Always download dependencies from source" $ENV{CPM_DOWNLOAD_ALL}) +option(CPM_DONT_UPDATE_MODULE_PATH "Don't update the module path to allow using find_package" + $ENV{CPM_DONT_UPDATE_MODULE_PATH} +) +option(CPM_DONT_CREATE_PACKAGE_LOCK "Don't create a package lock file in the binary path" + $ENV{CPM_DONT_CREATE_PACKAGE_LOCK} +) +option(CPM_INCLUDE_ALL_IN_PACKAGE_LOCK + "Add all packages added through CPM.cmake to the package lock" + $ENV{CPM_INCLUDE_ALL_IN_PACKAGE_LOCK} +) +option(CPM_USE_NAMED_CACHE_DIRECTORIES + "Use additional directory of package name in cache on the most nested level." + $ENV{CPM_USE_NAMED_CACHE_DIRECTORIES} +) + +set(CPM_VERSION + ${CURRENT_CPM_VERSION} + CACHE INTERNAL "" +) +set(CPM_DIRECTORY + ${CPM_CURRENT_DIRECTORY} + CACHE INTERNAL "" +) +set(CPM_FILE + ${CMAKE_CURRENT_LIST_FILE} + CACHE INTERNAL "" +) +set(CPM_PACKAGES + "" + CACHE INTERNAL "" +) +set(CPM_DRY_RUN + OFF + CACHE INTERNAL "Don't download or configure dependencies (for testing)" +) + +if(DEFINED ENV{CPM_SOURCE_CACHE}) + set(CPM_SOURCE_CACHE_DEFAULT $ENV{CPM_SOURCE_CACHE}) +else() + set(CPM_SOURCE_CACHE_DEFAULT OFF) +endif() + +set(CPM_SOURCE_CACHE + ${CPM_SOURCE_CACHE_DEFAULT} + CACHE PATH "Directory to download CPM dependencies" +) + +if(NOT CPM_DONT_UPDATE_MODULE_PATH AND NOT DEFINED CMAKE_FIND_PACKAGE_REDIRECTS_DIR) + set(CPM_MODULE_PATH + "${CMAKE_BINARY_DIR}/CPM_modules" + CACHE INTERNAL "" + ) + # remove old modules + file(REMOVE_RECURSE ${CPM_MODULE_PATH}) + file(MAKE_DIRECTORY ${CPM_MODULE_PATH}) + # locally added CPM modules should override global packages + set(CMAKE_MODULE_PATH "${CPM_MODULE_PATH};${CMAKE_MODULE_PATH}") +endif() + +if(NOT CPM_DONT_CREATE_PACKAGE_LOCK) + set(CPM_PACKAGE_LOCK_FILE + "${CMAKE_BINARY_DIR}/cpm-package-lock.cmake" + CACHE INTERNAL "" + ) + file(WRITE ${CPM_PACKAGE_LOCK_FILE} + "# CPM Package Lock\n# This file should be committed to version control\n\n" + ) +endif() + +include(FetchContent) + +# Try to infer package name from git repository uri (path or url) +function(cpm_package_name_from_git_uri URI RESULT) + if("${URI}" MATCHES "([^/:]+)/?.git/?$") + set(${RESULT} + ${CMAKE_MATCH_1} + PARENT_SCOPE + ) + else() + unset(${RESULT} PARENT_SCOPE) + endif() +endfunction() + +# Find the shortest hash that can be used eg, if origin_hash is +# cccb77ae9609d2768ed80dd42cec54f77b1f1455 the following files will be checked, until one is found +# that is either empty (allowing us to assign origin_hash), or whose contents matches ${origin_hash} +# +# * .../cccb.hash +# * .../cccb77ae.hash +# * .../cccb77ae9609.hash +# * .../cccb77ae9609d276.hash +# * etc +# +# We will be able to use a shorter path with very high probability, but in the (rare) event that the +# first couple characters collide, we will check longer and longer substrings. +function(cpm_get_shortest_hash source_cache_dir origin_hash short_hash_output_var) + # for compatibility with caches populated by a previous version of CPM, check if a directory using + # the full hash already exists + if(EXISTS "${source_cache_dir}/${origin_hash}") + set(${short_hash_output_var} + "${origin_hash}" + PARENT_SCOPE + ) + return() + endif() + + foreach(len RANGE 4 40 4) + string(SUBSTRING "${origin_hash}" 0 ${len} short_hash) + set(hash_lock ${source_cache_dir}/${short_hash}.lock) + set(hash_fp ${source_cache_dir}/${short_hash}.hash) + # Take a lock, so we don't have a race condition with another instance of cmake. We will release + # this lock when we can, however, if there is an error, we want to ensure it gets released on + # it's own on exit from the function. + file(LOCK ${hash_lock} GUARD FUNCTION) + + # Load the contents of .../${short_hash}.hash + file(TOUCH ${hash_fp}) + file(READ ${hash_fp} hash_fp_contents) + + if(hash_fp_contents STREQUAL "") + # Write the origin hash + file(WRITE ${hash_fp} ${origin_hash}) + file(LOCK ${hash_lock} RELEASE) + break() + elseif(hash_fp_contents STREQUAL origin_hash) + file(LOCK ${hash_lock} RELEASE) + break() + else() + file(LOCK ${hash_lock} RELEASE) + endif() + endforeach() + set(${short_hash_output_var} + "${short_hash}" + PARENT_SCOPE + ) +endfunction() + +# Try to infer package name and version from a url +function(cpm_package_name_and_ver_from_url url outName outVer) + if(url MATCHES + "[/\\?]([a-zA-Z0-9_\\.-]+)\\.(tar|tar\\.gz|tar\\.bz2|tar\\.xz|tar\\.zst|zip|ZIP)(\\?|/|$)" + ) + # We matched an archive + set(filename "${CMAKE_MATCH_1}") + + if(filename MATCHES "([a-zA-Z0-9_\\.-]+)[_-]v?(([0-9]+\\.)*[0-9]+[a-zA-Z0-9]*)") + # We matched - (ie foo-1.2.3) + set(${outName} + "${CMAKE_MATCH_1}" + PARENT_SCOPE + ) + set(${outVer} + "${CMAKE_MATCH_2}" + PARENT_SCOPE + ) + elseif(filename MATCHES "(([0-9]+\\.)+[0-9]+[a-zA-Z0-9]*)") + # We couldn't find a name, but we found a version + # + # In many cases (which we don't handle here) the url would look something like + # `irrelevant/ACTUAL_PACKAGE_NAME/irrelevant/1.2.3.zip`. In such a case we can't possibly + # distinguish the package name from the irrelevant bits. Moreover if we try to match the + # package name from the filename, we'd get bogus at best. + unset(${outName} PARENT_SCOPE) + set(${outVer} + "${CMAKE_MATCH_1}" + PARENT_SCOPE + ) + else() + # Boldly assume that the file name is the package name. + # + # Yes, something like `irrelevant/ACTUAL_NAME/irrelevant/download.zip` will ruin our day, but + # such cases should be quite rare. No popular service does this... we think. + set(${outName} + "${filename}" + PARENT_SCOPE + ) + unset(${outVer} PARENT_SCOPE) + endif() + else() + # No ideas yet what to do with non-archives + unset(${outName} PARENT_SCOPE) + unset(${outVer} PARENT_SCOPE) + endif() +endfunction() + +function(cpm_find_package NAME VERSION) + string(REPLACE " " ";" EXTRA_ARGS "${ARGN}") + find_package(${NAME} ${VERSION} ${EXTRA_ARGS} QUIET) + if(${CPM_ARGS_NAME}_FOUND) + if(DEFINED ${CPM_ARGS_NAME}_VERSION) + set(VERSION ${${CPM_ARGS_NAME}_VERSION}) + endif() + cpm_message(STATUS "${CPM_INDENT} Using local package ${CPM_ARGS_NAME}@${VERSION}") + CPMRegisterPackage(${CPM_ARGS_NAME} "${VERSION}") + set(CPM_PACKAGE_FOUND + YES + PARENT_SCOPE + ) + else() + set(CPM_PACKAGE_FOUND + NO + PARENT_SCOPE + ) + endif() +endfunction() + +# Create a custom FindXXX.cmake module for a CPM package This prevents `find_package(NAME)` from +# finding the system library +function(cpm_create_module_file Name) + if(NOT CPM_DONT_UPDATE_MODULE_PATH) + if(DEFINED CMAKE_FIND_PACKAGE_REDIRECTS_DIR) + # Redirect find_package calls to the CPM package. This is what FetchContent does when you set + # OVERRIDE_FIND_PACKAGE. The CMAKE_FIND_PACKAGE_REDIRECTS_DIR works for find_package in CONFIG + # mode, unlike the Find${Name}.cmake fallback. CMAKE_FIND_PACKAGE_REDIRECTS_DIR is not defined + # in script mode, or in CMake < 3.24. + # https://cmake.org/cmake/help/latest/module/FetchContent.html#fetchcontent-find-package-integration-examples + string(TOLOWER ${Name} NameLower) + file(WRITE ${CMAKE_FIND_PACKAGE_REDIRECTS_DIR}/${NameLower}-config.cmake + "include(\"\${CMAKE_CURRENT_LIST_DIR}/${NameLower}-extra.cmake\" OPTIONAL)\n" + "include(\"\${CMAKE_CURRENT_LIST_DIR}/${Name}Extra.cmake\" OPTIONAL)\n" + ) + file(WRITE ${CMAKE_FIND_PACKAGE_REDIRECTS_DIR}/${NameLower}-config-version.cmake + "set(PACKAGE_VERSION_COMPATIBLE TRUE)\n" "set(PACKAGE_VERSION_EXACT TRUE)\n" + ) + else() + file(WRITE ${CPM_MODULE_PATH}/Find${Name}.cmake + "include(\"${CPM_FILE}\")\n${ARGN}\nset(${Name}_FOUND TRUE)" + ) + endif() + endif() +endfunction() + +# Find a package locally or fallback to CPMAddPackage +function(CPMFindPackage) + set(oneValueArgs NAME VERSION GIT_TAG FIND_PACKAGE_ARGUMENTS) + + cmake_parse_arguments(CPM_ARGS "" "${oneValueArgs}" "" ${ARGN}) + + if(NOT DEFINED CPM_ARGS_VERSION) + if(DEFINED CPM_ARGS_GIT_TAG) + cpm_get_version_from_git_tag("${CPM_ARGS_GIT_TAG}" CPM_ARGS_VERSION) + endif() + endif() + + set(downloadPackage ${CPM_DOWNLOAD_ALL}) + if(DEFINED CPM_DOWNLOAD_${CPM_ARGS_NAME}) + set(downloadPackage ${CPM_DOWNLOAD_${CPM_ARGS_NAME}}) + elseif(DEFINED ENV{CPM_DOWNLOAD_${CPM_ARGS_NAME}}) + set(downloadPackage $ENV{CPM_DOWNLOAD_${CPM_ARGS_NAME}}) + endif() + if(downloadPackage) + CPMAddPackage(${ARGN}) + cpm_export_variables(${CPM_ARGS_NAME}) + return() + endif() + + cpm_find_package(${CPM_ARGS_NAME} "${CPM_ARGS_VERSION}" ${CPM_ARGS_FIND_PACKAGE_ARGUMENTS}) + + if(NOT CPM_PACKAGE_FOUND) + CPMAddPackage(${ARGN}) + cpm_export_variables(${CPM_ARGS_NAME}) + endif() + +endfunction() + +# checks if a package has been added before +function(cpm_check_if_package_already_added CPM_ARGS_NAME CPM_ARGS_VERSION) + if("${CPM_ARGS_NAME}" IN_LIST CPM_PACKAGES) + CPMGetPackageVersion(${CPM_ARGS_NAME} CPM_PACKAGE_VERSION) + if("${CPM_PACKAGE_VERSION}" VERSION_LESS "${CPM_ARGS_VERSION}") + message( + WARNING + "${CPM_INDENT} Requires a newer version of ${CPM_ARGS_NAME} (${CPM_ARGS_VERSION}) than currently included (${CPM_PACKAGE_VERSION})." + ) + endif() + cpm_get_fetch_properties(${CPM_ARGS_NAME}) + set(${CPM_ARGS_NAME}_ADDED NO) + set(CPM_PACKAGE_ALREADY_ADDED + YES + PARENT_SCOPE + ) + cpm_export_variables(${CPM_ARGS_NAME}) + else() + set(CPM_PACKAGE_ALREADY_ADDED + NO + PARENT_SCOPE + ) + endif() +endfunction() + +# Parse the argument of CPMAddPackage in case a single one was provided and convert it to a list of +# arguments which can then be parsed idiomatically. For example gh:foo/bar@1.2.3 will be converted +# to: GITHUB_REPOSITORY;foo/bar;VERSION;1.2.3 +function(cpm_parse_add_package_single_arg arg outArgs) + # Look for a scheme + if("${arg}" MATCHES "^([a-zA-Z]+):(.+)$") + string(TOLOWER "${CMAKE_MATCH_1}" scheme) + set(uri "${CMAKE_MATCH_2}") + + # Check for CPM-specific schemes + if(scheme STREQUAL "gh") + set(out "GITHUB_REPOSITORY;${uri}") + set(packageType "git") + elseif(scheme STREQUAL "gl") + set(out "GITLAB_REPOSITORY;${uri}") + set(packageType "git") + elseif(scheme STREQUAL "bb") + set(out "BITBUCKET_REPOSITORY;${uri}") + set(packageType "git") + # A CPM-specific scheme was not found. Looks like this is a generic URL so try to determine + # type + elseif(arg MATCHES ".git/?(@|#|$)") + set(out "GIT_REPOSITORY;${arg}") + set(packageType "git") + else() + # Fall back to a URL + set(out "URL;${arg}") + set(packageType "archive") + + # We could also check for SVN since FetchContent supports it, but SVN is so rare these days. + # We just won't bother with the additional complexity it will induce in this function. SVN is + # done by multi-arg + endif() + else() + if(arg MATCHES ".git/?(@|#|$)") + set(out "GIT_REPOSITORY;${arg}") + set(packageType "git") + else() + # Give up + message(FATAL_ERROR "${CPM_INDENT} Can't determine package type of '${arg}'") + endif() + endif() + + # For all packages we interpret @... as version. Only replace the last occurrence. Thus URIs + # containing '@' can be used + string(REGEX REPLACE "@([^@]+)$" ";VERSION;\\1" out "${out}") + + # Parse the rest according to package type + if(packageType STREQUAL "git") + # For git repos we interpret #... as a tag or branch or commit hash + string(REGEX REPLACE "#([^#]+)$" ";GIT_TAG;\\1" out "${out}") + elseif(packageType STREQUAL "archive") + # For archives we interpret #... as a URL hash. + string(REGEX REPLACE "#([^#]+)$" ";URL_HASH;\\1" out "${out}") + # We don't try to parse the version if it's not provided explicitly. cpm_get_version_from_url + # should do this at a later point + else() + # We should never get here. This is an assertion and hitting it means there's a problem with the + # code above. A packageType was set, but not handled by this if-else. + message(FATAL_ERROR "${CPM_INDENT} Unsupported package type '${packageType}' of '${arg}'") + endif() + + set(${outArgs} + ${out} + PARENT_SCOPE + ) +endfunction() + +# Check that the working directory for a git repo is clean +function(cpm_check_git_working_dir_is_clean repoPath gitTag isClean) + + find_package(Git REQUIRED) + + if(NOT GIT_EXECUTABLE) + # No git executable, assume directory is clean + set(${isClean} + TRUE + PARENT_SCOPE + ) + return() + endif() + + # check for uncommitted changes + execute_process( + COMMAND ${GIT_EXECUTABLE} status --porcelain + RESULT_VARIABLE resultGitStatus + OUTPUT_VARIABLE repoStatus + OUTPUT_STRIP_TRAILING_WHITESPACE ERROR_QUIET + WORKING_DIRECTORY ${repoPath} + ) + if(resultGitStatus) + # not supposed to happen, assume clean anyway + message(WARNING "${CPM_INDENT} Calling git status on folder ${repoPath} failed") + set(${isClean} + TRUE + PARENT_SCOPE + ) + return() + endif() + + if(NOT "${repoStatus}" STREQUAL "") + set(${isClean} + FALSE + PARENT_SCOPE + ) + return() + endif() + + # check for committed changes + execute_process( + COMMAND ${GIT_EXECUTABLE} diff -s --exit-code ${gitTag} + RESULT_VARIABLE resultGitDiff + OUTPUT_STRIP_TRAILING_WHITESPACE OUTPUT_QUIET + WORKING_DIRECTORY ${repoPath} + ) + + if(${resultGitDiff} EQUAL 0) + set(${isClean} + TRUE + PARENT_SCOPE + ) + else() + set(${isClean} + FALSE + PARENT_SCOPE + ) + endif() + +endfunction() + +# Add PATCH_COMMAND to CPM_ARGS_UNPARSED_ARGUMENTS. This method consumes a list of files in ARGN +# then generates a `PATCH_COMMAND` appropriate for `ExternalProject_Add()`. This command is appended +# to the parent scope's `CPM_ARGS_UNPARSED_ARGUMENTS`. +function(cpm_add_patches) + # Return if no patch files are supplied. + if(NOT ARGN) + return() + endif() + + # Find the patch program. + find_program(PATCH_EXECUTABLE patch) + if(CMAKE_HOST_WIN32 AND NOT PATCH_EXECUTABLE) + # The Windows git executable is distributed with patch.exe. Find the path to the executable, if + # it exists, then search `../usr/bin` and `../../usr/bin` for patch.exe. + find_package(Git QUIET) + if(GIT_EXECUTABLE) + get_filename_component(extra_search_path ${GIT_EXECUTABLE} DIRECTORY) + get_filename_component(extra_search_path_1up ${extra_search_path} DIRECTORY) + get_filename_component(extra_search_path_2up ${extra_search_path_1up} DIRECTORY) + find_program( + PATCH_EXECUTABLE patch HINTS "${extra_search_path_1up}/usr/bin" + "${extra_search_path_2up}/usr/bin" + ) + endif() + endif() + if(NOT PATCH_EXECUTABLE) + message(FATAL_ERROR "Couldn't find `patch` executable to use with PATCHES keyword.") + endif() + + # Create a temporary + set(temp_list ${CPM_ARGS_UNPARSED_ARGUMENTS}) + + # Ensure each file exists (or error out) and add it to the list. + set(first_item True) + foreach(PATCH_FILE ${ARGN}) + # Make sure the patch file exists, if we can't find it, try again in the current directory. + if(NOT EXISTS "${PATCH_FILE}") + if(NOT EXISTS "${CMAKE_CURRENT_LIST_DIR}/${PATCH_FILE}") + message(FATAL_ERROR "Couldn't find patch file: '${PATCH_FILE}'") + endif() + set(PATCH_FILE "${CMAKE_CURRENT_LIST_DIR}/${PATCH_FILE}") + endif() + + # Convert to absolute path for use with patch file command. + get_filename_component(PATCH_FILE "${PATCH_FILE}" ABSOLUTE) + + # The first patch entry must be preceded by "PATCH_COMMAND" while the following items are + # preceded by "&&". + if(first_item) + set(first_item False) + list(APPEND temp_list "PATCH_COMMAND") + else() + list(APPEND temp_list "&&") + endif() + # Add the patch command to the list + list(APPEND temp_list "${PATCH_EXECUTABLE}" "-p1" "<" "${PATCH_FILE}") + endforeach() + + # Move temp out into parent scope. + set(CPM_ARGS_UNPARSED_ARGUMENTS + ${temp_list} + PARENT_SCOPE + ) + +endfunction() + +# method to overwrite internal FetchContent properties, to allow using CPM.cmake to overload +# FetchContent calls. As these are internal cmake properties, this method should be used carefully +# and may need modification in future CMake versions. Source: +# https://github.com/Kitware/CMake/blob/dc3d0b5a0a7d26d43d6cfeb511e224533b5d188f/Modules/FetchContent.cmake#L1152 +function(cpm_override_fetchcontent contentName) + cmake_parse_arguments(PARSE_ARGV 1 arg "" "SOURCE_DIR;BINARY_DIR" "") + if(NOT "${arg_UNPARSED_ARGUMENTS}" STREQUAL "") + message(FATAL_ERROR "${CPM_INDENT} Unsupported arguments: ${arg_UNPARSED_ARGUMENTS}") + endif() + + string(TOLOWER ${contentName} contentNameLower) + set(prefix "_FetchContent_${contentNameLower}") + + set(propertyName "${prefix}_sourceDir") + define_property( + GLOBAL + PROPERTY ${propertyName} + BRIEF_DOCS "Internal implementation detail of FetchContent_Populate()" + FULL_DOCS "Details used by FetchContent_Populate() for ${contentName}" + ) + set_property(GLOBAL PROPERTY ${propertyName} "${arg_SOURCE_DIR}") + + set(propertyName "${prefix}_binaryDir") + define_property( + GLOBAL + PROPERTY ${propertyName} + BRIEF_DOCS "Internal implementation detail of FetchContent_Populate()" + FULL_DOCS "Details used by FetchContent_Populate() for ${contentName}" + ) + set_property(GLOBAL PROPERTY ${propertyName} "${arg_BINARY_DIR}") + + set(propertyName "${prefix}_populated") + define_property( + GLOBAL + PROPERTY ${propertyName} + BRIEF_DOCS "Internal implementation detail of FetchContent_Populate()" + FULL_DOCS "Details used by FetchContent_Populate() for ${contentName}" + ) + set_property(GLOBAL PROPERTY ${propertyName} TRUE) +endfunction() + +# Download and add a package from source +function(CPMAddPackage) + cpm_set_policies() + + set(oneValueArgs + NAME + FORCE + VERSION + GIT_TAG + DOWNLOAD_ONLY + GITHUB_REPOSITORY + GITLAB_REPOSITORY + BITBUCKET_REPOSITORY + GIT_REPOSITORY + SOURCE_DIR + FIND_PACKAGE_ARGUMENTS + NO_CACHE + SYSTEM + GIT_SHALLOW + EXCLUDE_FROM_ALL + SOURCE_SUBDIR + CUSTOM_CACHE_KEY + ) + + set(multiValueArgs URL OPTIONS DOWNLOAD_COMMAND PATCHES) + + list(LENGTH ARGN argnLength) + + # Parse single shorthand argument + if(argnLength EQUAL 1) + cpm_parse_add_package_single_arg("${ARGN}" ARGN) + + # The shorthand syntax implies EXCLUDE_FROM_ALL and SYSTEM + set(ARGN "${ARGN};EXCLUDE_FROM_ALL;YES;SYSTEM;YES;") + + # Parse URI shorthand argument + elseif(argnLength GREATER 1 AND "${ARGV0}" STREQUAL "URI") + list(REMOVE_AT ARGN 0 1) # remove "URI gh:<...>@version#tag" + cpm_parse_add_package_single_arg("${ARGV1}" ARGV0) + + set(ARGN "${ARGV0};EXCLUDE_FROM_ALL;YES;SYSTEM;YES;${ARGN}") + endif() + + cmake_parse_arguments(CPM_ARGS "" "${oneValueArgs}" "${multiValueArgs}" "${ARGN}") + + # Set default values for arguments + if(NOT DEFINED CPM_ARGS_VERSION) + if(DEFINED CPM_ARGS_GIT_TAG) + cpm_get_version_from_git_tag("${CPM_ARGS_GIT_TAG}" CPM_ARGS_VERSION) + endif() + endif() + + if(CPM_ARGS_DOWNLOAD_ONLY) + set(DOWNLOAD_ONLY ${CPM_ARGS_DOWNLOAD_ONLY}) + else() + set(DOWNLOAD_ONLY NO) + endif() + + if(DEFINED CPM_ARGS_GITHUB_REPOSITORY) + set(CPM_ARGS_GIT_REPOSITORY "https://github.com/${CPM_ARGS_GITHUB_REPOSITORY}.git") + elseif(DEFINED CPM_ARGS_GITLAB_REPOSITORY) + set(CPM_ARGS_GIT_REPOSITORY "https://gitlab.com/${CPM_ARGS_GITLAB_REPOSITORY}.git") + elseif(DEFINED CPM_ARGS_BITBUCKET_REPOSITORY) + set(CPM_ARGS_GIT_REPOSITORY "https://bitbucket.org/${CPM_ARGS_BITBUCKET_REPOSITORY}.git") + endif() + + if(DEFINED CPM_ARGS_GIT_REPOSITORY) + list(APPEND CPM_ARGS_UNPARSED_ARGUMENTS GIT_REPOSITORY ${CPM_ARGS_GIT_REPOSITORY}) + if(NOT DEFINED CPM_ARGS_GIT_TAG) + set(CPM_ARGS_GIT_TAG v${CPM_ARGS_VERSION}) + endif() + + # If a name wasn't provided, try to infer it from the git repo + if(NOT DEFINED CPM_ARGS_NAME) + cpm_package_name_from_git_uri(${CPM_ARGS_GIT_REPOSITORY} CPM_ARGS_NAME) + endif() + endif() + + set(CPM_SKIP_FETCH FALSE) + + if(DEFINED CPM_ARGS_GIT_TAG) + list(APPEND CPM_ARGS_UNPARSED_ARGUMENTS GIT_TAG ${CPM_ARGS_GIT_TAG}) + # If GIT_SHALLOW is explicitly specified, honor the value. + if(DEFINED CPM_ARGS_GIT_SHALLOW) + list(APPEND CPM_ARGS_UNPARSED_ARGUMENTS GIT_SHALLOW ${CPM_ARGS_GIT_SHALLOW}) + endif() + endif() + + if(DEFINED CPM_ARGS_URL) + # If a name or version aren't provided, try to infer them from the URL + list(GET CPM_ARGS_URL 0 firstUrl) + cpm_package_name_and_ver_from_url(${firstUrl} nameFromUrl verFromUrl) + # If we fail to obtain name and version from the first URL, we could try other URLs if any. + # However multiple URLs are expected to be quite rare, so for now we won't bother. + + # If the caller provided their own name and version, they trump the inferred ones. + if(NOT DEFINED CPM_ARGS_NAME) + set(CPM_ARGS_NAME ${nameFromUrl}) + endif() + if(NOT DEFINED CPM_ARGS_VERSION) + set(CPM_ARGS_VERSION ${verFromUrl}) + endif() + + list(APPEND CPM_ARGS_UNPARSED_ARGUMENTS URL "${CPM_ARGS_URL}") + endif() + + # Check for required arguments + + if(NOT DEFINED CPM_ARGS_NAME) + message( + FATAL_ERROR + "${CPM_INDENT} 'NAME' was not provided and couldn't be automatically inferred for package added with arguments: '${ARGN}'" + ) + endif() + + # Check if package has been added before + cpm_check_if_package_already_added(${CPM_ARGS_NAME} "${CPM_ARGS_VERSION}") + if(CPM_PACKAGE_ALREADY_ADDED) + cpm_export_variables(${CPM_ARGS_NAME}) + return() + endif() + + if(NOT DEFINED CPM_${CPM_ARGS_NAME}_SOURCE AND DEFINED ENV{CPM_${CPM_ARGS_NAME}_SOURCE}) + # Normalize separators to support Windows paths when reading from environment variables. + file(TO_CMAKE_PATH "$ENV{CPM_${CPM_ARGS_NAME}_SOURCE}" CPM_${CPM_ARGS_NAME}_SOURCE) + message(WARNING "${CPM_INDENT} '${CPM_ARGS_NAME}' version overridden by environment variable " + "CPM_${CPM_ARGS_NAME}_SOURCE='${CPM_${CPM_ARGS_NAME}_SOURCE}'" + ) + endif() + + # Check for manual overrides + if(NOT CPM_ARGS_FORCE AND NOT "${CPM_${CPM_ARGS_NAME}_SOURCE}" STREQUAL "") + set(PACKAGE_SOURCE ${CPM_${CPM_ARGS_NAME}_SOURCE}) + set(CPM_${CPM_ARGS_NAME}_SOURCE "") + if(NOT DEFINED ENV{CPM_${CPM_ARGS_NAME}_SOURCE}) + message(WARNING "${CPM_INDENT} '${CPM_ARGS_NAME}' version overridden by CMake variable " + "CPM_${CPM_ARGS_NAME}_SOURCE='${PACKAGE_SOURCE}'" + ) + endif() + CPMAddPackage( + NAME "${CPM_ARGS_NAME}" + SOURCE_DIR "${PACKAGE_SOURCE}" + EXCLUDE_FROM_ALL "${CPM_ARGS_EXCLUDE_FROM_ALL}" + SYSTEM "${CPM_ARGS_SYSTEM}" + PATCHES "${CPM_ARGS_PATCHES}" + OPTIONS "${CPM_ARGS_OPTIONS}" + SOURCE_SUBDIR "${CPM_ARGS_SOURCE_SUBDIR}" + DOWNLOAD_ONLY "${DOWNLOAD_ONLY}" + FORCE True + ) + cpm_export_variables(${CPM_ARGS_NAME}) + return() + endif() + + # Check for available declaration + if(NOT CPM_ARGS_FORCE AND NOT "${CPM_DECLARATION_${CPM_ARGS_NAME}}" STREQUAL "") + set(declaration ${CPM_DECLARATION_${CPM_ARGS_NAME}}) + set(CPM_DECLARATION_${CPM_ARGS_NAME} "") + CPMAddPackage(${declaration}) + cpm_export_variables(${CPM_ARGS_NAME}) + # checking again to ensure version and option compatibility + cpm_check_if_package_already_added(${CPM_ARGS_NAME} "${CPM_ARGS_VERSION}") + return() + endif() + + if(NOT CPM_ARGS_FORCE) + if(CPM_USE_LOCAL_PACKAGES OR CPM_LOCAL_PACKAGES_ONLY) + cpm_find_package(${CPM_ARGS_NAME} "${CPM_ARGS_VERSION}" ${CPM_ARGS_FIND_PACKAGE_ARGUMENTS}) + + if(CPM_PACKAGE_FOUND) + cpm_export_variables(${CPM_ARGS_NAME}) + return() + endif() + + if(CPM_LOCAL_PACKAGES_ONLY) + message( + SEND_ERROR + "${CPM_INDENT} ${CPM_ARGS_NAME} not found via find_package(${CPM_ARGS_NAME} ${CPM_ARGS_VERSION})" + ) + endif() + endif() + endif() + + CPMRegisterPackage("${CPM_ARGS_NAME}" "${CPM_ARGS_VERSION}") + + if(DEFINED CPM_ARGS_GIT_TAG) + set(PACKAGE_INFO "${CPM_ARGS_GIT_TAG}") + elseif(DEFINED CPM_ARGS_SOURCE_DIR) + set(PACKAGE_INFO "${CPM_ARGS_SOURCE_DIR}") + else() + set(PACKAGE_INFO "${CPM_ARGS_VERSION}") + endif() + + if(DEFINED FETCHCONTENT_BASE_DIR) + # respect user's FETCHCONTENT_BASE_DIR if set + set(CPM_FETCHCONTENT_BASE_DIR ${FETCHCONTENT_BASE_DIR}) + else() + set(CPM_FETCHCONTENT_BASE_DIR ${CMAKE_BINARY_DIR}/_deps) + endif() + + cpm_add_patches(${CPM_ARGS_PATCHES}) + + if(DEFINED CPM_ARGS_DOWNLOAD_COMMAND) + list(APPEND CPM_ARGS_UNPARSED_ARGUMENTS DOWNLOAD_COMMAND ${CPM_ARGS_DOWNLOAD_COMMAND}) + elseif(DEFINED CPM_ARGS_SOURCE_DIR) + list(APPEND CPM_ARGS_UNPARSED_ARGUMENTS SOURCE_DIR ${CPM_ARGS_SOURCE_DIR}) + if(NOT IS_ABSOLUTE ${CPM_ARGS_SOURCE_DIR}) + # Expand `CPM_ARGS_SOURCE_DIR` relative path. This is important because EXISTS doesn't work + # for relative paths. + get_filename_component( + source_directory ${CPM_ARGS_SOURCE_DIR} REALPATH BASE_DIR ${CMAKE_CURRENT_BINARY_DIR} + ) + else() + set(source_directory ${CPM_ARGS_SOURCE_DIR}) + endif() + if(NOT EXISTS ${source_directory}) + string(TOLOWER ${CPM_ARGS_NAME} lower_case_name) + # remove timestamps so CMake will re-download the dependency + file(REMOVE_RECURSE "${CPM_FETCHCONTENT_BASE_DIR}/${lower_case_name}-subbuild") + endif() + elseif(CPM_SOURCE_CACHE AND NOT CPM_ARGS_NO_CACHE) + string(TOLOWER ${CPM_ARGS_NAME} lower_case_name) + set(origin_parameters ${CPM_ARGS_UNPARSED_ARGUMENTS}) + list(SORT origin_parameters) + if(CPM_ARGS_CUSTOM_CACHE_KEY) + # Application set a custom unique directory name + set(download_directory ${CPM_SOURCE_CACHE}/${lower_case_name}/${CPM_ARGS_CUSTOM_CACHE_KEY}) + elseif(CPM_USE_NAMED_CACHE_DIRECTORIES) + string(SHA1 origin_hash "${origin_parameters};NEW_CACHE_STRUCTURE_TAG") + cpm_get_shortest_hash( + "${CPM_SOURCE_CACHE}/${lower_case_name}" # source cache directory + "${origin_hash}" # Input hash + origin_hash # Computed hash + ) + set(download_directory ${CPM_SOURCE_CACHE}/${lower_case_name}/${origin_hash}/${CPM_ARGS_NAME}) + else() + string(SHA1 origin_hash "${origin_parameters}") + cpm_get_shortest_hash( + "${CPM_SOURCE_CACHE}/${lower_case_name}" # source cache directory + "${origin_hash}" # Input hash + origin_hash # Computed hash + ) + set(download_directory ${CPM_SOURCE_CACHE}/${lower_case_name}/${origin_hash}) + endif() + # Expand `download_directory` relative path. This is important because EXISTS doesn't work for + # relative paths. + get_filename_component(download_directory ${download_directory} ABSOLUTE) + list(APPEND CPM_ARGS_UNPARSED_ARGUMENTS SOURCE_DIR ${download_directory}) + + if(CPM_SOURCE_CACHE) + file(LOCK ${download_directory}/../cmake.lock) + endif() + + if(EXISTS ${download_directory}) + if(CPM_SOURCE_CACHE) + file(LOCK ${download_directory}/../cmake.lock RELEASE) + endif() + + cpm_store_fetch_properties( + ${CPM_ARGS_NAME} "${download_directory}" + "${CPM_FETCHCONTENT_BASE_DIR}/${lower_case_name}-build" + ) + cpm_get_fetch_properties("${CPM_ARGS_NAME}") + + if(DEFINED CPM_ARGS_GIT_TAG AND NOT (PATCH_COMMAND IN_LIST CPM_ARGS_UNPARSED_ARGUMENTS)) + # warn if cache has been changed since checkout + cpm_check_git_working_dir_is_clean(${download_directory} ${CPM_ARGS_GIT_TAG} IS_CLEAN) + if(NOT ${IS_CLEAN}) + message( + WARNING "${CPM_INDENT} Cache for ${CPM_ARGS_NAME} (${download_directory}) is dirty" + ) + endif() + endif() + + cpm_add_subdirectory( + "${CPM_ARGS_NAME}" + "${DOWNLOAD_ONLY}" + "${${CPM_ARGS_NAME}_SOURCE_DIR}/${CPM_ARGS_SOURCE_SUBDIR}" + "${${CPM_ARGS_NAME}_BINARY_DIR}" + "${CPM_ARGS_EXCLUDE_FROM_ALL}" + "${CPM_ARGS_SYSTEM}" + "${CPM_ARGS_OPTIONS}" + ) + set(PACKAGE_INFO "${PACKAGE_INFO} at ${download_directory}") + + # As the source dir is already cached/populated, we override the call to FetchContent. + set(CPM_SKIP_FETCH TRUE) + cpm_override_fetchcontent( + "${lower_case_name}" SOURCE_DIR "${${CPM_ARGS_NAME}_SOURCE_DIR}/${CPM_ARGS_SOURCE_SUBDIR}" + BINARY_DIR "${${CPM_ARGS_NAME}_BINARY_DIR}" + ) + + else() + # Enable shallow clone when GIT_TAG is not a commit hash. Our guess may not be accurate, but + # it should guarantee no commit hash get mis-detected. + if(NOT DEFINED CPM_ARGS_GIT_SHALLOW) + cpm_is_git_tag_commit_hash("${CPM_ARGS_GIT_TAG}" IS_HASH) + if(NOT ${IS_HASH}) + list(APPEND CPM_ARGS_UNPARSED_ARGUMENTS GIT_SHALLOW TRUE) + endif() + endif() + + # remove timestamps so CMake will re-download the dependency + file(REMOVE_RECURSE ${CPM_FETCHCONTENT_BASE_DIR}/${lower_case_name}-subbuild) + set(PACKAGE_INFO "${PACKAGE_INFO} to ${download_directory}") + endif() + endif() + + if(NOT "${DOWNLOAD_ONLY}") + cpm_create_module_file(${CPM_ARGS_NAME} "CPMAddPackage(\"${ARGN}\")") + endif() + + if(CPM_PACKAGE_LOCK_ENABLED) + if((CPM_ARGS_VERSION AND NOT CPM_ARGS_SOURCE_DIR) OR CPM_INCLUDE_ALL_IN_PACKAGE_LOCK) + cpm_add_to_package_lock(${CPM_ARGS_NAME} "${ARGN}") + elseif(CPM_ARGS_SOURCE_DIR) + cpm_add_comment_to_package_lock(${CPM_ARGS_NAME} "local directory") + else() + cpm_add_comment_to_package_lock(${CPM_ARGS_NAME} "${ARGN}") + endif() + endif() + + cpm_message( + STATUS "${CPM_INDENT} Adding package ${CPM_ARGS_NAME}@${CPM_ARGS_VERSION} (${PACKAGE_INFO})" + ) + + if(NOT CPM_SKIP_FETCH) + # CMake 3.28 added EXCLUDE, SYSTEM (3.25), and SOURCE_SUBDIR (3.18) to FetchContent_Declare. + # Calling FetchContent_MakeAvailable will then internally forward these options to + # add_subdirectory. Up until these changes, we had to call FetchContent_Populate and + # add_subdirectory separately, which is no longer necessary and has been deprecated as of 3.30. + # A Bug in CMake prevents us to use the non-deprecated functions until 3.30.3. + set(fetchContentDeclareExtraArgs "") + if(${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.30.3") + if(${CPM_ARGS_EXCLUDE_FROM_ALL}) + list(APPEND fetchContentDeclareExtraArgs EXCLUDE_FROM_ALL) + endif() + if(${CPM_ARGS_SYSTEM}) + list(APPEND fetchContentDeclareExtraArgs SYSTEM) + endif() + if(DEFINED CPM_ARGS_SOURCE_SUBDIR) + list(APPEND fetchContentDeclareExtraArgs SOURCE_SUBDIR ${CPM_ARGS_SOURCE_SUBDIR}) + endif() + # For CMake version <3.28 OPTIONS are parsed in cpm_add_subdirectory + if(CPM_ARGS_OPTIONS AND NOT DOWNLOAD_ONLY) + foreach(OPTION ${CPM_ARGS_OPTIONS}) + cpm_parse_option("${OPTION}") + set(${OPTION_KEY} "${OPTION_VALUE}") + endforeach() + endif() + endif() + cpm_declare_fetch( + "${CPM_ARGS_NAME}" ${fetchContentDeclareExtraArgs} "${CPM_ARGS_UNPARSED_ARGUMENTS}" + ) + + cpm_fetch_package("${CPM_ARGS_NAME}" ${DOWNLOAD_ONLY} populated ${CPM_ARGS_UNPARSED_ARGUMENTS}) + if(CPM_SOURCE_CACHE AND download_directory) + file(LOCK ${download_directory}/../cmake.lock RELEASE) + endif() + if(${populated} AND ${CMAKE_VERSION} VERSION_LESS "3.30.3") + cpm_add_subdirectory( + "${CPM_ARGS_NAME}" + "${DOWNLOAD_ONLY}" + "${${CPM_ARGS_NAME}_SOURCE_DIR}/${CPM_ARGS_SOURCE_SUBDIR}" + "${${CPM_ARGS_NAME}_BINARY_DIR}" + "${CPM_ARGS_EXCLUDE_FROM_ALL}" + "${CPM_ARGS_SYSTEM}" + "${CPM_ARGS_OPTIONS}" + ) + endif() + cpm_get_fetch_properties("${CPM_ARGS_NAME}") + endif() + + set(${CPM_ARGS_NAME}_ADDED YES) + cpm_export_variables("${CPM_ARGS_NAME}") +endfunction() + +# Fetch a previously declared package +macro(CPMGetPackage Name) + if(DEFINED "CPM_DECLARATION_${Name}") + CPMAddPackage(NAME ${Name}) + else() + message(SEND_ERROR "${CPM_INDENT} Cannot retrieve package ${Name}: no declaration available") + endif() +endmacro() + +# export variables available to the caller to the parent scope expects ${CPM_ARGS_NAME} to be set +macro(cpm_export_variables name) + set(${name}_SOURCE_DIR + "${${name}_SOURCE_DIR}" + PARENT_SCOPE + ) + set(${name}_BINARY_DIR + "${${name}_BINARY_DIR}" + PARENT_SCOPE + ) + set(${name}_ADDED + "${${name}_ADDED}" + PARENT_SCOPE + ) + set(CPM_LAST_PACKAGE_NAME + "${name}" + PARENT_SCOPE + ) +endmacro() + +# declares a package, so that any call to CPMAddPackage for the package name will use these +# arguments instead. Previous declarations will not be overridden. +macro(CPMDeclarePackage Name) + if(NOT DEFINED "CPM_DECLARATION_${Name}") + set("CPM_DECLARATION_${Name}" "${ARGN}") + endif() +endmacro() + +function(cpm_add_to_package_lock Name) + if(NOT CPM_DONT_CREATE_PACKAGE_LOCK) + cpm_prettify_package_arguments(PRETTY_ARGN false ${ARGN}) + file(APPEND ${CPM_PACKAGE_LOCK_FILE} "# ${Name}\nCPMDeclarePackage(${Name}\n${PRETTY_ARGN})\n") + endif() +endfunction() + +function(cpm_add_comment_to_package_lock Name) + if(NOT CPM_DONT_CREATE_PACKAGE_LOCK) + cpm_prettify_package_arguments(PRETTY_ARGN true ${ARGN}) + file(APPEND ${CPM_PACKAGE_LOCK_FILE} + "# ${Name} (unversioned)\n# CPMDeclarePackage(${Name}\n${PRETTY_ARGN}#)\n" + ) + endif() +endfunction() + +# includes the package lock file if it exists and creates a target `cpm-update-package-lock` to +# update it +macro(CPMUsePackageLock file) + if(NOT CPM_DONT_CREATE_PACKAGE_LOCK) + get_filename_component(CPM_ABSOLUTE_PACKAGE_LOCK_PATH ${file} ABSOLUTE) + if(EXISTS ${CPM_ABSOLUTE_PACKAGE_LOCK_PATH}) + include(${CPM_ABSOLUTE_PACKAGE_LOCK_PATH}) + endif() + if(NOT TARGET cpm-update-package-lock) + add_custom_target( + cpm-update-package-lock COMMAND ${CMAKE_COMMAND} -E copy ${CPM_PACKAGE_LOCK_FILE} + ${CPM_ABSOLUTE_PACKAGE_LOCK_PATH} + ) + endif() + set(CPM_PACKAGE_LOCK_ENABLED true) + endif() +endmacro() + +# registers a package that has been added to CPM +function(CPMRegisterPackage PACKAGE VERSION) + list(APPEND CPM_PACKAGES ${PACKAGE}) + set(CPM_PACKAGES + ${CPM_PACKAGES} + CACHE INTERNAL "" + ) + set("CPM_PACKAGE_${PACKAGE}_VERSION" + ${VERSION} + CACHE INTERNAL "" + ) +endfunction() + +# retrieve the current version of the package to ${OUTPUT} +function(CPMGetPackageVersion PACKAGE OUTPUT) + set(${OUTPUT} + "${CPM_PACKAGE_${PACKAGE}_VERSION}" + PARENT_SCOPE + ) +endfunction() + +# declares a package in FetchContent_Declare +function(cpm_declare_fetch PACKAGE) + if(${CPM_DRY_RUN}) + cpm_message(STATUS "${CPM_INDENT} Package not declared (dry run)") + return() + endif() + + FetchContent_Declare(${PACKAGE} ${ARGN}) +endfunction() + +# returns properties for a package previously defined by cpm_declare_fetch +function(cpm_get_fetch_properties PACKAGE) + if(${CPM_DRY_RUN}) + return() + endif() + + set(${PACKAGE}_SOURCE_DIR + "${CPM_PACKAGE_${PACKAGE}_SOURCE_DIR}" + PARENT_SCOPE + ) + set(${PACKAGE}_BINARY_DIR + "${CPM_PACKAGE_${PACKAGE}_BINARY_DIR}" + PARENT_SCOPE + ) +endfunction() + +function(cpm_store_fetch_properties PACKAGE source_dir binary_dir) + if(${CPM_DRY_RUN}) + return() + endif() + + set(CPM_PACKAGE_${PACKAGE}_SOURCE_DIR + "${source_dir}" + CACHE INTERNAL "" + ) + set(CPM_PACKAGE_${PACKAGE}_BINARY_DIR + "${binary_dir}" + CACHE INTERNAL "" + ) +endfunction() + +# adds a package as a subdirectory if viable, according to provided options +function( + cpm_add_subdirectory + PACKAGE + DOWNLOAD_ONLY + SOURCE_DIR + BINARY_DIR + EXCLUDE + SYSTEM + OPTIONS +) + + if(NOT DOWNLOAD_ONLY AND EXISTS ${SOURCE_DIR}/CMakeLists.txt) + set(addSubdirectoryExtraArgs "") + if(EXCLUDE) + list(APPEND addSubdirectoryExtraArgs EXCLUDE_FROM_ALL) + endif() + if("${SYSTEM}" AND "${CMAKE_VERSION}" VERSION_GREATER_EQUAL "3.25") + # https://cmake.org/cmake/help/latest/prop_dir/SYSTEM.html#prop_dir:SYSTEM + list(APPEND addSubdirectoryExtraArgs SYSTEM) + endif() + if(OPTIONS) + foreach(OPTION ${OPTIONS}) + cpm_parse_option("${OPTION}") + set(${OPTION_KEY} "${OPTION_VALUE}") + endforeach() + endif() + set(CPM_OLD_INDENT "${CPM_INDENT}") + set(CPM_INDENT "${CPM_INDENT} ${PACKAGE}:") + add_subdirectory(${SOURCE_DIR} ${BINARY_DIR} ${addSubdirectoryExtraArgs}) + set(CPM_INDENT "${CPM_OLD_INDENT}") + endif() +endfunction() + +# downloads a previously declared package via FetchContent and exports the variables +# `${PACKAGE}_SOURCE_DIR` and `${PACKAGE}_BINARY_DIR` to the parent scope +function(cpm_fetch_package PACKAGE DOWNLOAD_ONLY populated) + set(${populated} + FALSE + PARENT_SCOPE + ) + if(${CPM_DRY_RUN}) + cpm_message(STATUS "${CPM_INDENT} Package ${PACKAGE} not fetched (dry run)") + return() + endif() + + FetchContent_GetProperties(${PACKAGE}) + + string(TOLOWER "${PACKAGE}" lower_case_name) + + if(NOT ${lower_case_name}_POPULATED) + if(${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.30.3") + if(DOWNLOAD_ONLY) + # MakeAvailable will call add_subdirectory internally which is not what we want when + # DOWNLOAD_ONLY is set. Populate will only download the dependency without adding it to the + # build + FetchContent_Populate( + ${PACKAGE} + SOURCE_DIR "${CPM_FETCHCONTENT_BASE_DIR}/${lower_case_name}-src" + BINARY_DIR "${CPM_FETCHCONTENT_BASE_DIR}/${lower_case_name}-build" + SUBBUILD_DIR "${CPM_FETCHCONTENT_BASE_DIR}/${lower_case_name}-subbuild" + ${ARGN} + ) + else() + FetchContent_MakeAvailable(${PACKAGE}) + endif() + else() + FetchContent_Populate(${PACKAGE}) + endif() + set(${populated} + TRUE + PARENT_SCOPE + ) + endif() + + cpm_store_fetch_properties( + ${CPM_ARGS_NAME} ${${lower_case_name}_SOURCE_DIR} ${${lower_case_name}_BINARY_DIR} + ) + + set(${PACKAGE}_SOURCE_DIR + ${${lower_case_name}_SOURCE_DIR} + PARENT_SCOPE + ) + set(${PACKAGE}_BINARY_DIR + ${${lower_case_name}_BINARY_DIR} + PARENT_SCOPE + ) +endfunction() + +# splits a package option +function(cpm_parse_option OPTION) + string(REGEX MATCH "^[^ ]+" OPTION_KEY "${OPTION}") + string(LENGTH "${OPTION}" OPTION_LENGTH) + string(LENGTH "${OPTION_KEY}" OPTION_KEY_LENGTH) + if(OPTION_KEY_LENGTH STREQUAL OPTION_LENGTH) + # no value for key provided, assume user wants to set option to "ON" + set(OPTION_VALUE "ON") + else() + math(EXPR OPTION_KEY_LENGTH "${OPTION_KEY_LENGTH}+1") + string(SUBSTRING "${OPTION}" "${OPTION_KEY_LENGTH}" "-1" OPTION_VALUE) + string(STRIP "${OPTION_VALUE}" OPTION_VALUE) + endif() + set(OPTION_KEY + "${OPTION_KEY}" + PARENT_SCOPE + ) + set(OPTION_VALUE + "${OPTION_VALUE}" + PARENT_SCOPE + ) +endfunction() + +# guesses the package version from a git tag +function(cpm_get_version_from_git_tag GIT_TAG RESULT) + string(LENGTH ${GIT_TAG} length) + if(length EQUAL 40) + # GIT_TAG is probably a git hash + set(${RESULT} + 0 + PARENT_SCOPE + ) + else() + string(REGEX MATCH "v?([0123456789.]*).*" _ ${GIT_TAG}) + set(${RESULT} + ${CMAKE_MATCH_1} + PARENT_SCOPE + ) + endif() +endfunction() + +# guesses if the git tag is a commit hash or an actual tag or a branch name. +function(cpm_is_git_tag_commit_hash GIT_TAG RESULT) + string(LENGTH "${GIT_TAG}" length) + # full hash has 40 characters, and short hash has at least 7 characters. + if(length LESS 7 OR length GREATER 40) + set(${RESULT} + 0 + PARENT_SCOPE + ) + else() + if(${GIT_TAG} MATCHES "^[a-fA-F0-9]+$") + set(${RESULT} + 1 + PARENT_SCOPE + ) + else() + set(${RESULT} + 0 + PARENT_SCOPE + ) + endif() + endif() +endfunction() + +function(cpm_prettify_package_arguments OUT_VAR IS_IN_COMMENT) + set(oneValueArgs + NAME + FORCE + VERSION + GIT_TAG + DOWNLOAD_ONLY + GITHUB_REPOSITORY + GITLAB_REPOSITORY + BITBUCKET_REPOSITORY + GIT_REPOSITORY + SOURCE_DIR + FIND_PACKAGE_ARGUMENTS + NO_CACHE + SYSTEM + GIT_SHALLOW + EXCLUDE_FROM_ALL + SOURCE_SUBDIR + ) + set(multiValueArgs URL OPTIONS DOWNLOAD_COMMAND) + cmake_parse_arguments(CPM_ARGS "" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) + + foreach(oneArgName ${oneValueArgs}) + if(DEFINED CPM_ARGS_${oneArgName}) + if(${IS_IN_COMMENT}) + string(APPEND PRETTY_OUT_VAR "#") + endif() + if(${oneArgName} STREQUAL "SOURCE_DIR") + string(REPLACE ${CMAKE_SOURCE_DIR} "\${CMAKE_SOURCE_DIR}" CPM_ARGS_${oneArgName} + ${CPM_ARGS_${oneArgName}} + ) + endif() + string(APPEND PRETTY_OUT_VAR " ${oneArgName} ${CPM_ARGS_${oneArgName}}\n") + endif() + endforeach() + foreach(multiArgName ${multiValueArgs}) + if(DEFINED CPM_ARGS_${multiArgName}) + if(${IS_IN_COMMENT}) + string(APPEND PRETTY_OUT_VAR "#") + endif() + string(APPEND PRETTY_OUT_VAR " ${multiArgName}\n") + foreach(singleOption ${CPM_ARGS_${multiArgName}}) + if(${IS_IN_COMMENT}) + string(APPEND PRETTY_OUT_VAR "#") + endif() + string(APPEND PRETTY_OUT_VAR " \"${singleOption}\"\n") + endforeach() + endif() + endforeach() + + if(NOT "${CPM_ARGS_UNPARSED_ARGUMENTS}" STREQUAL "") + if(${IS_IN_COMMENT}) + string(APPEND PRETTY_OUT_VAR "#") + endif() + string(APPEND PRETTY_OUT_VAR " ") + foreach(CPM_ARGS_UNPARSED_ARGUMENT ${CPM_ARGS_UNPARSED_ARGUMENTS}) + string(APPEND PRETTY_OUT_VAR " ${CPM_ARGS_UNPARSED_ARGUMENT}") + endforeach() + string(APPEND PRETTY_OUT_VAR "\n") + endif() + + set(${OUT_VAR} + ${PRETTY_OUT_VAR} + PARENT_SCOPE + ) + +endfunction() diff --git a/thirdparty/cpm/common_post_project.cmake b/thirdparty/cpm/common_post_project.cmake new file mode 100644 index 000000000000..99472d259cf2 --- /dev/null +++ b/thirdparty/cpm/common_post_project.cmake @@ -0,0 +1,24 @@ +# Shared stage setup that must run after project(); see common_pre_project.cmake for why the +# two halves are separate files. + +# all binaries will be located in ./build/Release/bin +set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}) +set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}) +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}) + +# Inhibit all warning messages +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -w") +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -w") + +if(EMSCRIPTEN) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -s USE_BOOST_HEADERS=1") +endif() + +# CPM.cmake reissues cmake_minimum_required(3.14), so include it after project() to keep that +# out of the policy scope the stage files are configured under. +include(${CMAKE_CURRENT_LIST_DIR}/cmake/CPM.cmake) +include(${CMAKE_CURRENT_LIST_DIR}/package-lock.cmake) + +macro(meshlib_add_package name) + CPMAddPackage(NAME ${name} ${MESHLIB_PACKAGE_${name}} ${ARGN}) +endmacro() diff --git a/thirdparty/cpm/common_pre_project.cmake b/thirdparty/cpm/common_pre_project.cmake new file mode 100644 index 000000000000..f16cbb72b2d5 --- /dev/null +++ b/thirdparty/cpm/common_pre_project.cmake @@ -0,0 +1,23 @@ +# Shared stage setup that must run BEFORE project(), because it decides how the compiler is +# probed: the language standard, the Emscripten memory model and the vcpkg toolchain. +# +# CMake requires a literal, direct project() call in the top-level CMakeLists.txt -- calling it +# from an included file makes CMake inject an implicit project() at line 1, which probes the +# compiler before these flags are set. That is why this file and common_post_project.cmake are +# separate rather than one common.cmake. + +set(CMAKE_CXX_STANDARD 20) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +if(CMAKE_VERSION VERSION_GREATER_EQUAL 4.0) + set(CMAKE_POLICY_VERSION_MINIMUM 3.5) +endif() + +get_filename_component(MESHLIB_THIRDPARTY_DIR "${CMAKE_CURRENT_LIST_DIR}" DIRECTORY) +set(MESHLIB_THIRDPARTY_INCLUDE_DIR "include") + +list(APPEND CMAKE_MODULE_PATH "${MESHLIB_THIRDPARTY_DIR}/../cmake/Modules") +include(ConfigureVcpkg) +if(MR_EMSCRIPTEN) + include(DefaultEmscriptenOptions) +endif() diff --git a/thirdparty/cpm/fetch/CMakeLists.txt b/thirdparty/cpm/fetch/CMakeLists.txt new file mode 100644 index 000000000000..557f4d867ef1 --- /dev/null +++ b/thirdparty/cpm/fetch/CMakeLists.txt @@ -0,0 +1,22 @@ +cmake_minimum_required(VERSION 3.18 FATAL_ERROR) + +# Materializes dependency sources without building them, and writes their locations to +# MESHLIB_FETCH_OUTPUT as `SRC_=` lines -- a form both `sh` and `make` can read. +# scripts/mrbind/generate.mk uses this to parse MeshLib headers against raw dependency sources +# on platforms that never run scripts/build_thirdparty.sh. +# +# project(... NONE) skips compiler detection, so this configures with no toolchain file even +# when the caller is targeting Emscripten. +project(MeshLib_thirdparty_fetch NONE) + +include(${CMAKE_CURRENT_LIST_DIR}/../cmake/CPM.cmake) +include(${CMAKE_CURRENT_LIST_DIR}/../package-lock.cmake) + +foreach(pkg IN LISTS MESHLIB_FETCH_PACKAGES) + CPMAddPackage(NAME ${pkg} ${MESHLIB_PACKAGE_${pkg}} DOWNLOAD_ONLY YES) + # Hyphenated package names are not legal shell or make variable names. + string(MAKE_C_IDENTIFIER "${pkg}" var) + string(APPEND sources "SRC_${var}=${${pkg}_SOURCE_DIR}\n") +endforeach() + +file(WRITE "${MESHLIB_FETCH_OUTPUT}" "${sources}") diff --git a/thirdparty/cpm/package-lock.cmake b/thirdparty/cpm/package-lock.cmake new file mode 100644 index 000000000000..884fd7249f0f --- /dev/null +++ b/thirdparty/cpm/package-lock.cmake @@ -0,0 +1,159 @@ +# Version pins for every third-party library built by thirdparty/cpm/stage1 and thirdparty/cpm/stage2. +# +# Each entry is the CPMAddPackage argument list for one dependency, minus NAME, which the +# caller supplies: the stages via meshlib_add_package(), thirdparty/cpm/fetch by appending +# DOWNLOAD_ONLY YES. Deliberately not CPMDeclarePackage -- a declaration *replaces* the +# caller's arguments rather than extending them, which silently drops DOWNLOAD_ONLY. +# +# GIT_TAG is a release tag only where the pin sits exactly on one; everywhere else it is the +# commit hash, because the pin tracks a mid-branch commit or a MeshInspector fork. Bumping a +# hash pin onto a tag is a version change, not a cleanup. +# +# Editing a file under patches/ in place does not invalidate CPM's source cache -- its key +# covers a patch's path, not its contents. Clear CPM_SOURCE_CACHE (thirdparty_sources/ by +# default) after doing so, or the previously patched tree is reused. +# +# scripts/check_third_party_licenses.py parses this file, so keep one argument per line. + +set(MESHLIB_PACKAGE_boost-libs + VERSION 1.83.0 + URL https://github.com/boostorg/boost/releases/download/boost-1.83.0/boost-1.83.0.tar.xz + PATCHES + ${CMAKE_CURRENT_LIST_DIR}/patches/boost-locale-no-thread.patch + ${CMAKE_CURRENT_LIST_DIR}/patches/boost-locale-std-mutex.patch + ${CMAKE_CURRENT_LIST_DIR}/patches/boost-honor-skip-install-rules.patch +) + +set(MESHLIB_PACKAGE_c-blosc + GIT_REPOSITORY https://github.com/Blosc/c-blosc.git + GIT_TAG v1.21.6 +) + +set(MESHLIB_PACKAGE_clip + GIT_REPOSITORY https://github.com/dacap/clip.git + GIT_TAG 7f2e86ab9690f7df88440002083edd257f87bc58 + PATCHES + ${CMAKE_CURRENT_LIST_DIR}/patches/clip-config-use-project-name.patch +) + +set(MESHLIB_PACKAGE_cpp-httplib + GIT_REPOSITORY https://github.com/yhirose/cpp-httplib + GIT_TAG b045ee7f6b434a85fd011e96e28c6d4abfb18788 +) + +set(MESHLIB_PACKAGE_cpr + GIT_REPOSITORY https://github.com/whoshuu/cpr.git + GIT_TAG 1.14.2 +) + +set(MESHLIB_PACKAGE_eigen + GIT_REPOSITORY https://gitlab.com/libeigen/eigen.git + GIT_TAG 5.0.1 +) + +set(MESHLIB_PACKAGE_expected + GIT_REPOSITORY https://github.com/Developer-Ecosystem-Engineering/expected.git + GIT_TAG d6e1fcc766b725f196d7f58097d72f8cfab4d56a +) + +set(MESHLIB_PACKAGE_fastmcpp + GIT_REPOSITORY https://github.com/MeshInspector/fastmcpp + GIT_TAG 9aa1a179f886f6d098f67693e18ee4778db924b7 +) + +set(MESHLIB_PACKAGE_fmt + GIT_REPOSITORY https://github.com/fmtlib/fmt.git + GIT_TAG 47a66c5eccc0cce71ad81b4a681a2032d86ca951 +) + +set(MESHLIB_PACKAGE_GDCM + GIT_REPOSITORY https://github.com/malaterre/GDCM.git + GIT_TAG v3.2.6 +) + +set(MESHLIB_PACKAGE_glad + GIT_REPOSITORY https://github.com/Dav1dde/glad.git + GIT_TAG e86f90457371c6233053bacf0d6f486a51ddcd67 +) + +set(MESHLIB_PACKAGE_googletest + GIT_REPOSITORY https://github.com/google/googletest.git + GIT_TAG b796f7d44681514f58a683a3a71ff17c94edb0c1 +) + +set(MESHLIB_PACKAGE_jsoncpp + GIT_REPOSITORY https://github.com/open-source-parsers/jsoncpp.git + GIT_TAG 42e892d96e47b1f6e29844cc705e148ec4856448 +) + +set(MESHLIB_PACKAGE_laz-perf + GIT_REPOSITORY https://github.com/MeshInspector/laz-perf + GIT_TAG 05ea01542e5c4417c05e7222f920e06276c79324 +) + +set(MESHLIB_PACKAGE_libE57Format + GIT_REPOSITORY https://github.com/MeshInspector/libE57Format + GIT_TAG 1816f72de58b27208a8f03c7213ba4039517402a +) + +set(MESHLIB_PACKAGE_libjpeg-turbo + GIT_REPOSITORY https://github.com/libjpeg-turbo/libjpeg-turbo.git + GIT_TAG 7fa4b5b762c9a99b46b0b7838f5fd55071b92ea5 + PATCHES + ${CMAKE_CURRENT_LIST_DIR}/patches/libjpeg-turbo-allow-add-subdirectory.patch +) + +set(MESHLIB_PACKAGE_libzip + GIT_REPOSITORY https://github.com/nih-at/libzip.git + GIT_TAG v1.11.4 +) + +set(MESHLIB_PACKAGE_mbedtls + GIT_REPOSITORY https://github.com/Mbed-TLS/mbedtls + GIT_TAG v3.5.0 +) + +set(MESHLIB_PACKAGE_nlohmann-json + GIT_REPOSITORY https://github.com/nlohmann/json + GIT_TAG 3946872265598aed5a7aea68cad4d9d1f168bd4b +) + +set(MESHLIB_PACKAGE_onetbb + GIT_REPOSITORY https://github.com/oneapi-src/oneTBB.git + GIT_TAG 12ceae12138af08845b3e8c369b24527346fe99e +) + +set(MESHLIB_PACKAGE_OpenCTM + GIT_REPOSITORY https://github.com/MeshInspector/OpenCTM.git + GIT_TAG 2b444a4c70c432c1f4990fb32b0fcb11d0d81aad +) + +set(MESHLIB_PACKAGE_openvdb + GIT_REPOSITORY https://github.com/AcademySoftwareFoundation/openvdb + GIT_TAG 87a508ca48a69f06d149be6cb0d8289fc1314f72 +) + +set(MESHLIB_PACKAGE_parallel-hashmap + GIT_REPOSITORY https://github.com/greg7mdp/parallel-hashmap.git + GIT_TAG v2.0.0 +) + +set(MESHLIB_PACKAGE_spdlog + GIT_REPOSITORY https://github.com/gabime/spdlog.git + GIT_TAG 6fa36017cfd5731d617e1a934f0e5ea9c4445b13 +) + +set(MESHLIB_PACKAGE_tinygltf + GIT_REPOSITORY https://github.com/syoyo/tinygltf + GIT_TAG v2.8.2 +) + +set(MESHLIB_PACKAGE_tinyxml2 + GIT_REPOSITORY https://github.com/leethomason/tinyxml2.git + GIT_TAG 321ea883b7190d4e85cae5512a12e5eaa8f8731f +) + +set(MESHLIB_PACKAGE_zlib-ng + GIT_REPOSITORY https://github.com/zlib-ng/zlib-ng.git + GIT_TAG 860e4cff7917d93f54f5d7f0bc1d0e8b1a3cb988 +) diff --git a/thirdparty/cpm/patches/boost-honor-skip-install-rules.patch b/thirdparty/cpm/patches/boost-honor-skip-install-rules.patch new file mode 100644 index 000000000000..a367e01d80ff --- /dev/null +++ b/thirdparty/cpm/patches/boost-honor-skip-install-rules.patch @@ -0,0 +1,15 @@ +diff -rup a/tools/cmake/include/BoostRoot.cmake b/tools/cmake/include/BoostRoot.cmake +--- a/tools/cmake/include/BoostRoot.cmake ++++ b/tools/cmake/include/BoostRoot.cmake +@@ -107,8 +107,11 @@ + set(BOOST_ENABLE_PYTHON OFF) + endif() + ++ if(NOT DEFINED BOOST_SKIP_INSTALL_RULES) ++ set(BOOST_SKIP_INSTALL_RULES ON) ++ endif() ++ + set(BUILD_TESTING OFF) +- set(BOOST_SKIP_INSTALL_RULES ON) + + endif() diff --git a/thirdparty/cpm/patches/boost-locale-no-thread.patch b/thirdparty/cpm/patches/boost-locale-no-thread.patch new file mode 100644 index 000000000000..38c7621cd010 --- /dev/null +++ b/thirdparty/cpm/patches/boost-locale-no-thread.patch @@ -0,0 +1,11 @@ +diff -rup a/libs/locale/CMakeLists.txt b/libs/locale/CMakeLists.txt +--- a/libs/locale/CMakeLists.txt ++++ b/libs/locale/CMakeLists.txt +@@ -51,7 +51,6 @@ + PRIVATE + Boost::core + Boost::predef +- Boost::thread + ) + + target_compile_definitions(boost_locale diff --git a/thirdparty/cpm/patches/boost-locale-std-mutex.patch b/thirdparty/cpm/patches/boost-locale-std-mutex.patch new file mode 100644 index 000000000000..d8757b0deabd --- /dev/null +++ b/thirdparty/cpm/patches/boost-locale-std-mutex.patch @@ -0,0 +1,185 @@ +diff -rup a/libs/locale/src/boost/locale/icu/date_time.cpp b/libs/locale/src/boost/locale/icu/date_time.cpp +--- a/libs/locale/src/boost/locale/icu/date_time.cpp ++++ b/libs/locale/src/boost/locale/icu/date_time.cpp +@@ -14,9 +14,9 @@ + #include "boost/locale/icu/icu_util.hpp" + #include "boost/locale/icu/time_zone.hpp" + #include "boost/locale/icu/uconv.hpp" +-#include + #include + #include ++#include + #include + #include + #include +@@ -214,8 +214,8 @@ namespace boost { namespace locale { nam + } + + private: +- typedef boost::unique_lock guard; +- mutable boost::mutex lock_; ++ typedef std::unique_lock guard; ++ mutable std::mutex lock_; + std::string encoding_; + hold_ptr calendar_; + }; +diff -rup a/libs/locale/src/boost/locale/shared/date_time.cpp b/libs/locale/src/boost/locale/shared/date_time.cpp +--- a/libs/locale/src/boost/locale/shared/date_time.cpp ++++ b/libs/locale/src/boost/locale/shared/date_time.cpp +@@ -7,9 +7,8 @@ + #include + #include + #include +-#include +-#include + #include ++#include + + namespace boost { namespace locale { + +@@ -395,9 +394,9 @@ namespace boost { namespace locale { + } + + namespace time_zone { +- boost::mutex& tz_mutex() ++ std::mutex& tz_mutex() + { +- static boost::mutex m; ++ static std::mutex m; + return m; + } + std::string& tz_id() +@@ -407,12 +406,12 @@ namespace boost { namespace locale { + } + std::string global() + { +- boost::unique_lock lock(tz_mutex()); ++ std::unique_lock lock(tz_mutex()); + return tz_id(); + } + std::string global(const std::string& new_id) + { +- boost::unique_lock lock(tz_mutex()); ++ std::unique_lock lock(tz_mutex()); + return boost::exchange(tz_id(), new_id); + } + } // namespace time_zone +diff -rup a/libs/locale/src/boost/locale/shared/generator.cpp b/libs/locale/src/boost/locale/shared/generator.cpp +--- a/libs/locale/src/boost/locale/shared/generator.cpp ++++ b/libs/locale/src/boost/locale/shared/generator.cpp +@@ -7,10 +7,9 @@ + #include + #include + #include +-#include +-#include + #include + #include ++#include + #include + + namespace boost { namespace locale { +@@ -21,7 +20,7 @@ namespace boost { namespace locale { + {} + + mutable std::map cached; +- mutable boost::mutex cached_lock; ++ mutable std::mutex cached_lock; + + category_t cats; + char_facet_t chars; +@@ -101,7 +100,7 @@ namespace boost { namespace locale { + std::locale generator::generate(const std::locale& base, const std::string& id) const + { + if(d->caching_enabled) { +- boost::unique_lock guard(d->cached_lock); ++ std::unique_lock guard(d->cached_lock); + const auto p = d->cached.find(id); + if(p != d->cached.end()) + return p->second; +@@ -126,7 +125,7 @@ namespace boost { namespace locale { + result = backend->install(result, facet, char_facet_t::nochar); + } + if(d->caching_enabled) { +- boost::unique_lock guard(d->cached_lock); ++ std::unique_lock guard(d->cached_lock); + const auto p = d->cached.find(id); + if(p == d->cached.end()) + d->cached[id] = result; +diff -rup a/libs/locale/src/boost/locale/shared/localization_backend.cpp b/libs/locale/src/boost/locale/shared/localization_backend.cpp +--- a/libs/locale/src/boost/locale/shared/localization_backend.cpp ++++ b/libs/locale/src/boost/locale/shared/localization_backend.cpp +@@ -5,10 +5,9 @@ + // https://www.boost.org/LICENSE_1_0.txt + + #include +-#include +-#include + #include + #include ++#include + #include + + #ifdef BOOST_LOCALE_WITH_ICU +@@ -211,9 +210,9 @@ namespace boost { namespace locale { + return mgr; + } + +- boost::mutex& localization_backend_manager_mutex() ++ std::mutex& localization_backend_manager_mutex() + { +- static boost::mutex the_mutex; ++ static std::mutex the_mutex; + return the_mutex; + } + localization_backend_manager& localization_backend_manager_global() +@@ -225,12 +224,12 @@ namespace boost { namespace locale { + + localization_backend_manager localization_backend_manager::global() + { +- boost::unique_lock lock(localization_backend_manager_mutex()); ++ std::unique_lock lock(localization_backend_manager_mutex()); + return localization_backend_manager_global(); + } + localization_backend_manager localization_backend_manager::global(const localization_backend_manager& in) + { +- boost::unique_lock lock(localization_backend_manager_mutex()); ++ std::unique_lock lock(localization_backend_manager_mutex()); + return exchange(localization_backend_manager_global(), in); + } + +diff -rup a/libs/locale/src/boost/locale/win32/lcid.cpp b/libs/locale/src/boost/locale/win32/lcid.cpp +--- a/libs/locale/src/boost/locale/win32/lcid.cpp ++++ b/libs/locale/src/boost/locale/win32/lcid.cpp +@@ -10,9 +10,8 @@ + + #include "boost/locale/win32/lcid.hpp" + #include +-#include +-#include + #include ++#include + #include + #include + #include +@@ -24,9 +23,9 @@ namespace boost { namespace locale { nam + + static table_type* volatile table = 0; + +- boost::mutex& lcid_table_mutex() ++ std::mutex& lcid_table_mutex() + { +- static boost::mutex m; ++ static std::mutex m; + return m; + } + +@@ -76,7 +75,7 @@ namespace boost { namespace locale { nam + if(table) + return *table; + else { +- boost::unique_lock lock(lcid_table_mutex()); ++ std::unique_lock lock(lcid_table_mutex()); + if(table) + return *table; + EnumSystemLocalesA(proc, LCID_INSTALLED); diff --git a/thirdparty/cpm/patches/clip-config-use-project-name.patch b/thirdparty/cpm/patches/clip-config-use-project-name.patch new file mode 100644 index 000000000000..babe2b2dd134 --- /dev/null +++ b/thirdparty/cpm/patches/clip-config-use-project-name.patch @@ -0,0 +1,10 @@ +diff -rup a/clip-config.cmake.in b/clip-config.cmake.in +--- a/clip-config.cmake.in ++++ b/clip-config.cmake.in +@@ -1,4 +1,4 @@ + @PACKAGE_INIT@ + +-include("${CMAKE_CURRENT_LIST_DIR}/@CMAKE_PROJECT_NAME@-targets.cmake") +-check_required_components("@CMAKE_PROJECT_NAME@") ++include("${CMAKE_CURRENT_LIST_DIR}/@PROJECT_NAME@-targets.cmake") ++check_required_components("@PROJECT_NAME@") diff --git a/thirdparty/cpm/patches/libjpeg-turbo-allow-add-subdirectory.patch b/thirdparty/cpm/patches/libjpeg-turbo-allow-add-subdirectory.patch new file mode 100644 index 000000000000..aaabff57592b --- /dev/null +++ b/thirdparty/cpm/patches/libjpeg-turbo-allow-add-subdirectory.patch @@ -0,0 +1,23 @@ +diff -rup a/CMakeLists.txt b/CMakeLists.txt +--- a/CMakeLists.txt ++++ b/CMakeLists.txt +@@ -50,10 +50,6 @@ + # ExternalProject_Add() or make downstream modifications to the libjpeg-turbo + # build system to suit their specific needs. Please do not file bug reports, + # feature requests, or pull requests regarding this. +-if(NOT CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR) +- message(FATAL_ERROR "The libjpeg-turbo build system cannot be integrated into another build system using add_subdirectory(). Use ExternalProject_Add() instead.") +-endif() +- + # CMake 3.14 and later sets CMAKE_MACOSX_BUNDLE to TRUE by default when + # CMAKE_SYSTEM_NAME is iOS, tvOS, or watchOS, which breaks the libjpeg-turbo + # build. (Specifically, when CMAKE_MACOSX_BUNDLE is TRUE, executables for +@@ -1820,8 +1816,3 @@ + DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} COMPONENT include) + + include(cmakescripts/BuildPackages.cmake) +- +-configure_file("${CMAKE_CURRENT_SOURCE_DIR}/cmakescripts/cmake_uninstall.cmake.in" +- "cmake_uninstall.cmake" IMMEDIATE @ONLY) +- +-add_custom_target(uninstall COMMAND ${CMAKE_COMMAND} -P cmake_uninstall.cmake) diff --git a/thirdparty/cpm/stage1/CMakeLists.txt b/thirdparty/cpm/stage1/CMakeLists.txt new file mode 100644 index 000000000000..33377834046a --- /dev/null +++ b/thirdparty/cpm/stage1/CMakeLists.txt @@ -0,0 +1,182 @@ +# to force update thirdparty touch this line +cmake_minimum_required(VERSION 3.18 FATAL_ERROR) + +include(${CMAKE_CURRENT_LIST_DIR}/../common_pre_project.cmake) +project(MeshLib_thirdparty_stage1 CXX) +include(${CMAKE_CURRENT_LIST_DIR}/../common_post_project.cmake) + +# Libraries with no dependency on anything else built here. Everything that reaches for a +# sibling through find_package lives in ../stage2, which is configured after this one is +# installed. + +IF(EMSCRIPTEN) + set(BOOST_INCLUDE_LIBRARIES locale) + set(BOOST_LOCALE_ENABLE_ICONV OFF) + set(BOOST_SKIP_INSTALL_RULES OFF) + meshlib_add_package(boost-libs) + + set(WITH_JAVA OFF) + set(WITH_JPEG8 ON) + set(WITH_SIMD OFF) + set(BUILD_TESTING OFF) + set(ENABLE_STATIC OFF) + set(CMAKE_INSTALL_DOCDIR_BACKUP "${CMAKE_INSTALL_DOCDIR}") + set(CMAKE_INSTALL_MANDIR_BACKUP "${CMAKE_INSTALL_MANDIR}") + set(CMAKE_INSTALL_DOCDIR "share/doc/libjpeg-turbo") + set(CMAKE_INSTALL_MANDIR "share/man") + meshlib_add_package(libjpeg-turbo) + set(CMAKE_INSTALL_DOCDIR "${CMAKE_INSTALL_DOCDIR_BACKUP}") + set(CMAKE_INSTALL_MANDIR "${CMAKE_INSTALL_MANDIR_BACKUP}") + + set(ENABLE_PROGRAMS OFF CACHE BOOL "") + set(ENABLE_TESTING OFF CACHE BOOL "") + set(DISABLE_PACKAGE_CONFIG_AND_INSTALL OFF CACHE BOOL "") + meshlib_add_package(mbedtls) + + set(INSTALL_GTEST ON) + add_compile_definitions(GTEST_HAS_CXXABI_H_=0) + meshlib_add_package(googletest) +ENDIF() + +IF(NOT APPLE AND NOT MR_EMSCRIPTEN) + set(BUILD_SHARED_LIBS ON) + + find_package(cpr) + IF(NOT cpr_FOUND) + set(CPR_ENABLE_UNINSTALL "OFF") + set(CPR_ENABLE_INSTALL "OFF") + set(CPR_USE_SYSTEM_GTEST "ON") + set(CPR_BUILD_TESTS "OFF") + set(CPR_BUILD_TESTS_SSL "OFF") + set(CPR_FORCE_USE_SYSTEM_CURL ON) + meshlib_add_package(cpr) + ENDIF() + + find_package(nlohmann_json) + if(NOT TARGET nlohmann_json::nlohmann_json) + set(JSON_BuildTests OFF) + set(JSON_Install ON) + meshlib_add_package(nlohmann-json) + endif() + + find_package(httplib) + if(NOT TARGET httplib::httplib) + set(HTTPLIB_TEST OFF) + set(HTTPLIB_USE_BROTLI_IF_AVAILABLE OFF) + set(HTTPLIB_USE_OPENSSL_IF_AVAILABLE OFF) + set(HTTPLIB_USE_ZLIB_IF_AVAILABLE OFF) + set(HTTPLIB_USE_ZSTD_IF_AVAILABLE OFF) + meshlib_add_package(cpp-httplib) + endif() + + find_package(PkgConfig) + IF(PkgConfig_FOUND) + pkg_check_modules(tinyxml2 QUIET tinyxml2) + ENDIF() + IF(NOT tinyxml2_FOUND OR tinyxml2_VERSION VERSION_LESS 10) + meshlib_add_package(tinyxml2) + ENDIF() +ENDIF() + +meshlib_add_package(OpenCTM) + +IF(NOT APPLE) + set(ZLIB_COMPAT OFF CACHE BOOL "") + set(ZLIB_ENABLE_TESTS OFF CACHE BOOL "") + set(ZLIBNG_ENABLE_TESTS OFF CACHE BOOL "") + set(WITH_GTEST OFF CACHE BOOL "") + set(WITH_GZFILEOP OFF CACHE BOOL "") + IF(EMSCRIPTEN) + # Force static: undefined BUILD_SHARED_LIBS makes zlib-ng emit duplicate + # libz-ng.a rules under Emscripten. Restore to undefined (not empty) + # afterwards -- jsoncpp downstream breaks on empty BUILD_SHARED_LIBS. + set(BUILD_SHARED_LIBS OFF) + meshlib_add_package(zlib-ng) + unset(BUILD_SHARED_LIBS) + ELSE() + meshlib_add_package(zlib-ng) + ENDIF() + + option(PHMAP_INSTALL "" ON) + meshlib_add_package(parallel-hashmap) +ENDIF() + +IF(EMSCRIPTEN) + meshlib_add_package(expected) + + set(GDCM_BUILD_APPLICATIONS OFF) + set(GDCM_BUILD_SHARED_LIBS OFF) + set(GDCM_BUILD_TESTING OFF) + set(GDCM_BUILD_DOCBOOK_MANPAGES OFF) + meshlib_add_package(GDCM) + + set(CMAKE_CXX_FLAGS_BACKUP "${CMAKE_CXX_FLAGS}") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -s NO_DISABLE_EXCEPTION_CATCHING=1") + set(JSONCPP_WITH_TESTS OFF) + set(JSONCPP_WITH_POST_BUILD_UNITTEST OFF) + set(BUILD_STATIC_LIBS OFF) + meshlib_add_package(jsoncpp) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS_BACKUP}") + + meshlib_add_package(fmt) + + set(SPDLOG_INSTALL ON) + set(SPDLOG_FMT_EXTERNAL ON) + meshlib_add_package(spdlog) + + option(TBB_TEST "Enable testing" OFF) + set(CMAKE_SYSTEM_PROCESSOR_BACKUP "${CMAKE_SYSTEM_PROCESSOR}") + set(CMAKE_SYSTEM_PROCESSOR "unknown") + meshlib_add_package(onetbb) + set(CMAKE_SYSTEM_PROCESSOR "${CMAKE_SYSTEM_PROCESSOR_BACKUP}") + install(DIRECTORY ${onetbb_SOURCE_DIR}/include/tbb DESTINATION ${MESHLIB_THIRDPARTY_INCLUDE_DIR}) + + meshlib_add_package(tinyxml2) + + set(BUILD_TESTS OFF CACHE BOOL "") + set(BUILD_FUZZERS OFF CACHE BOOL "") + set(BUILD_BENCHMARKS OFF CACHE BOOL "") + set(BUILD_STATIC OFF CACHE BOOL "") + set(BLOSC_INSTALL ON) + meshlib_add_package(c-blosc) + + set(EIGEN_BUILD_DOC OFF) + set(BUILD_TESTING OFF) + set(EIGEN_BUILD_CMAKE_PACKAGE ON) + set(EIGEN_BUILD_PKGCONFIG ON) + meshlib_add_package(eigen) +ELSE() # not MR_EMSCRIPTEN + set(BUILD_SHARED_LIBS ON) + + set(CLIP_EXAMPLES OFF CACHE BOOL "") + set(CLIP_TESTS OFF CACHE BOOL "") + set(CLIP_X11_WITH_PNG OFF CACHE BOOL "") + meshlib_add_package(clip) +ENDIF() + +find_package(glad) +IF(NOT glad_FOUND) + set(GLAD_INSTALL ON CACHE BOOL "") + set(GLAD_REPRODUCIBLE ON CACHE BOOL "") + meshlib_add_package(glad) +ENDIF() + +IF(NOT MR_EMSCRIPTEN) + set(PYBIND11_INSTALL ON) + # We want to build the stubs in our main `CMakeLists.txt` instead of here. + # It seems to work fine either way, but I'm not comfortable not having the stubs see the customized cmake variables + # that we set in our main `CMakeLists.txt`, such as those influencing the Pybind ABI strings. + # Even though the stubs shouldn't call any Pybind functions right now, they still include the Pybind header. + # Accidentally calling some inline Pybind function in it could have weird effects, I reckon. + # Note that regardless of this setting, the python-version-specific shims always need to be built in our main `CMakeLists.txt`, + # to see our configuration CMake variables. + option(PYBIND11_NONLIMITEDAPI_BUILD_STUBS "" OFF) + add_subdirectory(${MESHLIB_THIRDPARTY_DIR}/mrbind-pybind11 ./mrbind-pybind11) +ENDIF() + +set(TINYGLTF_INSTALL ON) +meshlib_add_package(tinygltf) + +set(WITH_TESTS OFF CACHE BOOL "") +set(WITH_EMBIND OFF CACHE BOOL "") +meshlib_add_package(laz-perf) diff --git a/thirdparty/cpm/stage2/CMakeLists.txt b/thirdparty/cpm/stage2/CMakeLists.txt new file mode 100644 index 000000000000..99db54b39321 --- /dev/null +++ b/thirdparty/cpm/stage2/CMakeLists.txt @@ -0,0 +1,64 @@ +# to force update thirdparty touch this line +cmake_minimum_required(VERSION 3.18 FATAL_ERROR) + +include(${CMAKE_CURRENT_LIST_DIR}/../common_pre_project.cmake) +project(MeshLib_thirdparty_stage2 CXX) +include(${CMAKE_CURRENT_LIST_DIR}/../common_post_project.cmake) + +# Libraries that resolve a dependency built by ../stage1 through find_package, so they can only +# be configured once stage1 has been installed into CMAKE_INSTALL_PREFIX: +# libzip -> MbedTLS, libE57Format -> tinyxml2, openvdb -> TBB + Blosc + Boost, fastmcpp -> +# httplib + nlohmann_json. + +IF(NOT APPLE AND NOT MR_EMSCRIPTEN) + find_package(OpenVDB 10 COMPONENTS openvdb) + IF(NOT OpenVDB_FOUND) + set(OPENVDB_ENABLE_UNINSTALL OFF) + set(OPENVDB_ENABLE_INSTALL OFF) + set(OPENVDB_CORE_SHARED "ON") + set(OPENVDB_CORE_STATIC "OFF") + set(OPENVDB_BUILD_BINARIES OFF) + set(OPENVDB_BUILD_VDB_PRINT OFF) + set(USE_EXPLICIT_INSTANTIATION OFF) + meshlib_add_package(openvdb) + ENDIF() +ENDIF() + +IF(EMSCRIPTEN) + set(BUILD_TOOLS OFF CACHE BOOL "") + set(BUILD_REGRESS OFF CACHE BOOL "") + set(BUILD_EXAMPLES OFF CACHE BOOL "") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -s USE_ZLIB=1") + set(LIBZIP_DO_INSTALL ON) + meshlib_add_package(libzip) + + set(E57_BUILD_TEST OFF) + set(E57_XML_PARSER TinyXML2) + meshlib_add_package(libE57Format) + + set(OPENVDB_ENABLE_UNINSTALL OFF) + set(OPENVDB_ENABLE_INSTALL OFF) + set(OPENVDB_CORE_SHARED "ON") + set(OPENVDB_CORE_STATIC "OFF") + set(OPENVDB_BUILD_BINARIES OFF) + set(OPENVDB_BUILD_VDB_PRINT OFF) + set(OPENVDB_USE_DELAYED_LOADING OFF) + set(USE_EXPLICIT_INSTANTIATION OFF) + set(Tbb_VERSION 2021.12) + set(CMAKE_CXX_FLAGS_BACKUP "${CMAKE_CXX_FLAGS}") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-missing-template-arg-list-after-template-kw") + meshlib_add_package(openvdb) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS_BACKUP}") +ELSE() # not MR_EMSCRIPTEN + set(BUILD_SHARED_LIBS ON) + + set(E57_GIT_SUBMODULE_UPDATE OFF) + set(E57_XML_PARSER TinyXML2 CACHE STRING "") + meshlib_add_package(libE57Format) + + set(FASTMCPP_BUILD_TESTS OFF) + set(FASTMCPP_BUILD_EXAMPLES OFF) + set(FASTMCPP_FETCH_CURL OFF) + set(CMAKE_POSITION_INDEPENDENT_CODE ON) + meshlib_add_package(fastmcpp) +ENDIF() diff --git a/thirdparty/libE57Format b/thirdparty/libE57Format index 46eb0d02e00a..1816f72de58b 160000 --- a/thirdparty/libE57Format +++ b/thirdparty/libE57Format @@ -1 +1 @@ -Subproject commit 46eb0d02e00a6b0aeaee3e6655328b78c7e07b5b +Subproject commit 1816f72de58b27208a8f03c7213ba4039517402a diff --git a/thirdparty/licenses/manifest.json b/thirdparty/licenses/manifest.json index 03fc932ce7d3..f08f0c2de2cc 100644 --- a/thirdparty/licenses/manifest.json +++ b/thirdparty/licenses/manifest.json @@ -341,7 +341,7 @@ "type": "submodule", "path": "thirdparty/libE57Format" }, - "version": "46eb0d02e00a6b0aeaee3e6655328b78c7e07b5b" + "version": "1816f72de58b27208a8f03c7213ba4039517402a" }, { "id": "tinyxml2",