diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 16a9dc3fd4..66a59375e4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -46,7 +46,7 @@ jobs: fmt_ver: 7.1.0 pybind11_ver: v2.7.0 setenvs: export PUGIXML_VERSION=v1.9 WEBP_VERSION=v1.1.0 USE_OPENVDB=0 - - desc: clang10/C++14 avx2 exr3.1 ocio2.0 + - desc: clang10/C++17 avx2 exr3.1 ocio2.0 nametag: linux-clang10-cpp14 runner: ubuntu-latest container: aswf/ci-osl:2021-clang10 @@ -528,16 +528,17 @@ jobs: runner: windows-2019 vsver: 2019 generator: "Visual Studio 16 2019" - openexr_ver: v3.2.4 + # openexr_ver: v3.2.4 python_ver: 3.7 # simd: sse4.2 - desc: windows-2022 runner: windows-2022 vsver: 2022 generator: "Visual Studio 17 2022" - openexr_ver: v3.2.4 + # openexr_ver: v3.2.4 python_ver: "3.9" # simd: sse4.2 + # setenvs: export OpenImageIO_BUILD_MISSING_DEPS=none runs-on: ${{ matrix.runner }} env: PYTHON_VERSION: ${{matrix.python_ver}} @@ -570,6 +571,7 @@ jobs: path: | build/cmake-save build/testsuite/*/*.* + build/deps/dist/*/*.cmake !build/testsuite/oiio-images !build/testsuite/openexr-images !build/testsuite/fits-images diff --git a/CMakeLists.txt b/CMakeLists.txt index 2e64d5a839..bc56593959 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -49,7 +49,6 @@ list (APPEND CMAKE_MODULE_PATH include (colors) include (set_utils) include (check_is_enabled) -include (checked_find_package) include (fancy_add_executable) # If the user wants to use Conan to build dependencies, they will have done @@ -169,6 +168,9 @@ include (add_oiio_plugin) # All the C++ and compiler related options and adjustments include (compiler) +# Dependency finding utilities and all dependency-related options +include (dependency_utils) + # Utilities and options related to finding python and making python bindings include (pythonutils) @@ -320,3 +322,5 @@ endif () if (PROJECT_IS_TOP_LEVEL) include (packaging) endif () + +print_package_notfound_report () diff --git a/src/build-scripts/build_libtiff.bash b/src/build-scripts/build_libtiff.bash index 64e4b17f16..ac1724bc2c 100755 --- a/src/build-scripts/build_libtiff.bash +++ b/src/build-scripts/build_libtiff.bash @@ -59,5 +59,6 @@ popd # Set up paths. These will only affect the caller if this script is # run with 'source' rather than in a separate shell. export LIBTIFF_ROOT=$LIBTIFF_INSTALL_DIR +export Tiff_ROOT=$LIBTIFF_INSTALL_DIR export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:${LIBTIFF_INSTALL_DIR}/lib diff --git a/src/build-scripts/build_openexr.bash b/src/build-scripts/build_openexr.bash index 36a1f33ee6..52d6cbbe48 100755 --- a/src/build-scripts/build_openexr.bash +++ b/src/build-scripts/build_openexr.bash @@ -43,6 +43,7 @@ cmake -S . -B ${OPENEXR_BUILD_DIR} \ -DCMAKE_BUILD_TYPE=${OPENEXR_BUILD_TYPE} \ -DCMAKE_INSTALL_PREFIX="${OPENEXR_INSTALL_DIR}" \ -DCMAKE_PREFIX_PATH="${CMAKE_PREFIX_PATH}" \ + -DBUILD_SHARED_LIBS=${OpenEXR_LOCAL_BUILD_SHARED_LIBS:=ON} \ -DOPENEXR_BUILD_UTILS=0 \ -DBUILD_TESTING=0 \ -DOPENEXR_VIEWERS_ENABLE=0 \ diff --git a/src/build-scripts/ci-startup.bash b/src/build-scripts/ci-startup.bash index 80ddedda04..a46fd517db 100755 --- a/src/build-scripts/ci-startup.bash +++ b/src/build-scripts/ci-startup.bash @@ -29,7 +29,6 @@ export UBSAN_OPTIONS=suppressions=$PWD/src/build-scripts/ubsan-suppressions.txt export PYTHON_VERSION=${PYTHON_VERSION:="3.7"} export PYTHONPATH=$OpenImageIO_ROOT/lib/python${PYTHON_VERSION}/site-packages:$PYTHONPATH -export BUILD_MISSING_DEPS=${BUILD_MISSING_DEPS:=1} export COMPILER=${COMPILER:=gcc} export CC=${CC:=gcc} export CXX=${CXX:=g++} @@ -48,6 +47,9 @@ export DYLD_LIBRARY_PATH=${LOCAL_DEPS_DIR}/dist/lib:$DYLD_LIBRARY_PATH # export OCIO="$PWD/testsuite/common/OpenColorIO/nuke-default/config.ocio" export TESTSUITE_CLEANUP_ON_SUCCESS=${TESTSUITE_CLEANUP_ON_SUCCESS:=1} +# For CI, default to building missing dependencies automatically +export OpenImageIO_BUILD_MISSING_DEPS=${OpenImageIO_BUILD_MISSING_DEPS:=all} + # Sonar export BUILD_WRAPPER_OUT_DIR="${PWD}/bw_output" export BW_OUTPUT_DIR="${PWD}/bw_output" diff --git a/src/build-scripts/ci-test.bash b/src/build-scripts/ci-test.bash index 40f97cc265..9d7e86712f 100755 --- a/src/build-scripts/ci-test.bash +++ b/src/build-scripts/ci-test.bash @@ -19,7 +19,7 @@ fi # debugging info in the CI logs. # echo ; echo "Results of oiiotool --version:" -$OpenImageIO_ROOT/bin/oiiotool --version || true +$OpenImageIO_ROOT/bin/oiiotool --version echo ; echo "Results of oiiotool brief help:" $OpenImageIO_ROOT/bin/oiiotool || true echo ; echo "Results of oiiotool full --help:" diff --git a/src/build-scripts/gh-win-installdeps.bash b/src/build-scripts/gh-win-installdeps.bash index 053c95c565..e5423939be 100755 --- a/src/build-scripts/gh-win-installdeps.bash +++ b/src/build-scripts/gh-win-installdeps.bash @@ -114,16 +114,18 @@ source src/build-scripts/build_pybind11.bash echo "CMAKE_PREFIX_PATH = $CMAKE_PREFIX_PATH" -OPENEXR_CXX_FLAGS=" /W1 /EHsc /DWIN32=1 " -#OPENEXR_BUILD_TYPE=$CMAKE_BUILD_TYPE -OPENEXR_INSTALL_DIR=$DEP_DIR -source src/build-scripts/build_openexr.bash -export PATH="$OPENEXR_INSTALL_DIR/bin:$OPENEXR_INSTALL_DIR/lib:$PATH" -export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$PATH -# the above line is admittedly sketchy - -cp $DEP_DIR/lib/*.lib $DEP_DIR/bin -cp $DEP_DIR/bin/*.dll $DEP_DIR/lib +if [[ "$OPENEXR_VERSION" != "" ]] ; then + OPENEXR_CXX_FLAGS=" /W1 /EHsc /DWIN32=1 " + #OPENEXR_BUILD_TYPE=$CMAKE_BUILD_TYPE + OPENEXR_INSTALL_DIR=$DEP_DIR + source src/build-scripts/build_openexr.bash + export PATH="$OPENEXR_INSTALL_DIR/bin:$OPENEXR_INSTALL_DIR/lib:$PATH" + export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$PATH + # the above line is admittedly sketchy +fi + +cp $DEP_DIR/lib/*.lib $DEP_DIR/bin || true +cp $DEP_DIR/bin/*.dll $DEP_DIR/lib || true echo "DEP_DIR $DEP_DIR :" ls -R -l "$DEP_DIR" diff --git a/src/cmake/Config.cmake.in b/src/cmake/Config.cmake.in index 0ee290b4d4..30d4e3a5d2 100644 --- a/src/cmake/Config.cmake.in +++ b/src/cmake/Config.cmake.in @@ -8,20 +8,11 @@ include(CMakeFindDependencyMacro) # add here all the find_dependency() whenever switching to config based dependencies if (NOT @OPENIMAGEIO_CONFIG_DO_NOT_FIND_IMATH@ AND NOT OPENIMAGEIO_CONFIG_DO_NOT_FIND_IMATH) - if (@OpenEXR_VERSION@ VERSION_GREATER_EQUAL 3.0) - find_dependency(Imath @Imath_VERSION@ - HINTS @Imath_DIR@) - elseif (@OpenEXR_VERSION@ VERSION_GREATER_EQUAL 2.4 AND @FOUND_OPENEXR_WITH_CONFIG@) - find_dependency(IlmBase @OpenEXR_VERSION@ - HINTS @IlmBase_DIR@ @OpenEXR_DIR@) - find_dependency(OpenEXR @OpenEXR_VERSION@ - HINTS @OpenEXR_DIR@) - find_dependency(ZLIB @ZLIB_VERSION@) # Because OpenEXR doesn't do it - find_dependency(Threads) # Because OpenEXR doesn't do it - endif () + find_dependency(Imath @Imath_VERSION@ + HINTS @Imath_DIR@) endif () -if (NOT @OIIO_USING_FMT_LOCAL@ AND NOT @INTERNALIZE_FMT@) +if (NOT @fmt_LOCAL_BUILD@ AND NOT @OIIO_INTERNALIZE_FMT@) find_dependency(fmt) endif () diff --git a/src/cmake/build_Imath.cmake b/src/cmake/build_Imath.cmake new file mode 100644 index 0000000000..25b7961111 --- /dev/null +++ b/src/cmake/build_Imath.cmake @@ -0,0 +1,47 @@ +# Copyright Contributors to the OpenImageIO project. +# SPDX-License-Identifier: Apache-2.0 +# https://github.com/AcademySoftwareFoundation/OpenImageIO + +###################################################################### +# Imath by hand! +###################################################################### + +set_cache (Imath_BUILD_VERSION 3.1.10 "Imath version for local builds") +set (Imath_GIT_REPOSITORY "https://github.com/AcademySoftwareFoundation/Imath") +set (Imath_GIT_TAG "v${Imath_BUILD_VERSION}") +set_cache (Imath_BUILD_SHARED_LIBS ${LOCAL_BUILD_SHARED_LIBS_DEFAULT} + DOC "Should a local Imath build, if necessary, build shared libraries" ADVANCED) + +string (MAKE_C_IDENTIFIER ${Imath_BUILD_VERSION} Imath_VERSION_IDENT) + +build_dependency_with_cmake(Imath + VERSION ${Imath_BUILD_VERSION} + GIT_REPOSITORY ${Imath_GIT_REPOSITORY} + GIT_TAG ${Imath_GIT_TAG} + CMAKE_ARGS + -D BUILD_SHARED_LIBS=${Imath_BUILD_SHARED_LIBS} + # Don't built unnecessary parts of Imath + -D BUILD_TESTING=OFF + -D IMATH_BUILD_EXAMPLES=OFF + -D IMATH_BUILD_PYTHON=OFF + -D IMATH_BUILD_TESTING=OFF + -D IMATH_BUILD_TOOLS=OFF + -D IMATH_INSTALL_DOCS=OFF + -D IMATH_INSTALL_PKG_CONFIG=OFF + -D IMATH_INSTALL_TOOLS=OFF + # Give the library a custom name and symbol namespace so it can't + # conflict with any others in the system or linked into the same app. + # not needed -D IMATH_NAMESPACE_CUSTOM=1 + # not needed -D IMATH_INTERNAL_NAMESPACE=${PROJ_NAMESPACE_V}_Imath_${Imath_VERSION_IDENT} + -D IMATH_LIB_SUFFIX=_v${Imath_VERSION_IDENT}_${PROJ_NAMESPACE_V} + ) + + +# Signal to caller that we need to find again at the installed location +set (Imath_REFIND TRUE) +set (Imath_REFIND_ARGS CONFIG) +set (Imath_REFIND_VERSION ${Imath_BUILD_VERSION}) + +if (Imath_BUILD_SHARED_LIBS) + install_local_dependency_libs (Imath Imath) +endif () diff --git a/src/cmake/build_OpenColorIO.cmake b/src/cmake/build_OpenColorIO.cmake new file mode 100644 index 0000000000..5905eb597a --- /dev/null +++ b/src/cmake/build_OpenColorIO.cmake @@ -0,0 +1,54 @@ +# Copyright Contributors to the OpenImageIO project. +# SPDX-License-Identifier: Apache-2.0 +# https://github.com/AcademySoftwareFoundation/OpenImageIO + +###################################################################### +# OpenColorIO by hand! +###################################################################### + +set_cache (OpenColorIO_BUILD_VERSION 2.3.2 "OpenColorIO version for local builds") +set (OpenColorIO_GIT_REPOSITORY "https://github.com/AcademySoftwareFoundation/OpenColorIO") +set (OpenColorIO_GIT_TAG "v${OpenColorIO_BUILD_VERSION}") +set_cache (OpenColorIO_BUILD_SHARED_LIBS ON + DOC "Should a local OpenColorIO build, if necessary, build shared libraries" ADVANCED) +# We would prefer to build a static OCIO, but haven't figured out how to make +# it all work with the static dependencies, it just makes things complicated +# downstream. + +string (MAKE_C_IDENTIFIER ${OpenColorIO_BUILD_VERSION} OpenColorIO_VERSION_IDENT) + +build_dependency_with_cmake(OpenColorIO + VERSION ${OpenColorIO_BUILD_VERSION} + GIT_REPOSITORY ${OpenColorIO_GIT_REPOSITORY} + GIT_TAG ${OpenColorIO_GIT_TAG} + CMAKE_ARGS + -D BUILD_SHARED_LIBS=${OpenColorIO_BUILD_SHARED_LIBS} + -D CMAKE_INSTALL_LIBDIR=lib + # Don't built unnecessary parts of OCIO + -D OCIO_BUILD_APPS=OFF + -D OCIO_BUILD_GPU_TESTS=OFF + -D OCIO_BUILD_PYTHON=OFF + -D OCIO_BUILD_TESTS=OFF + -D OCIO_USE_OIIO_FOR_APPS=OFF + -D OCIO_INSTALL_DOCS=OFF + # Make OCIO build all its dependencies statically + -D OCIO_INSTALL_EXT_PACKAGES=MISSING + # Give the library a custom name and symbol namespace so it can't + # conflict with any others in the system or linked into the same app. + -D OCIO_NAMESPACE=${PROJ_NAMESPACE_V}_OpenColorIO + -D OCIO_LIBNAME_SUFFIX=_v${OpenColorIO_VERSION_IDENT}_${PROJ_NAMESPACE_V} + ) + +# Set some things up that we'll need for a subsequent find_package to work + +#list (APPEND CMAKE_PREFIX_PATH ${OpenColorIO_LOCAL_INSTALL_DIR}) +set (OpenColorIO_ROOT ${OpenColorIO_LOCAL_INSTALL_DIR}) +set (OpenColorIO_DIR ${OpenColorIO_LOCAL_INSTALL_DIR}) + +# Signal to caller that we need to find again at the installed location +set (OpenColorIO_REFIND TRUE) +set (OpenColorIO_REFIND_ARGS CONFIG) + +if (OpenColorIO_BUILD_SHARED_LIBS) + install_local_dependency_libs (OpenColorIO OpenColorIO) +endif () diff --git a/src/cmake/build_OpenEXR.cmake b/src/cmake/build_OpenEXR.cmake new file mode 100644 index 0000000000..849716fb8d --- /dev/null +++ b/src/cmake/build_OpenEXR.cmake @@ -0,0 +1,53 @@ +# Copyright Contributors to the OpenImageIO project. +# SPDX-License-Identifier: Apache-2.0 +# https://github.com/AcademySoftwareFoundation/OpenImageIO + + +set_cache (OpenEXR_BUILD_VERSION 3.2.4 "OpenEXR version for local builds") +set (OpenEXR_GIT_REPOSITORY "https://github.com/AcademySoftwareFoundation/OpenEXR") +set (OpenEXR_GIT_TAG "v${OpenEXR_BUILD_VERSION}") +set_cache (OpenEXR_BUILD_SHARED_LIBS ${LOCAL_BUILD_SHARED_LIBS_DEFAULT} + DOC "Should a local OpenEXR build, if necessary, build shared libraries" ADVANCED) + +string (MAKE_C_IDENTIFIER ${OpenEXR_BUILD_VERSION} OpenEXR_VERSION_IDENT) + +build_dependency_with_cmake(OpenEXR + VERSION ${OpenEXR_BUILD_VERSION} + GIT_REPOSITORY ${OpenEXR_GIT_REPOSITORY} + GIT_TAG ${OpenEXR_GIT_TAG} + CMAKE_ARGS + -D BUILD_SHARED_LIBS=${OpenEXR_BUILD_SHARED_LIBS} + -D OPENEXR_FORCE_INTERNAL_DEFLATE=ON + # Don't built unnecessary parts of OpenEXR + -D BUILD_TESTING=OFF + -D BUILD_WEBSITE=OFF + -D OPENEXR_BUILD_EXAMPLES=OFF + -D OPENEXR_BUILD_PYTHON=OFF + -D OPENEXR_BUILD_SHARED_LIBS=OFF + -D OPENEXR_BUILD_TOOLS=OFF + -D OPENEXR_BUILD_WEBSITE=OFF + -D OPENEXR_INSTALL_DOCS=OFF + -D OPENEXR_INSTALL_PKG_CONFIG=OFF + -D OPENEXR_INSTALL_TOOLS=OFF + # Give the library a custom name and symbol namespace so it can't + # conflict with any others in the system or linked into the same app. + -D OPENEXR_NAMESPACE_CUSTOM=1 + -D ILMTHREAD_NAMESPACE_CUSTOM=1 + -D IEX_NAMESPACE_CUSTOM=1 + -D OPENEXR_INTERNAL_IMF_NAMESPACE=${PROJ_NAMESPACE_V}_Imf_${OpenEXR_VERSION_IDENT} + -D ILMTHREAD_INTERNAL_NAMESPACE=${PROJ_NAMESPACE_V}_IlmThread_${OpenEXR_VERSION_IDENT} + -D Iex_INTERNAL_NAMESPACE=${PROJ_NAMESPACE_V}_Iex_${OpenEXR_VERSION_IDENT} + -D OPENEXR_LIB_SUFFIX=_v${OpenEXR_VERSION_IDENT}_${PROJ_NAMESPACE_V} + ) + + +# Signal to caller that we need to find again at the installed location +set (OpenEXR_REFIND TRUE) +set (OpenEXR_REFIND_ARGS CONFIG) +set (OpenEXR_REFIND_VERSION ${OpenEXR_BUILD_VERSION}) + +if (OpenEXR_BUILD_SHARED_LIBS) + install_local_dependency_libs (OpenEXR OpenEXR) + install_local_dependency_libs (OpenEXR IlmThread) + install_local_dependency_libs (OpenEXR Iex) +endif () diff --git a/src/cmake/build_Robinmap.cmake b/src/cmake/build_Robinmap.cmake new file mode 100644 index 0000000000..1aa96d9751 --- /dev/null +++ b/src/cmake/build_Robinmap.cmake @@ -0,0 +1,24 @@ +# Copyright Contributors to the OpenImageIO project. +# SPDX-License-Identifier: Apache-2.0 +# https://github.com/AcademySoftwareFoundation/OpenImageIO + +###################################################################### +# Robinmap by hand! +###################################################################### + +set_cache (Robinmap_BUILD_VERSION 1.3.0 "Robinmap version for local builds") +set (Robinmap_GIT_REPOSITORY "https://github.com/Tessil/robin-map") +set (Robinmap_GIT_TAG "v${Robinmap_BUILD_VERSION}") + +build_dependency_with_cmake(Robinmap + VERSION ${Robinmap_BUILD_VERSION} + GIT_REPOSITORY ${Robinmap_GIT_REPOSITORY} + GIT_TAG ${Robinmap_GIT_TAG} + # CMAKE_ARGS + ) + +# Set some things up that we'll need for a subsequent find_package to work +set (Robinmap_ROOT ${Robinmap_INSTALL_DIR}) + +# Signal to caller that we need to find again at the installed location +set (Robinmap_REFIND TRUE) diff --git a/src/cmake/build_fmt.cmake b/src/cmake/build_fmt.cmake new file mode 100644 index 0000000000..95a497c70a --- /dev/null +++ b/src/cmake/build_fmt.cmake @@ -0,0 +1,29 @@ +# Copyright Contributors to the OpenImageIO project. +# SPDX-License-Identifier: Apache-2.0 +# https://github.com/AcademySoftwareFoundation/OpenImageIO + +###################################################################### +# fmt by hand! +###################################################################### + +set_cache (fmt_BUILD_VERSION 10.2.1 "fmt version for local builds") +set (fmt_GIT_REPOSITORY "https://github.com/fmtlib/fmt") +set (fmt_GIT_TAG "${fmt_BUILD_VERSION}") +# Note: fmt doesn't put "v" in front of version for its git tags + +build_dependency_with_cmake(fmt + VERSION ${fmt_BUILD_VERSION} + GIT_REPOSITORY ${fmt_GIT_REPOSITORY} + GIT_TAG ${fmt_GIT_TAG} + CMAKE_ARGS + # -D CMAKE_INSTALL_LIBDIR=lib + # Don't built unnecessary parts of fmt + -D FMT_DOC=OFF + -D FMT_TEST=OFF + ) + +# Set some things up that we'll need for a subsequent find_package to work +set (fmt_ROOT ${fmt_INSTALL_DIR}) + +# Signal to caller that we need to find again at the installed location +set (fmt_REFIND TRUE) diff --git a/src/cmake/checked_find_package.cmake b/src/cmake/checked_find_package.cmake deleted file mode 100644 index 3644fde5c8..0000000000 --- a/src/cmake/checked_find_package.cmake +++ /dev/null @@ -1,214 +0,0 @@ -# Copyright Contributors to the OpenImageIO project. -# SPDX-License-Identifier: Apache-2.0 -# https://github.com/AcademySoftwareFoundation/OpenImageIO - - -set (REQUIRED_DEPS "" CACHE STRING - "Additional dependencies to consider required (semicolon-separated list, or ALL)") -set (OPTIONAL_DEPS "" CACHE STRING - "Additional dependencies to consider optional (semicolon-separated list, or ALL)") -option (ALWAYS_PREFER_CONFIG "Prefer a dependency's exported config file if it's available" OFF) - -# Track all build deps we find with checked_find_package -set (CFP_ALL_BUILD_DEPS_FOUND "") - -# Utility function to list the names and values of all variables matching -# the pattern (case-insensitive) -function (dump_matching_variables pattern) - string (TOLOWER ${pattern} _pattern_lower) - get_cmake_property(_allvars VARIABLES) - list (SORT _allvars) - foreach (_var IN LISTS _allvars) - string (TOLOWER ${_var} _var_lower) - if (_var_lower MATCHES ${_pattern_lower}) - message (STATUS " ${_var} = ${${_var}}") - endif () - endforeach () -endfunction () - - - -# checked_find_package(Pkgname ...) is a wrapper for find_package, with the -# following extra features: -# * If either `USE_Pkgname` or the all-uppercase `USE_PKGNAME` (or -# `ENABLE_Pkgname` or `ENABLE_PKGNAME`) exists as either a CMake or -# environment variable, is nonempty by contains a non-true/nonzero -# value, do not search for or use the package. The optional ENABLE -# arguments allow you to override the name of the enabling variable. In -# other words, support for the dependency is presumed to be ON, unless -# turned off explicitly from one of these sources. -# * Print a message if the package is enabled but not found. This is based -# on ${Pkgname}_FOUND or $PKGNAME_FOUND. -# * Optional DEFINITIONS ... are passed to add_compile_definitions -# if the package is found. -# * Optional SETVARIABLES ... is a list of CMake variables to set to -# TRUE if the package is found (they will not be set or changed if the -# package is not found). -# * Optional PRINT is a list of variables that will be printed -# if the package is found, if VERBOSE is on. -# * Optional DEPS is a list of hard dependencies; for each one, if -# dep_FOUND is not true, disable this package with an error message. -# * Optional ISDEPOF names another package for which the -# present package is only needed because it's a dependency, and -# therefore if is disabled, we don't bother with this -# package either. -# * Optional VERSION_MIN and VERSION_MAX, if supplied, give minimum and -# maximum versions that will be accepted. The min is inclusive, the max -# is exclusive (i.e., check for min <= version < max). Note that this is -# not the same as providing a version number to find_package, which -# checks compatibility, not minimum. Sometimes we really do just want to -# say a minimum or a range. (N.B. When our minimum CMake >= 3.19, the -# built-in way to do this is with version ranges passed to -# find_package.) -# * Optional RECOMMEND_MIN, if supplied, gives a minimum recommended -# version, accepting but warning if it is below this number (even -# if above the true minimum version accepted). The warning message -# can give an optional explanation, passed as RECOMMEND_MIN_REASON. -# * Optional CONFIG, if supplied, only accepts the package from an -# exported config and never uses a FindPackage.cmake module. -# * Optional PREFER_CONFIG, if supplied, tries to use an exported config -# file from the package before using a FindPackage.cmake module. -# * Optional DEBUG turns on extra debugging information related to how -# this package is found. -# * Found package "name version" or "name NONE" are accumulated in the list -# CFP_ALL_BUILD_DEPS_FOUND. If the optional NO_RECORD_NOTFOUND is -# supplied, un-found packags will not be recorded. -# -# N.B. This needs to be a macro, not a function, because the find modules -# will set(blah val PARENT_SCOPE) and we need that to be the global scope, -# not merely the scope for this function. -macro (checked_find_package pkgname) - cmake_parse_arguments(_pkg # prefix - # noValueKeywords: - "REQUIRED;CONFIG;PREFER_CONFIG;DEBUG;NO_RECORD_NOTFOUND" - # singleValueKeywords: - "ENABLE;ISDEPOF;VERSION_MIN;VERSION_MAX;RECOMMEND_MIN;RECOMMEND_MIN_REASON" - # multiValueKeywords: - "DEFINITIONS;PRINT;DEPS;SETVARIABLES" - # argsToParse: - ${ARGN}) - string (TOLOWER ${pkgname} pkgname_lower) - string (TOUPPER ${pkgname} pkgname_upper) - set (_pkg_VERBOSE ${VERBOSE}) - if (_pkg_DEBUG) - set (_pkg_VERBOSE ON) - endif () - if (NOT _pkg_VERBOSE) - set (${pkgname}_FIND_QUIETLY true) - set (${pkgname_upper}_FIND_QUIETLY true) - endif () - if ("${pkgname}" IN_LIST REQUIRED_DEPS OR "ALL" IN_LIST REQUIRED_DEPS) - set (_pkg_REQUIRED 1) - endif () - if ("${pkgname}" IN_LIST OPTIONAL_DEPS OR "ALL" IN_LIST OPTIONAL_DEPS) - set (_pkg_REQUIRED 0) - endif () - set (_quietskip false) - check_is_enabled (${pkgname} _enable) - set (_disablereason "") - foreach (_dep ${_pkg_DEPS}) - if (_enable AND NOT ${_dep}_FOUND) - set (_enable false) - set (ENABLE_${pkgname} OFF PARENT_SCOPE) - set (_disablereason "(because ${_dep} was not found)") - endif () - endforeach () - if (_pkg_ISDEPOF) - check_is_enabled (${_pkg_ISDEPOF} _dep_enabled) - if (NOT _dep_enabled) - set (_enable false) - set (_quietskip true) - endif () - endif () - set (_config_status "") - if (_enable OR _pkg_REQUIRED) - if (${pkgname}_FOUND OR ${pkgname_upper}_FOUND) - # was already found - elseif (_pkg_CONFIG OR _pkg_PREFER_CONFIG OR ALWAYS_PREFER_CONFIG) - find_package (${pkgname} CONFIG ${_pkg_UNPARSED_ARGUMENTS}) - if (${pkgname}_FOUND OR ${pkgname_upper}_FOUND) - set (_config_status "from CONFIG") - endif () - endif () - if (NOT (${pkgname}_FOUND OR ${pkgname_upper}_FOUND) AND NOT _pkg_CONFIG) - find_package (${pkgname} ${_pkg_UNPARSED_ARGUMENTS}) - endif() - if ((${pkgname}_FOUND OR ${pkgname_upper}_FOUND) - AND ${pkgname}_VERSION - AND (_pkg_VERSION_MIN OR _pkg_VERSION_MAX)) - if ((_pkg_VERSION_MIN AND ${pkgname}_VERSION VERSION_LESS _pkg_VERSION_MIN) - OR (_pkg_VERSION_MAX AND ${pkgname}_VERSION VERSION_GREATER _pkg_VERSION_MAX)) - message (STATUS "${ColorRed}${pkgname} ${${pkgname}_VERSION} is outside the required range ${_pkg_VERSION_MIN}...${_pkg_VERSION_MAX} ${ColorReset}") - unset (${pkgname}_FOUND) - unset (${pkgname}_VERSION) - unset (${pkgname_upper}_FOUND) - unset (${pkgname_upper}_VERSION) - endif () - endif () - if (${pkgname}_FOUND OR ${pkgname_upper}_FOUND) - foreach (_vervar ${pkgname_upper}_VERSION ${pkgname}_VERSION_STRING - ${pkgname_upper}_VERSION_STRING) - if (NOT ${pkgname}_VERSION AND ${_vervar}) - set (${pkgname}_VERSION ${${_vervar}}) - endif () - endforeach () - message (STATUS "${ColorGreen}Found ${pkgname} ${${pkgname}_VERSION} ${_config_status}${ColorReset}") - add_compile_definitions (${_pkg_DEFINITIONS}) - foreach (_v IN LISTS _pkg_SETVARIABLES) - set (${_v} TRUE) - endforeach () - if (_pkg_RECOMMEND_MIN) - if (${${pkgname}_VERSION} VERSION_LESS ${_pkg_RECOMMEND_MIN}) - message (STATUS "${ColorYellow}Recommend ${pkgname} >= ${_pkg_RECOMMEND_MIN} ${_pkg_RECOMMEND_MIN_REASON} ${ColorReset}") - endif () - endif () - string (STRIP "${pkgname} ${${pkgname}_VERSION}" app_) - list (APPEND CFP_ALL_BUILD_DEPS_FOUND "${app_}") - else () - message (STATUS "${ColorRed}${pkgname} library not found ${ColorReset}") - if (${pkgname}_ROOT) - message (STATUS "${ColorRed} ${pkgname}_ROOT was: ${${pkgname}_ROOT} ${ColorReset}") - elseif ($ENV{${pkgname}_ROOT}) - message (STATUS "${ColorRed} ENV ${pkgname}_ROOT was: ${${pkgname}_ROOT} ${ColorReset}") - else () - message (STATUS "${ColorRed} Try setting ${pkgname}_ROOT ? ${ColorReset}") - endif () - if (EXISTS "${PROJECT_SOURCE_DIR}/src/build-scripts/build_${pkgname}.bash") - message (STATUS "${ColorRed} Maybe this will help: src/build-scripts/build_${pkgname}.bash ${ColorReset}") - elseif (EXISTS "${PROJECT_SOURCE_DIR}/src/build-scripts/build_${pkgname_upper}.bash") - message (STATUS "${ColorRed} Maybe this will help: src/build-scripts/build_${pkgname_upper}.bash ${ColorReset}") - elseif (EXISTS "${PROJECT_SOURCE_DIR}/src/build-scripts/build_${pkgname_lower}.bash") - message (STATUS "${ColorRed} Maybe this will help: src/build-scripts/build_${pkgname_lower}.bash ${ColorReset}") - elseif (EXISTS "${PROJECT_SOURCE_DIR}/src/build-scripts/build_lib${pkgname_lower}.bash") - message (STATUS "${ColorRed} Maybe this will help: src/build-scripts/build_lib${pkgname_lower}.bash ${ColorReset}") - endif () - if (_pkg_REQUIRED) - message (FATAL_ERROR "${ColorRed}${pkgname} is required, aborting.${ColorReset}") - endif () - if (NOT _pkg_NO_RECORD_NOTFOUND) - list (APPEND CFP_ALL_BUILD_DEPS_FOUND "${pkgname} NONE") - endif () - endif() - if (_pkg_VERBOSE AND (${pkgname}_FOUND OR ${pkgname_upper}_FOUND OR _pkg_DEBUG)) - if (_pkg_DEBUG) - dump_matching_variables (${pkgname}) - endif () - set (_vars_to_print ${pkgname}_INCLUDES ${pkgname_upper}_INCLUDES - ${pkgname}_INCLUDE_DIR ${pkgname_upper}_INCLUDE_DIR - ${pkgname}_INCLUDE_DIRS ${pkgname_upper}_INCLUDE_DIRS - ${pkgname}_LIBRARIES ${pkgname_upper}_LIBRARIES - ${_pkg_PRINT}) - list (REMOVE_DUPLICATES _vars_to_print) - foreach (_v IN LISTS _vars_to_print) - if (NOT "${${_v}}" STREQUAL "") - message (STATUS " ${_v} = ${${_v}}") - endif () - endforeach () - endif () - else () - if (NOT _quietskip) - message (STATUS "${ColorRed}Not using ${pkgname} -- disabled ${_disablereason} ${ColorReset}") - endif () - endif () -endmacro() - diff --git a/src/cmake/colors.cmake b/src/cmake/colors.cmake index e2513c1209..a9f747bd33 100644 --- a/src/cmake/colors.cmake +++ b/src/cmake/colors.cmake @@ -9,4 +9,9 @@ set (ColorReset "${ColorEsc}[m") set (ColorRed "${ColorEsc}[31m") set (ColorGreen "${ColorEsc}[32m") set (ColorYellow "${ColorEsc}[33m") +set (ColorBlue "${ColorEsc}[34m") +set (ColorMagenta "${ColorEsc}[35m") +set (ColorBoldRed "${ColorEsc}[1;31m") +set (ColorBoldGreen "${ColorEsc}[1;32m") +set (ColorBoldYellow "${ColorEsc}[1;33m") set (ColorBoldWhite "${ColorEsc}[1;37m") diff --git a/src/cmake/compiler.cmake b/src/cmake/compiler.cmake index a124365010..50b5a2f823 100644 --- a/src/cmake/compiler.cmake +++ b/src/cmake/compiler.cmake @@ -298,7 +298,7 @@ endif () # the proper compiler directives added to generate code for those ISA # capabilities. # -set (USE_SIMD "" CACHE STRING "Use SIMD directives (0, sse2, sse3, ssse3, sse4.1, sse4.2, avx, avx2, avx512f, f16c, aes)") +set_cache (USE_SIMD "" "Use SIMD directives (0, sse2, sse3, ssse3, sse4.1, sse4.2, avx, avx2, avx512f, f16c, aes)") set (SIMD_COMPILE_FLAGS "") message (STATUS "Compiling with SIMD level ${USE_SIMD}") if (NOT USE_SIMD STREQUAL "") @@ -306,7 +306,7 @@ if (NOT USE_SIMD STREQUAL "") set (SIMD_COMPILE_FLAGS ${SIMD_COMPILE_FLAGS} "-DOIIO_NO_SIMD=1") else () set(_highest_msvc_arch 0) - string (REPLACE "," ";" SIMD_FEATURE_LIST ${USE_SIMD}) + string (REPLACE "," ";" SIMD_FEATURE_LIST "${USE_SIMD}") foreach (feature ${SIMD_FEATURE_LIST}) message (VERBOSE "SIMD feature: ${feature}") if (MSVC OR CMAKE_COMPILER_IS_INTEL) diff --git a/src/cmake/dependency_utils.cmake b/src/cmake/dependency_utils.cmake new file mode 100644 index 0000000000..bbb4174bea --- /dev/null +++ b/src/cmake/dependency_utils.cmake @@ -0,0 +1,574 @@ +# Copyright Contributors to the OpenImageIO project. +# SPDX-License-Identifier: Apache-2.0 +# https://github.com/AcademySoftwareFoundation/OpenImageIO + + +set_cache (${PROJECT_NAME}_REQUIRED_DEPS "" + "Additional dependencies to consider required (semicolon-separated list, or ALL)") +set_cache (${PROJECT_NAME}_OPTIONAL_DEPS "" + "Additional dependencies to consider optional (semicolon-separated list, or ALL)") +set_option (${PROJECT_NAME}_ALWAYS_PREFER_CONFIG + "Prefer a dependency's exported config file if it's available" OFF) + +set_cache (${PROJECT_NAME}_BUILD_MISSING_DEPS "" + "Try to download and build any of these missing dependencies (or 'all')") +set_cache (${PROJECT_NAME}_BUILD_LOCAL_DEPS "" + "Force local builds of these dependencies if possible (or 'all')") + +set_cache (${PROJECT_NAME}_LOCAL_DEPS_ROOT "${PROJECT_BINARY_DIR}/deps" + "Directory were we do local builds of dependencies") +list (APPEND CMAKE_PREFIX_PATH ${${PROJECT_NAME}_LOCAL_DEPS_ROOT}/dist) + +# Build type for locally built dependencies. Default to the same build type +# as the current project. +set_cache (${PROJECT_NAME}_DEPENDENCY_BUILD_TYPE ${CMAKE_BUILD_TYPE} + "Build type for locally built dependencies") + +if (MSVC) + # I haven't been able to get Windows local dependency builds to work with + # static libraries, so default to shared libraries on Windows until we can + # figure it out. + set_cache (LOCAL_BUILD_SHARED_LIBS_DEFAULT ON + DOC "Should a local dependency build, if necessary, build shared libraries" ADVANCED) +else () + # On non-Windows, default to static libraries for local builds. + set_cache (LOCAL_BUILD_SHARED_LIBS_DEFAULT OFF + DOC "Should a local dependency build, if necessary, build shared libraries" ADVANCED) +endif () + + +# Track all build deps we find with checked_find_package +set (CFP_ALL_BUILD_DEPS_FOUND "") + +# Track all build deps we failed to find with checked_find_package +set (CFP_ALL_BUILD_DEPS_NOTFOUND "") +set (CFP_LOCALLY_BUILDABLE_DEPS_NOTFOUND "") + +# Track all build deps we found but were of inadequate version +set (CFP_ALL_BUILD_DEPS_BADVERSION "") +set (CFP_LOCALLY_BUILDABLE_DEPS_BADVERSION "") + +# Which dependencies did we build locally +set (CFP_LOCALLY_BUILT_DEPS "") + + + +# Utility function to list the names and values of all variables matching +# the pattern (case-insensitive) +function (dump_matching_variables pattern) + string (TOLOWER ${pattern} _pattern_lower) + get_cmake_property(_allvars VARIABLES) + list (SORT _allvars) + foreach (_var IN LISTS _allvars) + string (TOLOWER ${_var} _var_lower) + if (_var_lower MATCHES ${_pattern_lower}) + message (STATUS " ${_var} = ${${_var}}") + endif () + endforeach () +endfunction () + + +# Helper: Print a report about missing dependencies and give insructions on +# how to turn on automatic local dependency building. +function (print_package_notfound_report) + if (CFP_ALL_BUILD_DEPS_NOTFOUND OR CFP_ALL_BUILD_DEPS_BADVERSION) + message (STATUS) + message (STATUS "${ColorBoldYellow}=========================================================================${ColorReset}") + message (STATUS) + if (CFP_ALL_BUILD_DEPS_NOTFOUND) + message (STATUS "${ColorBoldWhite}The following dependencies were not found:${ColorReset}") + foreach (_pkg IN LISTS CFP_ALL_BUILD_DEPS_NOTFOUND) + if (_pkg IN_LIST CFP_LOCALLY_BUILT_DEPS) + message (STATUS " ${_pkg} ${ColorMagenta}(BUILT LOCALLY)${ColorReset}") + else () + message (STATUS " ${_pkg}") + endif () + endforeach () + endif () + if (CFP_ALL_BUILD_DEPS_BADVERSION) + message (STATUS "${ColorBoldWhite}The following dependencies were found but were too old:${ColorReset}") + foreach (_pkg IN LISTS CFP_ALL_BUILD_DEPS_BADVERSION) + if (_pkg IN_LIST CFP_LOCALLY_BUILT_DEPS) + message (STATUS " ${_pkg} ${ColorMagenta}(BUILT LOCALLY)${ColorReset}") + else () + message (STATUS " ${_pkg}") + endif () + endforeach () + endif () + if (CFP_LOCALLY_BUILDABLE_DEPS_NOTFOUND OR CFP_LOCALLY_BUILDABLE_DEPS_BADVERSION) + message (STATUS) + message (STATUS "${ColorBoldWhite}For some of these, we can build them locally:${ColorReset}") + foreach (_pkg IN LISTS CFP_LOCALLY_BUILDABLE_DEPS_NOTFOUND CFP_LOCALLY_BUILDABLE_DEPS_BADVERSION) + message (STATUS " ${_pkg}") + endforeach () + message (STATUS "${ColorBoldWhite}To build them automatically if not found, build again with option:${ColorReset}") + message (STATUS " ${ColorBoldGreen}-D${PROJECT_NAME}_BUILD_MISSING_DEPS=all${ColorReset}") + endif () + message (STATUS) + message (STATUS "${ColorBoldYellow}=========================================================================${ColorReset}") + message (STATUS) + endif () +endfunction () + + + +# Helper: called if a package is not found, print error messages, including +# a fatal error if the package was required. +function (handle_package_notfound pkgname required) + message (STATUS "${ColorRed}${pkgname} library not found ${ColorReset}") + if (${pkgname}_ROOT) + message (STATUS " ${pkgname}_ROOT was: ${${pkgname}_ROOT}") + elseif ($ENV{${pkgname}_ROOT}) + message (STATUS " ENV ${pkgname}_ROOT was: ${${pkgname}_ROOT}") + else () + message (STATUS " Try setting ${pkgname}_ROOT ?") + endif () + if (EXISTS "${PROJECT_SOURCE_DIR}/src/build-scripts/build_${pkgname}.bash") + message (STATUS " Maybe this will help: src/build-scripts/build_${pkgname}.bash") + elseif (EXISTS "${PROJECT_SOURCE_DIR}/src/build-scripts/build_${pkgname_upper}.bash") + message (STATUS " Maybe this will help: src/build-scripts/build_${pkgname_upper}.bash") + elseif (EXISTS "${PROJECT_SOURCE_DIR}/src/build-scripts/build_${pkgname_lower}.bash") + message (STATUS " Maybe this will help: src/build-scripts/build_${pkgname_lower}.bash") + elseif (EXISTS "${PROJECT_SOURCE_DIR}/src/build-scripts/build_lib${pkgname_lower}.bash") + message (STATUS " Maybe this will help: src/build-scripts/build_lib${pkgname_lower}.bash") + endif () + if (required) + print_package_notfound_report() + message (FATAL_ERROR "${pkgname} is required, aborting.") + endif () +endfunction () + + +# Check whether the package's version (in pkgversion) lies within versionmin +# and versionmax (inclusive). Store TRUE result variable if the version was in +# range, FALSE if it was out of range. If it did not match, clear a bunch of +# variables that may have been set by the find_package call (including +# clearing the package's FOUND variable). +function (reject_out_of_range_versions pkgname pkgversion versionmin versionmax result) + set (${result} FALSE PARENT_SCOPE) + string (TOUPPER ${pkgname} pkgname_upper) + # message (STATUS "roorv: ${pkgname} ${pkgversion} ${versionmin} ${versionmax}") + if (NOT ${pkgname}_FOUND AND NOT ${pkgname_upper}_FOUND) + message (STATUS "${pkgname} was not found") + elseif ("${pkgversion}" STREQUAL "") + message (ERROR "${pkgname} found but version was empty") + elseif (pkgversion VERSION_LESS versionmin + OR pkgversion VERSION_GREATER versionmax) + # message (STATUS "${ColorRed}${pkgname} ${pkgversion} is outside the required range ${versionmin}...${versionmax} ${ColorReset}") + # list (APPEND CFP_ALL_BUILD_DEPS_BADVERSION ${pkgname}) + # if (${pkgname}_local_build_script_exists) + # list (APPEND CFP_LOCALLY_BUILDABLE_DEPS_BADVERSION ${pkgname}) + # endif () + unset (${pkgname}_FOUND PARENT_SCOPE) + unset (${pkgname}_VERSION PARENT_SCOPE) + unset (${pkgname}_INCLUDE PARENT_SCOPE) + unset (${pkgname}_INCLUDES PARENT_SCOPE) + unset (${pkgname}_LIBRARY PARENT_SCOPE) + unset (${pkgname}_LIBRARIES PARENT_SCOPE) + unset (${pkgname_upper}_FOUND PARENT_SCOPE) + unset (${pkgname_upper}_VERSION PARENT_SCOPE) + unset (${pkgname_upper}_INCLUDE PARENT_SCOPE) + unset (${pkgname_upper}_INCLUDES PARENT_SCOPE) + unset (${pkgname_upper}_LIBRARY PARENT_SCOPE) + unset (${pkgname_upper}_LIBRARIES PARENT_SCOPE) + else () + # Version matched the range + set (${result} TRUE PARENT_SCOPE) + # message (STATUS "${pkgname} ${pkgversion} is INSIDE the required range ${versionmin}...${versionmax}") + endif () +endfunction () + + + +# checked_find_package(Pkgname ...) is a wrapper for find_package, with the +# following extra features: +# * If either `USE_Pkgname` or the all-uppercase `USE_PKGNAME` (or +# `ENABLE_Pkgname` or `ENABLE_PKGNAME`) exists as either a CMake or +# environment variable, is nonempty by contains a non-true/nonzero +# value, do not search for or use the package. The optional ENABLE +# arguments allow you to override the name of the enabling variable. In +# other words, support for the dependency is presumed to be ON, unless +# turned off explicitly from one of these sources. +# * Print a message if the package is enabled but not found. This is based +# on ${Pkgname}_FOUND or $PKGNAME_FOUND. +# * Optional DEFINITIONS ... are passed to add_compile_definitions +# if the package is found. +# * Optional SETVARIABLES ... is a list of CMake variables to set to +# TRUE if the package is found (they will not be set or changed if the +# package is not found). +# * Optional PRINT is a list of variables that will be printed +# if the package is found, if VERBOSE is on. +# * Optional DEPS is a list of hard dependencies; for each one, if +# dep_FOUND is not true, disable this package with an error message. +# * Optional ISDEPOF names another package for which the +# present package is only needed because it's a dependency, and +# therefore if is disabled, we don't bother with this +# package either. +# * Optional VERSION_MIN and VERSION_MAX, if supplied, give minimum and +# maximum versions that will be accepted. The min is inclusive, the max +# is exclusive (i.e., check for min <= version < max). Note that this is +# not the same as providing a version number to find_package, which +# checks compatibility, not minimum. Sometimes we really do just want to +# say a minimum or a range. (N.B. When our minimum CMake >= 3.19, the +# built-in way to do this is with version ranges passed to +# find_package.) +# * Optional RECOMMEND_MIN, if supplied, gives a minimum recommended +# version, accepting but warning if it is below this number (even +# if above the true minimum version accepted). The warning message +# can give an optional explanation, passed as RECOMMEND_MIN_REASON. +# * Optional CONFIG, if supplied, only accepts the package from an +# exported config and never uses a FindPackage.cmake module. +# * Optional PREFER_CONFIG, if supplied, tries to use an exported config +# file from the package before using a FindPackage.cmake module. +# * Optional DEBUG turns on extra debugging information related to how +# this package is found. +# * Found package "name version" or "name NONE" are accumulated in the list +# CFP_ALL_BUILD_DEPS_FOUND. If the optional NO_RECORD_NOTFOUND is +# supplied, un-found packags will not be recorded. +# * Optional BUILD_LOCAL, if supplied, if followed by a token that specifies +# the conditions under which to build the package locally by including a +# script included in src/cmake/build_${pkgname}.cmake. If the condition is +# "always", it will attempt to do so unconditionally. If "missing", it +# will only do so if the package is not found. Also note that if the +# global ${PROJECT_NAME}_BUILD_LOCAL_DEPS contains the package name or +# is "all", it will behave as if set to "always", and if the variable +# ${PROJECT_NAME}_BUILD_MISSING_DEPS contains the package name or is +# "all", it will behave as if set to "missing". +# * Optional NO_FP_RANGE_CHECK avoids passing the version range to +# find_package itself. +# +# Explanation about local builds: +# +# If the package isn't found externally in the usual places or doesn't meet +# the version criteria, we check for the existance of a file at +# `src/build-scripts/build_.cmake`. If that exists, we include and +# execute it. The script can do whatever it wants, but should either (a) +# somehow set up the link targets that would have been found had the package +# had been found, or (b) set the variable `_REFIND` to a true value +# and have done something to ensure that the package will be found if we try a +# second time. For (b), typically that might mean downloading the package and +# building it locally, and then setting the `_ROOT` to where it's +# installed. +# +# N.B. This needs to be a macro, not a function, because the find modules +# will set(blah val PARENT_SCOPE) and we need that to be the global scope, +# not merely the scope for this function. +macro (checked_find_package pkgname) + # + # Various setup logic + # + cmake_parse_arguments(_pkg # prefix + # noValueKeywords: + "REQUIRED;CONFIG;PREFER_CONFIG;DEBUG;NO_RECORD_NOTFOUND;NO_FP_RANGE_CHECK" + # singleValueKeywords: + "ENABLE;ISDEPOF;VERSION_MIN;VERSION_MAX;RECOMMEND_MIN;RECOMMEND_MIN_REASON;BUILD_LOCAL" + # multiValueKeywords: + "DEFINITIONS;PRINT;DEPS;SETVARIABLES" + # argsToParse: + ${ARGN}) + string (TOLOWER ${pkgname} pkgname_lower) + string (TOUPPER ${pkgname} pkgname_upper) + set (_pkg_VERBOSE ${VERBOSE}) + if (_pkg_DEBUG) + set (_pkg_VERBOSE ON) + endif () + if (NOT _pkg_VERBOSE) + set (${pkgname}_FIND_QUIETLY true) + set (${pkgname_upper}_FIND_QUIETLY true) + endif () + if ("${pkgname}" IN_LIST ${PROJECT_NAME}_REQUIRED_DEPS OR "ALL" IN_LIST ${PROJECT_NAME}_REQUIRED_DEPS) + set (_pkg_REQUIRED 1) + endif () + if ("${pkgname}" IN_LIST ${PROJECT_NAME}_OPTIONAL_DEPS OR "ALL" IN_LIST ${PROJECT_NAME}_OPTIONAL_DEPS) + set (_pkg_REQUIRED 0) + endif () + # string (TOLOWER "${_pkg_BUILD_LOCAL}" _pkg_BUILD_LOCAL) + if ("${pkgname}" IN_LIST ${PROJECT_NAME}_BUILD_LOCAL_DEPS + OR ${PROJECT_NAME}_BUILD_LOCAL_DEPS STREQUAL "all") + set (_pkg_BUILD_LOCAL "always") + elseif ("${pkgname}" IN_LIST ${PROJECT_NAME}_BUILD_MISSING_DEPS + OR ${PROJECT_NAME}_BUILD_MISSING_DEPS STREQUAL "all") + set_if_not (_pkg_BUILD_LOCAL "missing") + endif () + set (${pkgname}_local_build_script "${PROJECT_SOURCE_DIR}/src/cmake/build_${pkgname}.cmake") + if (EXISTS ${${pkgname}_local_build_script}) + set (${pkgname}_local_build_script_exists TRUE) + endif () + if (_pkg_BUILD_LOCAL AND NOT EXISTS "${${pkgname}_local_build_script}") + unset (_pkg_BUILD_LOCAL) + endif () + set (_quietskip false) + check_is_enabled (${pkgname} _enable) + set (_disablereason "") + foreach (_dep ${_pkg_DEPS}) + if (_enable AND NOT ${_dep}_FOUND) + set (_enable false) + set (ENABLE_${pkgname} OFF PARENT_SCOPE) + set (_disablereason "(because ${_dep} was not found)") + endif () + endforeach () + if (_pkg_ISDEPOF) + check_is_enabled (${_pkg_ISDEPOF} _dep_enabled) + if (NOT _dep_enabled) + set (_enable false) + set (_quietskip true) + endif () + endif () + set (_config_status "") + unset (_${pkgname}_version_range) + if (_pkg_BUILD_LOCAL AND NOT _pkg_NO_FP_RANGE_CHECK) + # SKIP THIS -- I find it unreliable because the package's exported + # PKGConfigVersion.cmake has might have arbitrary rules. Use our own + # MIN_VERSION and MAX_VERSION parameters to manually check instead. + # + if (_pkg_VERSION_MIN AND _pkg_VERSION_MAX AND CMAKE_VERSION VERSION_GREATER_EQUAL 3.19) + set (_${pkgname}_version_range "${_pkg_VERSION_MIN}...<${_pkg_VERSION_MAX}") + elseif (_pkg_VERSION_MIN) + set (_${pkgname}_version_range "${_pkg_VERSION_MIN}") + endif () + endif () + set_if_not (_pkg_VERSION_MIN "0.0.1") + set_if_not (_pkg_VERSION_MAX "10000.0.0") + # + # Now we try to find or build + # + set (${pkgname}_FOUND FALSE) + set (${pkgname}_LOCAL_BUILD FALSE) + if (_enable OR _pkg_REQUIRED) + # Unless instructed not to, try to find the package externally + # installed. + if (${pkgname}_FOUND OR ${pkgname_upper}_FOUND OR _pkg_BUILD_LOCAL STREQUAL "always") + # was already found, or we're forcing a local build + elseif (_pkg_CONFIG OR _pkg_PREFER_CONFIG OR ${PROJECT_NAME}_ALWAYS_PREFER_CONFIG) + find_package (${pkgname} ${_${pkgname}_version_range} CONFIG ${_pkg_UNPARSED_ARGUMENTS}) + reject_out_of_range_versions (${pkgname} "${${pkgname}_VERSION}" + ${_pkg_VERSION_MIN} ${_pkg_VERSION_MAX} + _pkg_version_in_range) + if (${pkgname}_FOUND OR ${pkgname_upper}_FOUND) + set (_config_status "from CONFIG") + endif () + endif () + if (NOT ${pkgname}_FOUND AND NOT ${pkgname_upper}_FOUND AND NOT _pkg_BUILD_LOCAL STREQUAL "always" AND NOT _pkg_CONFIG) + find_package (${pkgname} ${_${pkgname}_version_range} ${_pkg_UNPARSED_ARGUMENTS}) + endif() + if (NOT ${pkgname}_FOUND AND NOT ${pkgname_upper}_FOUND) + list (APPEND CFP_ALL_BUILD_DEPS_NOTFOUND ${pkgname}) + if (${pkgname}_local_build_script_exists) + list (APPEND CFP_LOCALLY_BUILDABLE_DEPS_NOTFOUND ${pkgname}) + endif () + endif () + # Some FindPackage modules set nonstandard variables for the versions + if (NOT ${pkgname}_VERSION AND ${pkgname_upper}_VERSION) + set (${pkgname}_VERSION ${${pkgname_upper}_VERSION}) + endif () + if (NOT ${pkgname}_VERSION AND ${pkgname_upper}_VERSION_STRING) + set (${pkgname}_VERSION ${${pkgname_upper}_VERSION_STRING}) + endif () + # If the package was found but the version is outside the required + # range, unset the relevant variables so that we can try again fresh. + if ((${pkgname}_FOUND OR ${pkgname_upper}_FOUND) AND ${pkgname}_VERSION) + reject_out_of_range_versions (${pkgname} ${${pkgname}_VERSION} + ${_pkg_VERSION_MIN} ${_pkg_VERSION_MAX} + _pkg_version_in_range) + if (NOT _pkg_version_in_range) + message (STATUS "${ColorRed}${pkgname} ${${pkgname}_VERSION} is outside the required range ${_pkg_VERSION_MIN}...${_pkg_VERSION_MAX} ${ColorReset}") + list (APPEND CFP_ALL_BUILD_DEPS_BADVERSION ${pkgname}) + if (${pkgname}_local_build_script_exists) + list (APPEND CFP_LOCALLY_BUILDABLE_DEPS_BADVERSION ${pkgname}) + endif () + endif () + endif () + # If we haven't found the package yet and are allowed to build a local + # version, and a build_.cmake exists, include it to build the + # package locally. + if (NOT ${pkgname}_FOUND AND NOT ${pkgname_upper}_FOUND + AND (_pkg_BUILD_LOCAL STREQUAL "always" OR _pkg_BUILD_LOCAL STREQUAL "missing") + AND EXISTS "${${pkgname}_local_build_script}") + message (STATUS "${ColorMagenta}Building package ${pkgname} ${${pkgname}_VERSION} locally${ColorReset}") + list(APPEND CMAKE_MESSAGE_INDENT " ") + include("${${pkgname}_local_build_script}") + list(POP_BACK CMAKE_MESSAGE_INDENT) + # set (${pkgname}_FOUND TRUE) + set (${pkgname}_LOCAL_BUILD TRUE) + list (APPEND CFP_LOCALLY_BUILT_DEPS ${pkgname}) + list (REMOVE_ITEM CFP_LOCALLY_BUILDABLE_DEPS_NOTFOUND ${pkgname}) + endif() + # If the local build instrctions set _REFIND, then try a find + # again to pick up the local one, at which point we can proceed as if + # it had been found externally all along. The local build script can + # also optionally set the following hints: + # ${pkgname}_REFIND_VERSION : the version that was just installed, + # to specifically find. + # ${pkgname}_REFIND_ARGS : additional arguments to pass to find_package + if (${pkgname}_REFIND) + message (STATUS "Refinding ${pkgname} with ${pkgname}_ROOT=${${pkgname}_ROOT}") + find_package (${pkgname} ${_pkg_UNPARSED_ARGUMENTS} ${${pkgname}_REFIND_ARGS}) + unset (${pkgname}_REFIND) + endif() + # It's all downhill from here: if we found the package, follow the + # various instructions we got about variables to set, compile + # definitions to add, etc. + if (${pkgname}_FOUND OR ${pkgname_upper}_FOUND) + foreach (_vervar ${pkgname_upper}_VERSION ${pkgname}_VERSION_STRING + ${pkgname_upper}_VERSION_STRING) + if (NOT ${pkgname}_VERSION AND ${_vervar}) + set (${pkgname}_VERSION ${${_vervar}}) + endif () + endforeach () + message (STATUS "${ColorGreen}Found ${pkgname} ${${pkgname}_VERSION} ${_config_status}${ColorReset}") + add_compile_definitions (${_pkg_DEFINITIONS}) + foreach (_v IN LISTS _pkg_SETVARIABLES) + set (${_v} TRUE) + endforeach () + if (_pkg_RECOMMEND_MIN) + if (${${pkgname}_VERSION} VERSION_LESS ${_pkg_RECOMMEND_MIN}) + message (STATUS "${ColorYellow}Recommend ${pkgname} >= ${_pkg_RECOMMEND_MIN} ${_pkg_RECOMMEND_MIN_REASON} ${ColorReset}") + endif () + endif () + string (STRIP "${pkgname} ${${pkgname}_VERSION}" app_) + list (APPEND CFP_ALL_BUILD_DEPS_FOUND "${app_}") + else () + handle_package_notfound (${pkgname} ${_pkg_REQUIRED}) + if (NOT _pkg_NO_RECORD_NOTFOUND) + list (APPEND CFP_ALL_BUILD_DEPS_FOUND "${pkgname} NONE") + endif () + endif() + if (_pkg_VERBOSE AND (${pkgname}_FOUND OR ${pkgname_upper}_FOUND OR _pkg_DEBUG)) + if (_pkg_DEBUG) + dump_matching_variables (${pkgname}) + endif () + set (_vars_to_print ${pkgname}_INCLUDES ${pkgname_upper}_INCLUDES + ${pkgname}_INCLUDE_DIR ${pkgname_upper}_INCLUDE_DIR + ${pkgname}_INCLUDE_DIRS ${pkgname_upper}_INCLUDE_DIRS + ${pkgname}_LIBRARIES ${pkgname_upper}_LIBRARIES + ${_pkg_PRINT}) + list (REMOVE_DUPLICATES _vars_to_print) + foreach (_v IN LISTS _vars_to_print) + if (NOT "${${_v}}" STREQUAL "") + message (STATUS " ${_v} = ${${_v}}") + endif () + endforeach () + endif () + else () + if (NOT _quietskip) + message (STATUS "${ColorRed}Not using ${pkgname} -- disabled ${_disablereason} ${ColorReset}") + endif () + endif () + unset (_${pkgname}_version_range) +endmacro() + + + +# Helper to build a dependency with CMake. Given a package name, git repo and +# tag, and optional cmake args, it will clone the repo into the surrounding +# project's build area, configures, and build sit, and installs it into a +# special dist area (unless the NOINSTALL option is given). +# +# After running, it leaves the following variables set: +# ${pkgname}_LOCAL_SOURCE_DIR +# ${pkgname}_LOCAL_BUILD_DIR +# ${pkgname}_LOCAL_INSTALL_DIR +# +# Unless NOINSTALL is specified, the after the installation step, the +# installation directory will be added to the CMAKE_PREFIX_PATH and also will +# be stored in the ${pkgname}_ROOT variable. +# +macro (build_dependency_with_cmake pkgname) + cmake_parse_arguments(_pkg # prefix + # noValueKeywords: + "NOINSTALL" + # singleValueKeywords: + "GIT_REPOSITORY;GIT_TAG;VERSION" + # multiValueKeywords: + "CMAKE_ARGS" + # argsToParse: + ${ARGN}) + + message (STATUS "Building local ${pkgname} ${_pkg_VERSION} from ${_pkg_GIT_REPOSITORY}") + + set (${pkgname}_LOCAL_SOURCE_DIR "${${PROJECT_NAME}_LOCAL_DEPS_ROOT}/${pkgname}") + set (${pkgname}_LOCAL_BUILD_DIR "${${PROJECT_NAME}_LOCAL_DEPS_ROOT}/${pkgname}-build") + set (${pkgname}_LOCAL_INSTALL_DIR "${${PROJECT_NAME}_LOCAL_DEPS_ROOT}/dist") + message (STATUS "Downloading local ${_pkg_GIT_REPOSITORY}") + + # Clone the repo if we don't already have it + find_package (Git REQUIRED) + if (NOT IS_DIRECTORY ${${pkgname}_LOCAL_SOURCE_DIR}) + execute_process(COMMAND ${GIT_EXECUTABLE} clone ${_pkg_GIT_REPOSITORY} + -b ${_pkg_GIT_TAG} --depth 1 + ${${pkgname}_LOCAL_SOURCE_DIR}) + if (NOT IS_DIRECTORY ${${pkgname}_LOCAL_SOURCE_DIR}) + message (FATAL_ERROR "Could not download ${_pkg_GIT_REPOSITORY}") + endif () + endif () + execute_process(COMMAND ${GIT_EXECUTABLE} checkout ${_pkg_GIT_TAG} + WORKING_DIRECTORY ${${pkgname}_LOCAL_SOURCE_DIR}) + + set (_pkg_quiet OUTPUT_QUIET) + + # Configure the package + execute_process (COMMAND + ${CMAKE_COMMAND} + # Put things in our special local build areas + -S ${${pkgname}_LOCAL_SOURCE_DIR} + -B ${${pkgname}_LOCAL_BUILD_DIR} + -DCMAKE_INSTALL_PREFIX=${${pkgname}_LOCAL_INSTALL_DIR} + # Same build type as us + -DCMAKE_BUILD_TYPE=${${PROJECT_NAME}_DEPENDENCY_BUILD_TYPE} + # Shhhh + -DCMAKE_MESSAGE_INDENT=" " + -DCMAKE_MESSAGE_LOG_LEVEL=WARNING + -DCMAKE_COMPILE_WARNING_AS_ERROR=OFF + -DCMAKE_VERBOSE_MAKEFILE=OFF + -DCMAKE_RULE_MESSAGES=OFF + # Build args passed by caller + ${_pkg_CMAKE_ARGS} + ${pkg_quiet} + ) + + # Build the package + execute_process (COMMAND ${CMAKE_COMMAND} + --build ${${pkgname}_LOCAL_BUILD_DIR} + --config ${${PROJECT_NAME}_DEPENDENCY_BUILD_TYPE} + ${pkg_quiet} + ) + + # Install the project, unless instructed not to do so + if (NOT _pkg_NOINSTALL) + execute_process (COMMAND ${CMAKE_COMMAND} + --build ${${pkgname}_LOCAL_BUILD_DIR} + --config ${${PROJECT_NAME}_DEPENDENCY_BUILD_TYPE} + --target install + ${pkg_quiet} + ) + set (${pkgname}_ROOT ${${pkgname}_LOCAL_INSTALL_DIR}) + list (APPEND CMAKE_PREFIX_PATH ${${pkgname}_LOCAL_INSTALL_DIR}) + endif () +endmacro () + + +# Copy libraries from a locally-built dependency into our own install area. +# This is useful for dynamic libraries that we need to be part of our own +# installation. +macro (install_local_dependency_libs pkgname libname) + # We need to include the Imath dynamic libraries in our own install. + # get_target_property(_lib_files Imath::Imath INTERFACE_LINK_LIBRARIES) + set (patterns ${ARGN}) + file (GLOB _lib_files + "${${pkgname}_LOCAL_INSTALL_DIR}/lib/*${libname}*" + "${${pkgname}_LOCAL_INSTALL_DIR}/lib/${${PROJECT_NAME}_DEPENDENCY_BUILD_TYPE}/*${libname}*" + ) + install (FILES ${_lib_files} TYPE LIB) + # message("${pkgname}_LOCAL_INSTALL_DIR = ${${pkgname}_LOCAL_INSTALL_DIR}") + # message(" lib files = ${_lib_files}") + if (WIN32) + # On Windows, check for DLLs, which go in the bin directory + file (GLOB _lib_files + "${${pkgname}_LOCAL_INSTALL_DIR}/bin/*${libname}*.dll" + "${${pkgname}_LOCAL_INSTALL_DIR}/bin/${${PROJECT_NAME}_DEPENDENCY_BUILD_TYPE}/*${libname}*.dll" + ) + # message(" dll files = ${_lib_files}") + install (FILES ${_lib_files} TYPE BIN) + endif () + unset (_lib_files) +endmacro () diff --git a/src/cmake/externalpackages.cmake b/src/cmake/externalpackages.cmake index 2beac76278..c7bb4f1d0c 100644 --- a/src/cmake/externalpackages.cmake +++ b/src/cmake/externalpackages.cmake @@ -20,13 +20,15 @@ message (STATUS "* - To exclude an optional dependency (even if found),") message (STATUS "* -DUSE_Package=OFF or set environment var USE_Package=OFF ") message (STATUS "${ColorReset}") + set (OIIO_LOCAL_DEPS_PATH "${CMAKE_SOURCE_DIR}/ext/dist" CACHE STRING "Local area for dependencies added to CMAKE_PREFIX_PATH") list (APPEND CMAKE_PREFIX_PATH ${OIIO_LOCAL_DEPS_PATH}) -include (ExternalProject) +# Tell CMake that find_package should try to find the highest matching version +# of a package, rather than the first one it finds. +set(CMAKE_FIND_PACKAGE_SORT_ORDER NATURAL) -option (BUILD_MISSING_DEPS "Try to download and build any missing dependencies" OFF) include (FindThreads) @@ -54,27 +56,23 @@ checked_find_package (TIFF REQUIRED # IlmBase & OpenEXR checked_find_package (Imath REQUIRED - VERSION_MIN 3.1 - PRINT IMATH_INCLUDES Imath_VERSION) + VERSION_MIN 3.1 + PRINT IMATH_INCLUDES OPENEXR_INCLUDES Imath_VERSION +) + checked_find_package (OpenEXR REQUIRED - VERSION_MIN 3.1 - PRINT IMATH_INCLUDES OPENEXR_INCLUDES Imath_VERSION) + VERSION_MIN 3.1 + PRINT IMATH_INCLUDES OPENEXR_INCLUDES Imath_VERSION + ) + # Force Imath includes to be before everything else to ensure that we have # the right Imath/OpenEXR version, not some older version in the system -# library. This shouldn't be necessary, except for the common case of people -# building against Imath/OpenEXR 3.x when there is still a system-level -# install version of 2.x. +# library. include_directories(BEFORE ${IMATH_INCLUDES} ${OPENEXR_INCLUDES}) -if (MSVC AND NOT LINKSTATIC) - add_compile_definitions (OPENEXR_DLL) # Is this needed for new versions? -endif () set (OIIO_USING_IMATH 3) -set (OPENIMAGEIO_IMATH_TARGETS - $ - $ ) -set (OPENIMAGEIO_OPENEXR_TARGETS - $ ) -set (OPENIMAGEIO_IMATH_DEPENDENCY_VISIBILITY "PUBLIC" CACHE STRING +set (OPENIMAGEIO_IMATH_TARGETS Imath::Imath) +set (OPENIMAGEIO_OPENEXR_TARGETS OpenEXR::OpenEXR) +set (OPENIMAGEIO_IMATH_DEPENDENCY_VISIBILITY "PRIVATE" CACHE STRING "Should we expose Imath library dependency as PUBLIC or PRIVATE") set (OPENIMAGEIO_CONFIG_DO_NOT_FIND_IMATH OFF CACHE BOOL "Exclude find_dependency(Imath) from the exported OpenImageIOConfig.cmake") @@ -127,8 +125,10 @@ checked_find_package (Freetype DEFINITIONS USE_FREETYPE=1 ) checked_find_package (OpenColorIO + VERSION_MIN 1.1 + VERSION_MAX 2.9 + NO_FP_RANGE_CHECK DEFINITIONS USE_OCIO=1 USE_OPENCOLORIO=1 - # PREFER_CONFIG ) if (OpenColorIO_FOUND) option (OIIO_DISABLE_BUILTIN_OCIO_CONFIGS @@ -136,9 +136,13 @@ if (OpenColorIO_FOUND) if (OIIO_DISABLE_BUILTIN_OCIO_CONFIGS OR "$ENV{OIIO_DISABLE_BUILTIN_OCIO_CONFIGS}") add_compile_definitions(OIIO_DISABLE_BUILTIN_OCIO_CONFIGS) endif () + if (NOT OPENCOLORIO_INCLUDES) + get_target_property(OPENCOLORIO_INCLUDES OpenColorIO::OpenColorIO INTERFACE_INCLUDE_DIRECTORIES) + endif () else () set (OpenColorIO_FOUND 0) endif () +include_directories(BEFORE ${OPENCOLORIO_INCLUDES}) checked_find_package (OpenCV 3.0 DEFINITIONS USE_OPENCV=1) @@ -218,109 +222,23 @@ if (USE_QT AND OPENGL_FOUND) endif () -########################################################################### # Tessil/robin-map +checked_find_package (Robinmap REQUIRED + VERSION_MIN 0.6.2 + BUILD_LOCAL missing + ) -option (BUILD_ROBINMAP_FORCE "Force local download/build of robin-map even if installed" OFF) -option (BUILD_MISSING_ROBINMAP "Local download/build of robin-map if not installed" ON) -set (BUILD_ROBINMAP_VERSION "v0.6.2" CACHE STRING "Preferred Tessil/robin-map version, of downloading/building our own") - -macro (find_or_download_robin_map) - # If we weren't told to force our own download/build of robin-map, look - # for an installed version. Still prefer a copy that seems to be - # locally installed in this tree. - if (NOT BUILD_ROBINMAP_FORCE) - find_package (Robinmap QUIET) - endif () - # If an external copy wasn't found and we requested that missing - # packages be built, or we we are forcing a local copy to be built, then - # download and build it. - # Download the headers from github - if ((BUILD_MISSING_ROBINMAP AND NOT ROBINMAP_FOUND) OR BUILD_ROBINMAP_FORCE) - message (STATUS "Downloading local Tessil/robin-map") - set (ROBINMAP_INSTALL_DIR "${PROJECT_SOURCE_DIR}/ext/robin-map") - set (ROBINMAP_GIT_REPOSITORY "https://github.com/Tessil/robin-map") - if (NOT IS_DIRECTORY ${ROBINMAP_INSTALL_DIR}/include/tsl) - find_package (Git REQUIRED) - execute_process(COMMAND ${GIT_EXECUTABLE} clone ${ROBINMAP_GIT_REPOSITORY} -n ${ROBINMAP_INSTALL_DIR}) - execute_process(COMMAND ${GIT_EXECUTABLE} checkout ${BUILD_ROBINMAP_VERSION} - WORKING_DIRECTORY ${ROBINMAP_INSTALL_DIR}) - if (IS_DIRECTORY ${ROBINMAP_INSTALL_DIR}/include/tsl) - message (STATUS "DOWNLOADED Tessil/robin-map to ${ROBINMAP_INSTALL_DIR}.\n" - "Remove that dir to get rid of it.") - else () - message (FATAL_ERROR "Could not download Tessil/robin-map") - endif () - endif () - set (ROBINMAP_INCLUDE_DIR "${ROBINMAP_INSTALL_DIR}/include") - endif () - checked_find_package (Robinmap REQUIRED) -endmacro() - -find_or_download_robin_map () - - -########################################################################### # fmtlib +option (OIIO_INTERNALIZE_FMT "Copy fmt headers into /include/OpenImageIO/detail/fmt" ON) +checked_find_package (fmt REQUIRED + VERSION_MIN 7.0 + VERSION_MAX 10.99 + BUILD_LOCAL missing + ) +get_target_property(FMT_INCLUDE_DIR fmt::fmt-header-only INTERFACE_INCLUDE_DIRECTORIES) -option (BUILD_FMT_FORCE "Force local download/build of fmt even if installed" OFF) -option (BUILD_MISSING_FMT "Local download/build of fmt if not installed" ON) -option (INTERNALIZE_FMT "Copy fmt headers into /include/OpenImageIO/detail/fmt" ON) -set (BUILD_FMT_VERSION "10.0.0" CACHE STRING "Preferred fmtlib/fmt version, when downloading/building our own") - -macro (find_or_download_fmt) - # If we weren't told to force our own download/build of fmt, look - # for an installed version. Still prefer a copy that seems to be - # locally installed in this tree. - if (NOT BUILD_FMT_FORCE) - find_package (fmt QUIET) - endif () - # If an external copy wasn't found and we requested that missing - # packages be built, or we we are forcing a local copy to be built, then - # download and build it. - if ((BUILD_MISSING_FMT AND NOT fmt_FOUND) OR BUILD_FMT_FORCE) - message (STATUS "Downloading local fmtlib/fmt") - set (FMT_INSTALL_DIR "${PROJECT_SOURCE_DIR}/ext/fmt") - set (FMT_GIT_REPOSITORY "https://github.com/fmtlib/fmt") - if (NOT IS_DIRECTORY ${FMT_INSTALL_DIR}/include/fmt) - find_package (Git REQUIRED) - execute_process(COMMAND ${GIT_EXECUTABLE} clone ${FMT_GIT_REPOSITORY} -n ${FMT_INSTALL_DIR}) - execute_process(COMMAND ${GIT_EXECUTABLE} checkout ${BUILD_FMT_VERSION} - WORKING_DIRECTORY ${FMT_INSTALL_DIR}) - if (IS_DIRECTORY ${FMT_INSTALL_DIR}/include/fmt) - message (STATUS "DOWNLOADED fmtlib/fmt to ${FMT_INSTALL_DIR}.\n" - "Remove that dir to get rid of it.") - else () - message (FATAL_ERROR "Could not download fmtlib/fmt") - endif () - endif () - set (FMT_INCLUDE_DIR "${FMT_INSTALL_DIR}/include") - set (OIIO_USING_FMT_LOCAL TRUE) - if (EXISTS "${FMT_INCLUDE_DIR}/fmt/base.h") - file (STRINGS "${FMT_INCLUDE_DIR}/fmt/base.h" TMP REGEX "^#define FMT_VERSION .*$") - else () - file (STRINGS "${FMT_INCLUDE_DIR}/fmt/core.h" TMP REGEX "^#define FMT_VERSION .*$") - endif () - string (REGEX MATCHALL "[0-9]+" FMT_VERSION_NUMERIC ${TMP}) - math(EXPR FMT_VERSION_PATCH "${FMT_VERSION_NUMERIC} % 100") - math(EXPR FMT_VERSION_MINOR "(${FMT_VERSION_NUMERIC} / 100) % 100") - math(EXPR FMT_VERSION_MAJOR "${FMT_VERSION_NUMERIC} / 10000") - set (fmt_VERSION "${FMT_VERSION_MAJOR}.${FMT_VERSION_MINOR}.${FMT_VERSION_PATCH}") - list (APPEND CFP_ALL_BUILD_DEPS_FOUND "${pkgname} ${${pkgname}_VERSION}") - else () - get_target_property(FMT_INCLUDE_DIR fmt::fmt-header-only INTERFACE_INCLUDE_DIRECTORIES) - set (OIIO_USING_FMT_LOCAL FALSE) - checked_find_package (fmt REQUIRED - VERSION_MIN 7.0) - endif () -endmacro() - -find_or_download_fmt() -if (fmt_VERSION VERSION_EQUAL 9.1.0 - AND GCC_VERSION VERSION_GREATER 0.0 AND NOT GCC_VERSION VERSION_GREATER 7.2) - message (WARNING "${ColorRed}fmt 9.1 is known to not work with gcc <= 7.2${ColorReset}") -endif () +########################################################################### list (SORT CFP_ALL_BUILD_DEPS_FOUND COMPARE STRING CASE INSENSITIVE) message (STATUS "All build dependencies: ${CFP_ALL_BUILD_DEPS_FOUND}") diff --git a/src/cmake/set_utils.cmake b/src/cmake/set_utils.cmake index 528c0e550c..42a29cdc07 100644 --- a/src/cmake/set_utils.cmake +++ b/src/cmake/set_utils.cmake @@ -11,6 +11,16 @@ macro (set_if_not var value) endmacro () +# Set a variable to a `replacement`, replacing its previous value, but only if +# `replacement` is non-empty. +macro (set_replace_if_nonempty var replacement) + if (NOT "${replacement}" STREQUAL "") + set (${var} ${replacement}) + endif () +endmacro () + + + # Set a cmake variable `var` from an environment variable, if it is not # already defined (or if the FORCE flag is used). By default, the env var is # the same name as `var`, but you can specify a different env var name with diff --git a/src/include/CMakeLists.txt b/src/include/CMakeLists.txt index 7bec09739e..001699c3bd 100644 --- a/src/include/CMakeLists.txt +++ b/src/include/CMakeLists.txt @@ -64,7 +64,7 @@ install (FILES ${detail_headers} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/${PROJECT_NAME}/detail COMPONENT developer) -if (INTERNALIZE_FMT OR OIIO_USING_FMT_LOCAL) +if (OIIO_INTERNALIZE_FMT OR fmt_LOCAL_BUILD) set (fmt_headers_base_names) foreach (header_name core.h format-inl.h format.h ostream.h printf.h std.h base.h chrono.h) diff --git a/src/libOpenImageIO/CMakeLists.txt b/src/libOpenImageIO/CMakeLists.txt index bcf5b0787f..61b4865f4a 100644 --- a/src/libOpenImageIO/CMakeLists.txt +++ b/src/libOpenImageIO/CMakeLists.txt @@ -136,6 +136,7 @@ target_include_directories (OpenImageIO ${OPENEXR_INCLUDES} PRIVATE ${ROBINMAP_INCLUDES} + ${OPENIMAGEIO_OPENCOLORIO_INCLUDES} ) target_include_directories (OpenImageIO SYSTEM PUBLIC ${OpenCV_INCLUDES}) @@ -208,6 +209,7 @@ if (CMAKE_UNITY_BUILD) endif () endforeach () set_property (SOURCE ${iba_sources} APPEND PROPERTY SKIP_UNITY_BUILD_INCLUSION TRUE) + set_property (SOURCE ../openvdb.imageio/openvdbinput.cpp APPEND PROPERTY SKIP_UNITY_BUILD_INCLUSION TRUE) endif () set_target_properties(OpenImageIO diff --git a/src/libutil/CMakeLists.txt b/src/libutil/CMakeLists.txt index 4277ce4699..1d89395ff7 100644 --- a/src/libutil/CMakeLists.txt +++ b/src/libutil/CMakeLists.txt @@ -68,7 +68,7 @@ function (setup_oiio_util_library targetname) PRIVATE stdc++_libbacktrace) endif () - if (INTERNALIZE_FMT OR OIIO_USING_FMT_LOCAL) + if (OIIO_INTERNALIZE_FMT OR fmt_LOCAL_BUILD) add_dependencies(${targetname} fmt_internal_target) else () target_link_libraries (${targetname}