From 3002a9b820f000d6883e458aa73fc7e81df34fed Mon Sep 17 00:00:00 2001 From: Larry Gritz Date: Fri, 24 May 2024 18:48:20 -0700 Subject: [PATCH 1/6] build: new system to build dependencies locally if needed High level TLDR: * If checked_find_package doesn't find a dependency (or it is not an acceptable version), it looks for `src/cmake/build_.cmake` and if that exists, includes it. It can do anything, but is expected to somehow provide the dependency so that a second find_package will find it and then proceed as if it were a system install. * I've implemented these scripts so far for Imath, OpenEXR, OpenColorIO, fmt, and robin-map, that download, build, install the package in OIIO's build area. More to come later? * This is really simple with a new build_dependency_with_cmake macro, much simpler than ExternalProject_Add, as I've seen it used elsewhere. * Just look at any of the new build_blah.cmake files to see how simple it is for each new dependency we set up this way. * By default, pre-installed packages it can find always take precedent over building locally. So if you have all the dependencies already installed, none of this should behave any differntly than before. But there are variables that let you override on a package by package basis, giving the option of never building locally, building locally if the package is missing, or forcing a local build to always happen. ---- Various details: A bunch of cmake things (including checked_find_package) have been moved into a new file, dependency_utils.cmake. build_Imath.cmake, build_OpenColorIO.cmake, build_OpenEXR.cmake, build_Robinmap.cmake, and build_fmt.cmake implement local builds of those packages. They're very simple, and lean heavily on common infrastructure of build_dependency_with_cmake, which also can be found in dependency_utils.cmake. Robinmap and fmt are extra simple because we use them as header-only libraries. For Imath and OpenEXR, I build them as static libraries, so they will be incorporated into libOpenImageIO (and/or _Util) libraries to be totally internal to them, there should be no symbols exposed outside our libraries. This should mean that the resulting libOpenImageIO should be perfectly safe to link in an application that also links against OpenEXR, Imath, or OpenColorIO, even different versions thereof, without any interference. Note that none of those packages are used in our public APIs, only internally. OpenColorIO was a little trickier. It builds its own dependencies as static libraries that are internalized, but OCIO itself is a dynamic library. So we end up having to make it part of our install, but I use OCIO's build system to make a custom symbol namespace and a custom library name, so it still should not interfere with any other OCIO linked into the application. We'll see how it goes for furture dependencies we want to add. The header only, static libraries incorporated and hidden, and dynamic library but renamed and with custom namespace, are all techniques that work well. I'm not sure I'd advocate doing local builds of any dependency that we can't incorporate in one of these ways, but I guess we'll cross that bridge when we get to it. checked_find_package() has moved to dependency_utils.cmake, and has been enhanced to take several new options, and also so that if the enclosed find_package() fails and there is a src/cmake/build_PKG.cmake, it will run it to build the dependency itself in the build area. If that build_PKG sets a variable called PKG_REFIND, it will try find_package again to find the one just built. build_dependency_with_cmake() is given a git repo and tag, and basically clones the repo, checks out the tag, configures, builds, and installs it (all in our own build area). --- Signed-off-by: Larry Gritz --- .github/workflows/ci.yml | 7 +- CMakeLists.txt | 2 +- src/build-scripts/build_libtiff.bash | 1 + src/build-scripts/build_openexr.bash | 1 + src/build-scripts/ci-test.bash | 2 +- src/build-scripts/gh-win-installdeps.bash | 22 +- src/cmake/Config.cmake.in | 15 +- src/cmake/build_Imath.cmake | 43 +++ src/cmake/build_OpenColorIO.cmake | 51 +++ src/cmake/build_OpenEXR.cmake | 49 +++ src/cmake/build_Robinmap.cmake | 24 ++ src/cmake/build_fmt.cmake | 29 ++ src/cmake/checked_find_package.cmake | 214 ----------- src/cmake/colors.cmake | 2 + src/cmake/compiler.cmake | 4 +- src/cmake/dependency_utils.cmake | 427 ++++++++++++++++++++++ src/cmake/externalpackages.cmake | 162 +++----- src/include/CMakeLists.txt | 2 +- src/libOpenImageIO/CMakeLists.txt | 2 + src/libutil/CMakeLists.txt | 2 +- 20 files changed, 699 insertions(+), 362 deletions(-) create mode 100644 src/cmake/build_Imath.cmake create mode 100644 src/cmake/build_OpenColorIO.cmake create mode 100644 src/cmake/build_OpenEXR.cmake create mode 100644 src/cmake/build_Robinmap.cmake create mode 100644 src/cmake/build_fmt.cmake delete mode 100644 src/cmake/checked_find_package.cmake create mode 100644 src/cmake/dependency_utils.cmake diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 16a9dc3fd4..b4bcdd78a6 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,14 +528,14 @@ 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 runs-on: ${{ matrix.runner }} @@ -570,6 +570,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..4dd8e44f8e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -49,7 +49,7 @@ list (APPEND CMAKE_MODULE_PATH include (colors) include (set_utils) include (check_is_enabled) -include (checked_find_package) +include (dependency_utils) include (fancy_add_executable) # If the user wants to use Conan to build dependencies, they will have done 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-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..a374e532d0 --- /dev/null +++ b/src/cmake/build_Imath.cmake @@ -0,0 +1,43 @@ +# 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) + +install_local_dependency_libs (Imath Imath) diff --git a/src/cmake/build_OpenColorIO.cmake b/src/cmake/build_OpenColorIO.cmake new file mode 100644 index 0000000000..d003a43880 --- /dev/null +++ b/src/cmake/build_OpenColorIO.cmake @@ -0,0 +1,51 @@ +# 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) + +install_local_dependency_libs (OpenColorIO OpenColorIO) diff --git a/src/cmake/build_OpenEXR.cmake b/src/cmake/build_OpenEXR.cmake new file mode 100644 index 0000000000..23a4ba6b2a --- /dev/null +++ b/src/cmake/build_OpenEXR.cmake @@ -0,0 +1,49 @@ +# 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) + +install_local_dependency_libs (OpenEXR OpenEXR) +install_local_dependency_libs (OpenEXR IlmThread) +install_local_dependency_libs (OpenEXR Iex) 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..d8bd13cce4 100644 --- a/src/cmake/colors.cmake +++ b/src/cmake/colors.cmake @@ -9,4 +9,6 @@ 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 (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..ce7e4a4daf --- /dev/null +++ b/src/cmake/dependency_utils.cmake @@ -0,0 +1,427 @@ +# 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) + +# 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 "") + +# 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: 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 "${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 (required) + message (FATAL_ERROR "${ColorRed}${pkgname} is required, aborting.${ColorReset}") + 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". +# +# 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" + # 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 () + if (_pkg_BUILD_LOCAL AND NOT EXISTS "${PROJECT_SOURCE_DIR}/src/cmake/build_${pkgname}.cmake") + 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) + 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 () + # + # 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}) + 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 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 + 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}_INCLUDE) + unset (${pkgname}_INCLUDES) + unset (${pkgname}_LIBRARY) + unset (${pkgname}_LIBRARIES) + unset (${pkgname_upper}_FOUND) + unset (${pkgname_upper}_VERSION) + unset (${pkgname_upper}_INCLUDE) + unset (${pkgname_upper}_INCLUDES) + unset (${pkgname_upper}_LIBRARY) + unset (${pkgname_upper}_LIBRARIES) + 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 "${PROJECT_SOURCE_DIR}/src/cmake/build_${pkgname}.cmake") + message (STATUS "${ColorMagenta}Building package ${pkgname} ${${pkgname}_VERSION} locally${ColorReset}") + list(APPEND CMAKE_MESSAGE_INDENT " ") + include(${PROJECT_SOURCE_DIR}/src/cmake/build_${pkgname}.cmake) + list(POP_BACK CMAKE_MESSAGE_INDENT) + set (${pkgname}_FOUND TRUE) + set (${pkgname}_LOCAL_BUILD TRUE) + 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. + if (${pkgname}_REFIND) + message (STATUS "Refinding ${pkgname}") + find_package (${pkgname} ${_${pkgname}_version_range} ${_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..9390547f70 100644 --- a/src/cmake/externalpackages.cmake +++ b/src/cmake/externalpackages.cmake @@ -20,13 +20,22 @@ 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_cache (${PROJECT_NAME}_BUILD_MISSING_DEPS "all" + "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 (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}) +list (APPEND CMAKE_PREFIX_PATH ${OIIO_LOCAL_DEPS_ROOT}) -include (ExternalProject) +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) +# set (${PROJECT_NAME}_LOCAL_DEPS_BUILD "${${PROJECT_NAME}_LOCAL_DEPS_ROOT}/build") +# set (${PROJECT_NAME}_LOCAL_DEPS_INSTALL "${${PROJECT_NAME}_LOCAL_DEPS_ROOT}/install") -option (BUILD_MISSING_DEPS "Try to download and build any missing dependencies" OFF) include (FindThreads) @@ -54,27 +63,25 @@ 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 + BUILD_LOCAL missing + 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 + BUILD_LOCAL missing + 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,6 +134,9 @@ checked_find_package (Freetype DEFINITIONS USE_FREETYPE=1 ) checked_find_package (OpenColorIO + VERSION_MIN 2.1 + VERSION_MAX 3.0 + BUILD_LOCAL missing DEFINITIONS USE_OCIO=1 USE_OPENCOLORIO=1 # PREFER_CONFIG ) @@ -136,9 +146,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 +232,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/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} From a0ce5633c1abbea37287b4cbf4812e035c2324b4 Mon Sep 17 00:00:00 2001 From: Larry Gritz Date: Sun, 2 Jun 2024 23:22:57 -0700 Subject: [PATCH 2/6] Don't change OCIO minimum as part of this PR, keep it a 1.1 min Signed-off-by: Larry Gritz --- src/cmake/externalpackages.cmake | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/cmake/externalpackages.cmake b/src/cmake/externalpackages.cmake index 9390547f70..1a1605b7bb 100644 --- a/src/cmake/externalpackages.cmake +++ b/src/cmake/externalpackages.cmake @@ -134,8 +134,8 @@ checked_find_package (Freetype DEFINITIONS USE_FREETYPE=1 ) checked_find_package (OpenColorIO - VERSION_MIN 2.1 - VERSION_MAX 3.0 + VERSION_MIN 1.1 + VERSION_MAX 2.9 BUILD_LOCAL missing DEFINITIONS USE_OCIO=1 USE_OPENCOLORIO=1 # PREFER_CONFIG From c1da6b6aafe1bcad36bb5c907c84f2f137adfac5 Mon Sep 17 00:00:00 2001 From: Larry Gritz Date: Mon, 3 Jun 2024 23:08:03 -0700 Subject: [PATCH 3/6] Amendment: Make the default be to NOT build any local packages unless explicitly instructed. (Except for robinmap and fmt, which we had always automatically downloaded if missing; we continue to do that.) But we also add a nice report after config that details which missing optinal dependencies we could have built and how to arrange it. Signed-off-by: Larry Gritz --- CMakeLists.txt | 2 + src/build-scripts/ci-startup.bash | 4 +- src/cmake/colors.cmake | 3 + src/cmake/dependency_utils.cmake | 99 +++++++++++++++++++++++++++---- src/cmake/externalpackages.cmake | 17 +----- 5 files changed, 99 insertions(+), 26 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 4dd8e44f8e..21ec701473 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -320,3 +320,5 @@ endif () if (PROJECT_IS_TOP_LEVEL) include (packaging) endif () + +print_package_notfound_report () diff --git a/src/build-scripts/ci-startup.bash b/src/build-scripts/ci-startup.bash index 80ddedda04..c5bc376da8 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, build 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/cmake/colors.cmake b/src/cmake/colors.cmake index d8bd13cce4..a9f747bd33 100644 --- a/src/cmake/colors.cmake +++ b/src/cmake/colors.cmake @@ -11,4 +11,7 @@ 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/dependency_utils.cmake b/src/cmake/dependency_utils.cmake index ce7e4a4daf..7e715766a5 100644 --- a/src/cmake/dependency_utils.cmake +++ b/src/cmake/dependency_utils.cmake @@ -10,6 +10,15 @@ set_cache (${PROJECT_NAME}_OPTIONAL_DEPS "" 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} @@ -31,6 +40,16 @@ 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 "") + + + # Utility function to list the names and values of all variables matching # the pattern (case-insensitive) function (dump_matching_variables pattern) @@ -46,28 +65,65 @@ function (dump_matching_variables pattern) 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) + message (STATUS " ${_pkg}") + 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) + message (STATUS " ${_pkg}") + 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 "${ColorRed} ${pkgname}_ROOT was: ${${pkgname}_ROOT} ${ColorReset}") + message (STATUS " ${pkgname}_ROOT was: ${${pkgname}_ROOT}") elseif ($ENV{${pkgname}_ROOT}) - message (STATUS "${ColorRed} ENV ${pkgname}_ROOT was: ${${pkgname}_ROOT} ${ColorReset}") + message (STATUS " ENV ${pkgname}_ROOT was: ${${pkgname}_ROOT}") else () - message (STATUS "${ColorRed} Try setting ${pkgname}_ROOT ? ${ColorReset}") + message (STATUS " Try setting ${pkgname}_ROOT ?") 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}") + 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 "${ColorRed} Maybe this will help: src/build-scripts/build_${pkgname_upper}.bash ${ColorReset}") + 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 "${ColorRed} Maybe this will help: src/build-scripts/build_${pkgname_lower}.bash ${ColorReset}") + 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 "${ColorRed} Maybe this will help: src/build-scripts/build_lib${pkgname_lower}.bash ${ColorReset}") + message (STATUS " Maybe this will help: src/build-scripts/build_lib${pkgname_lower}.bash") endif () if (required) - message (FATAL_ERROR "${ColorRed}${pkgname} is required, aborting.${ColorReset}") + print_package_notfound_report() + message (FATAL_ERROR "${pkgname} is required, aborting.") endif () endfunction () @@ -167,7 +223,11 @@ macro (checked_find_package pkgname) OR ${PROJECT_NAME}_BUILD_MISSING_DEPS STREQUAL "all") set_if_not (_pkg_BUILD_LOCAL "missing") endif () - if (_pkg_BUILD_LOCAL AND NOT EXISTS "${PROJECT_SOURCE_DIR}/src/cmake/build_${pkgname}.cmake") + 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) @@ -215,6 +275,19 @@ macro (checked_find_package pkgname) 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) @@ -223,6 +296,10 @@ macro (checked_find_package pkgname) 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}") + 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) unset (${pkgname}_VERSION) unset (${pkgname}_INCLUDE) @@ -242,10 +319,10 @@ macro (checked_find_package pkgname) # 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 "${PROJECT_SOURCE_DIR}/src/cmake/build_${pkgname}.cmake") + AND EXISTS "${${pkgname}_local_build_script}") message (STATUS "${ColorMagenta}Building package ${pkgname} ${${pkgname}_VERSION} locally${ColorReset}") list(APPEND CMAKE_MESSAGE_INDENT " ") - include(${PROJECT_SOURCE_DIR}/src/cmake/build_${pkgname}.cmake) + include("${${pkgname}_local_build_script}") list(POP_BACK CMAKE_MESSAGE_INDENT) set (${pkgname}_FOUND TRUE) set (${pkgname}_LOCAL_BUILD TRUE) diff --git a/src/cmake/externalpackages.cmake b/src/cmake/externalpackages.cmake index 1a1605b7bb..2f3e336389 100644 --- a/src/cmake/externalpackages.cmake +++ b/src/cmake/externalpackages.cmake @@ -21,20 +21,10 @@ message (STATUS "* -DUSE_Package=OFF or set environment var USE_Package=OFF message (STATUS "${ColorReset}") -set_cache (${PROJECT_NAME}_BUILD_MISSING_DEPS "all" - "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 (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_ROOT}) -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) -# set (${PROJECT_NAME}_LOCAL_DEPS_BUILD "${${PROJECT_NAME}_LOCAL_DEPS_ROOT}/build") -# set (${PROJECT_NAME}_LOCAL_DEPS_INSTALL "${${PROJECT_NAME}_LOCAL_DEPS_ROOT}/install") include (FindThreads) @@ -64,13 +54,13 @@ checked_find_package (TIFF REQUIRED # IlmBase & OpenEXR checked_find_package (Imath REQUIRED VERSION_MIN 3.1 - BUILD_LOCAL missing + # BUILD_LOCAL missing PRINT IMATH_INCLUDES OPENEXR_INCLUDES Imath_VERSION ) checked_find_package (OpenEXR REQUIRED VERSION_MIN 3.1 - BUILD_LOCAL missing + # BUILD_LOCAL missing PRINT IMATH_INCLUDES OPENEXR_INCLUDES Imath_VERSION ) @@ -136,9 +126,8 @@ checked_find_package (Freetype checked_find_package (OpenColorIO VERSION_MIN 1.1 VERSION_MAX 2.9 - BUILD_LOCAL missing + # BUILD_LOCAL missing DEFINITIONS USE_OCIO=1 USE_OPENCOLORIO=1 - # PREFER_CONFIG ) if (OpenColorIO_FOUND) option (OIIO_DISABLE_BUILTIN_OCIO_CONFIGS From 98db75bf2b399f3cdd4a5b7dda593bc9e42408c3 Mon Sep 17 00:00:00 2001 From: Larry Gritz Date: Wed, 5 Jun 2024 15:19:31 -0700 Subject: [PATCH 4/6] Fixes Signed-off-by: Larry Gritz --- .github/workflows/ci.yml | 1 + CMakeLists.txt | 4 +- src/build-scripts/ci-startup.bash | 2 +- src/cmake/build_Imath.cmake | 2 + src/cmake/build_OpenColorIO.cmake | 3 +- src/cmake/build_OpenEXR.cmake | 2 + src/cmake/dependency_utils.cmake | 120 +++++++++++++++++++++++------- src/cmake/externalpackages.cmake | 8 +- src/cmake/set_utils.cmake | 10 +++ 9 files changed, 120 insertions(+), 32 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b4bcdd78a6..66a59375e4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -538,6 +538,7 @@ jobs: # 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}} diff --git a/CMakeLists.txt b/CMakeLists.txt index 21ec701473..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 (dependency_utils) 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) diff --git a/src/build-scripts/ci-startup.bash b/src/build-scripts/ci-startup.bash index c5bc376da8..a46fd517db 100755 --- a/src/build-scripts/ci-startup.bash +++ b/src/build-scripts/ci-startup.bash @@ -47,7 +47,7 @@ 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, build missing dependencies automatically +# For CI, default to building missing dependencies automatically export OpenImageIO_BUILD_MISSING_DEPS=${OpenImageIO_BUILD_MISSING_DEPS:=all} # Sonar diff --git a/src/cmake/build_Imath.cmake b/src/cmake/build_Imath.cmake index a374e532d0..ffc7303571 100644 --- a/src/cmake/build_Imath.cmake +++ b/src/cmake/build_Imath.cmake @@ -39,5 +39,7 @@ build_dependency_with_cmake(Imath # 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}) install_local_dependency_libs (Imath Imath) diff --git a/src/cmake/build_OpenColorIO.cmake b/src/cmake/build_OpenColorIO.cmake index d003a43880..cbcdb8cca6 100644 --- a/src/cmake/build_OpenColorIO.cmake +++ b/src/cmake/build_OpenColorIO.cmake @@ -42,10 +42,11 @@ build_dependency_with_cmake(OpenColorIO # 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_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) install_local_dependency_libs (OpenColorIO OpenColorIO) diff --git a/src/cmake/build_OpenEXR.cmake b/src/cmake/build_OpenEXR.cmake index 23a4ba6b2a..dee62a68fa 100644 --- a/src/cmake/build_OpenEXR.cmake +++ b/src/cmake/build_OpenEXR.cmake @@ -43,6 +43,8 @@ build_dependency_with_cmake(OpenEXR # 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}) install_local_dependency_libs (OpenEXR OpenEXR) install_local_dependency_libs (OpenEXR IlmThread) diff --git a/src/cmake/dependency_utils.cmake b/src/cmake/dependency_utils.cmake index 7e715766a5..bbb4174bea 100644 --- a/src/cmake/dependency_utils.cmake +++ b/src/cmake/dependency_utils.cmake @@ -48,6 +48,9 @@ set (CFP_LOCALLY_BUILDABLE_DEPS_NOTFOUND "") 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 @@ -75,13 +78,21 @@ function (print_package_notfound_report) 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) - message (STATUS " ${_pkg}") + 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) - message (STATUS " ${_pkg}") + 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) @@ -128,6 +139,47 @@ function (handle_package_notfound pkgname required) 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 @@ -182,6 +234,21 @@ endfunction () # 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, @@ -192,7 +259,7 @@ macro (checked_find_package pkgname) # cmake_parse_arguments(_pkg # prefix # noValueKeywords: - "REQUIRED;CONFIG;PREFER_CONFIG;DEBUG;NO_RECORD_NOTFOUND" + "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: @@ -249,13 +316,19 @@ macro (checked_find_package pkgname) endif () set (_config_status "") unset (_${pkgname}_version_range) - if (_pkg_BUILD_LOCAL) + 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 # @@ -268,6 +341,9 @@ macro (checked_find_package pkgname) # 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 () @@ -290,28 +366,16 @@ macro (checked_find_package pkgname) 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 - 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)) + 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 () - unset (${pkgname}_FOUND) - unset (${pkgname}_VERSION) - unset (${pkgname}_INCLUDE) - unset (${pkgname}_INCLUDES) - unset (${pkgname}_LIBRARY) - unset (${pkgname}_LIBRARIES) - unset (${pkgname_upper}_FOUND) - unset (${pkgname_upper}_VERSION) - unset (${pkgname_upper}_INCLUDE) - unset (${pkgname_upper}_INCLUDES) - unset (${pkgname_upper}_LIBRARY) - unset (${pkgname_upper}_LIBRARIES) endif () endif () # If we haven't found the package yet and are allowed to build a local @@ -324,15 +388,21 @@ macro (checked_find_package pkgname) list(APPEND CMAKE_MESSAGE_INDENT " ") include("${${pkgname}_local_build_script}") list(POP_BACK CMAKE_MESSAGE_INDENT) - set (${pkgname}_FOUND TRUE) + # 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. + # 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}") - find_package (${pkgname} ${_${pkgname}_version_range} ${_pkg_UNPARSED_ARGUMENTS} ${${pkgname}_REFIND_ARGS}) + 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 diff --git a/src/cmake/externalpackages.cmake b/src/cmake/externalpackages.cmake index 2f3e336389..f3875148a1 100644 --- a/src/cmake/externalpackages.cmake +++ b/src/cmake/externalpackages.cmake @@ -23,8 +23,11 @@ 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_ROOT}) +list (APPEND CMAKE_PREFIX_PATH ${OIIO_LOCAL_DEPS_PATH}) +# 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) include (FindThreads) @@ -54,13 +57,11 @@ checked_find_package (TIFF REQUIRED # IlmBase & OpenEXR checked_find_package (Imath REQUIRED VERSION_MIN 3.1 - # BUILD_LOCAL missing PRINT IMATH_INCLUDES OPENEXR_INCLUDES Imath_VERSION ) checked_find_package (OpenEXR REQUIRED VERSION_MIN 3.1 - # BUILD_LOCAL missing PRINT IMATH_INCLUDES OPENEXR_INCLUDES Imath_VERSION ) @@ -126,7 +127,6 @@ checked_find_package (Freetype checked_find_package (OpenColorIO VERSION_MIN 1.1 VERSION_MAX 2.9 - # BUILD_LOCAL missing DEFINITIONS USE_OCIO=1 USE_OPENCOLORIO=1 ) if (OpenColorIO_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 From 74de43c394abd372bcefd5699907af00bd3e9a97 Mon Sep 17 00:00:00 2001 From: Larry Gritz Date: Sat, 8 Jun 2024 16:03:22 -0700 Subject: [PATCH 5/6] fix Signed-off-by: Larry Gritz --- src/cmake/externalpackages.cmake | 1 + 1 file changed, 1 insertion(+) diff --git a/src/cmake/externalpackages.cmake b/src/cmake/externalpackages.cmake index f3875148a1..c7bb4f1d0c 100644 --- a/src/cmake/externalpackages.cmake +++ b/src/cmake/externalpackages.cmake @@ -127,6 +127,7 @@ checked_find_package (Freetype checked_find_package (OpenColorIO VERSION_MIN 1.1 VERSION_MAX 2.9 + NO_FP_RANGE_CHECK DEFINITIONS USE_OCIO=1 USE_OPENCOLORIO=1 ) if (OpenColorIO_FOUND) From c4b74ba8877a38053bb947c405bdaf83a6647e86 Mon Sep 17 00:00:00 2001 From: Larry Gritz Date: Sat, 8 Jun 2024 19:28:40 -0700 Subject: [PATCH 6/6] Only install dynamic dependency libs, not static libs Signed-off-by: Larry Gritz --- src/cmake/build_Imath.cmake | 4 +++- src/cmake/build_OpenColorIO.cmake | 4 +++- src/cmake/build_OpenEXR.cmake | 8 +++++--- 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/src/cmake/build_Imath.cmake b/src/cmake/build_Imath.cmake index ffc7303571..25b7961111 100644 --- a/src/cmake/build_Imath.cmake +++ b/src/cmake/build_Imath.cmake @@ -42,4 +42,6 @@ set (Imath_REFIND TRUE) set (Imath_REFIND_ARGS CONFIG) set (Imath_REFIND_VERSION ${Imath_BUILD_VERSION}) -install_local_dependency_libs (Imath Imath) +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 index cbcdb8cca6..5905eb597a 100644 --- a/src/cmake/build_OpenColorIO.cmake +++ b/src/cmake/build_OpenColorIO.cmake @@ -49,4 +49,6 @@ set (OpenColorIO_DIR ${OpenColorIO_LOCAL_INSTALL_DIR}) set (OpenColorIO_REFIND TRUE) set (OpenColorIO_REFIND_ARGS CONFIG) -install_local_dependency_libs (OpenColorIO OpenColorIO) +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 index dee62a68fa..849716fb8d 100644 --- a/src/cmake/build_OpenEXR.cmake +++ b/src/cmake/build_OpenEXR.cmake @@ -46,6 +46,8 @@ set (OpenEXR_REFIND TRUE) set (OpenEXR_REFIND_ARGS CONFIG) set (OpenEXR_REFIND_VERSION ${OpenEXR_BUILD_VERSION}) -install_local_dependency_libs (OpenEXR OpenEXR) -install_local_dependency_libs (OpenEXR IlmThread) -install_local_dependency_libs (OpenEXR Iex) +if (OpenEXR_BUILD_SHARED_LIBS) + install_local_dependency_libs (OpenEXR OpenEXR) + install_local_dependency_libs (OpenEXR IlmThread) + install_local_dependency_libs (OpenEXR Iex) +endif ()