diff --git a/cmake/defaults/CYCOMMON.cmake b/cmake/defaults/CYCOMMON.cmake index 472db3ac2..53b45f90f 100644 --- a/cmake/defaults/CYCOMMON.cmake +++ b/cmake/defaults/CYCOMMON.cmake @@ -16,10 +16,13 @@ SET(RV_DEPS_ATOMIC_OPS_DOWNLOAD_HASH # dav1d https://github.com/videolan/dav1d SET(RV_DEPS_DAV1D_VERSION - "1.4.3" + "1.5.3" ) SET(RV_DEPS_DAV1D_DOWNLOAD_HASH - "2c62106fda87a69122dc8709243a34e8" + "6a195752588586acf13349a1cceedab8" +) +SET(RV_DEPS_DAV1D_VERSION_LIB + "7" ) # doctest https://github.com/doctest/doctest @@ -205,10 +208,10 @@ SET(RV_DEPS_OTIO_VERSION # pcre2 https://github.com/PCRE2Project/pcre2 SET(RV_DEPS_PCRE2_VERSION - "10.43" + "10.47" ) SET(RV_DEPS_PCRE2_DOWNLOAD_HASH - "e4c3f2a24eb5c15bec8360e50b3f0137" + "9a77e2cdc4410addf9a77363a89fe858" ) # png https://github.com/glennrp/libpng diff --git a/cmake/defaults/rv_options.cmake b/cmake/defaults/rv_options.cmake index db9483325..806d0094a 100644 --- a/cmake/defaults/rv_options.cmake +++ b/cmake/defaults/rv_options.cmake @@ -116,3 +116,29 @@ SET_PROPERTY( CACHE RV_FFMPEG PROPERTY STRINGS ${_RV_FFMPEG} ) + +# +# Dependency resolution option +# +# When ON, try find_package() for each dependency before building from source. When OFF (default), always build dependencies from source (current behavior). +# +# Per-dependency override: set RV_DEPS__FORCE_BUILD=ON to force building a specific dependency from source even when RV_DEPS_PREFER_INSTALLED=ON. +# +OPTION(RV_DEPS_PREFER_INSTALLED "Try find_package() for dependencies before building from source" OFF) + +# +# Version matching mode for dependency resolution. +# +# Controls how RV_FIND_DEPENDENCY matches versions when RV_DEPS_PREFER_INSTALLED=ON. EXACT — require the exact version specified in CY*.cmake (default, +# recommended) MINIMUM — accept the specified version or newer (standard find_package behavior) +# +# Per-dependency override: set RV_DEPS__VERSION_MATCH=EXACT or MINIMUM to override the global setting for a specific dependency. +# +SET(RV_DEPS_VERSION_MATCH + "EXACT" + CACHE STRING "Version matching mode for find_package: EXACT or MINIMUM" +) +SET_PROPERTY( + CACHE RV_DEPS_VERSION_MATCH + PROPERTY STRINGS EXACT MINIMUM +) diff --git a/cmake/dependencies/CMakeLists.txt b/cmake/dependencies/CMakeLists.txt index 02c78e304..c08f98fe9 100644 --- a/cmake/dependencies/CMakeLists.txt +++ b/cmake/dependencies/CMakeLists.txt @@ -8,6 +8,12 @@ INCLUDE(rv_create_std_deps_vars) INCLUDE(rv_make_std_lib_name) INCLUDE(rv_stage_dependency_libs) INCLUDE(rv_add_imported_library) +INCLUDE(rv_find_dependency) + +# All imported targets created by find_package() are automatically GLOBAL, so subdirectories can reference them without manual promotion. +SET(CMAKE_FIND_PACKAGE_TARGETS_GLOBAL + TRUE +) INCLUDE(ProcessorCount) PROCESSORCOUNT(_cpu_count) diff --git a/cmake/dependencies/atomic_ops.cmake b/cmake/dependencies/atomic_ops.cmake index f509ce273..e175512bc 100644 --- a/cmake/dependencies/atomic_ops.cmake +++ b/cmake/dependencies/atomic_ops.cmake @@ -29,35 +29,17 @@ SET(_lib_dir ${_install_dir}/lib ) -IF(RV_TARGET_WINDOWS) - SET(_atomic_ops_lib_name - libatomic_ops.a - ) -ELSE() - SET(_atomic_ops_lib_name - ${CMAKE_STATIC_LIBRARY_PREFIX}atomic_ops${CMAKE_STATIC_LIBRARY_SUFFIX} - ) -ENDIF() +SET(_atomic_ops_lib_name + ${CMAKE_STATIC_LIBRARY_PREFIX}atomic_ops${CMAKE_STATIC_LIBRARY_SUFFIX} +) SET(_atomic_ops_lib ${_lib_dir}/${_atomic_ops_lib_name} ) -SET(_make_command - make -) -SET(_configure_command - sh ./configure -) -SET(_autogen_command - sh ./autogen.sh -) - -# Make sure NOT to enable GPL -SET(_configure_args - "--disable-gpl" +SET(_build_dir + ${RV_DEPS_BASE_DIR}/${_target}/build ) -LIST(APPEND _configure_args "--prefix=${_install_dir}") EXTERNALPROJECT_ADD( ${_target} @@ -67,10 +49,11 @@ EXTERNALPROJECT_ADD( URL_MD5 ${_download_hash} DOWNLOAD_NAME ${_target}_${_version}.zip DOWNLOAD_DIR ${RV_DEPS_DOWNLOAD_DIR} - CONFIGURE_COMMAND ${_autogen_command} && ${_configure_command} ${_configure_args} - BUILD_COMMAND ${_make_command} -j${_cpu_count} - INSTALL_COMMAND ${_make_command} install - BUILD_IN_SOURCE TRUE + CONFIGURE_COMMAND ${CMAKE_COMMAND} -S ${RV_DEPS_BASE_DIR}/${_target}/src -B ${_build_dir} -DCMAKE_INSTALL_PREFIX=${_install_dir} + -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} -Denable_gpl=OFF + BUILD_COMMAND ${CMAKE_COMMAND} --build ${_build_dir} --config ${CMAKE_BUILD_TYPE} -j${_cpu_count} + INSTALL_COMMAND ${CMAKE_COMMAND} --install ${_build_dir} --prefix ${_install_dir} --config ${CMAKE_BUILD_TYPE} + BUILD_IN_SOURCE FALSE BUILD_ALWAYS FALSE BUILD_BYPRODUCTS ${_atomic_ops_lib} USES_TERMINAL_BUILD TRUE diff --git a/cmake/dependencies/boost.cmake b/cmake/dependencies/boost.cmake index e1929cbc3..181eb3077 100644 --- a/cmake/dependencies/boost.cmake +++ b/cmake/dependencies/boost.cmake @@ -15,18 +15,10 @@ SET(_ext_boost_version SET(_major_minor_version ${RV_DEPS_BOOST_MAJOR_MINOR_VERSION} ) -SET(_download_hash - ${RV_DEPS_BOOST_DOWNLOAD_HASH} -) -RV_CREATE_STANDARD_DEPS_VARIABLES("RV_DEPS_BOOST" "${_ext_boost_version}" "" "") +RV_CREATE_STANDARD_DEPS_VARIABLES("RV_DEPS_BOOST" "${_ext_boost_version}" "" "" FORCE_LIB) RV_SHOW_STANDARD_DEPS_VARIABLES() -STRING(REPLACE "." "_" _version_with_underscore ${_version}) -SET(_download_url - "https://archives.boost.io/release/${_version}/source/boost_${_version_with_underscore}.tar.gz" -) - SET(_boost_libs atomic chrono @@ -44,10 +36,32 @@ SET(_boost_libs timer ) -SET(_lib_dir - ${_install_dir}/lib +# Build DEPS_LIST_TARGETS from _boost_libs +SET(_boost_deps_list_targets + "" +) +FOREACH( + _boost_lib + ${_boost_libs} +) + LIST(APPEND _boost_deps_list_targets Boost::${_boost_lib}) +ENDFOREACH() + +# --- Try to find installed package --- +RV_FIND_DEPENDENCY( + TARGET + ${_target} + PACKAGE + Boost + VERSION + ${_ext_boost_version} + COMPONENTS + ${_boost_libs} + DEPS_LIST_TARGETS + ${_boost_deps_list_targets} ) +# --- Library naming (shared between find and build paths) --- # Note: Boost has a custom lib naming scheme on windows IF(RV_TARGET_WINDOWS) SET(BOOST_SHARED_LIBRARY_PREFIX @@ -80,6 +94,7 @@ ELSE() ) ENDIF() +# Compute per-lib paths AFTER RV_FIND_DEPENDENCY (which may override _lib_dir) FOREACH( _boost_lib ${_boost_libs} @@ -99,149 +114,77 @@ FOREACH( ENDIF() ENDFOREACH() -LIST(APPEND _boost_b2_options "-s") -LIST(APPEND _boost_b2_options "NO_LZMA=1") +# --- Build from source if not found --- +IF(NOT ${_target}_FOUND) + INCLUDE(${CMAKE_CURRENT_LIST_DIR}/build/boost.cmake) -IF(RV_VERBOSE_INVOCATION) - LIST(APPEND _boost_b2_options "-d+2") -ELSE() - LIST(APPEND _boost_b2_options "-d+0") -ENDIF() - -IF(RV_TARGET_DARWIN) - SET(_toolset - "clang" - ) - -ELSEIF(RV_TARGET_LINUX) - SET(_toolset - "gcc" - ) -ELSEIF(RV_TARGET_WINDOWS) - SET(_toolset - "msvc-14.3" + # Build path: we control the filenames, use OUTPUTS for precise tracking + FOREACH( + _boost_lib + ${_boost_libs} ) -ELSE() - MESSAGE(FATAL_ERROR "Unsupported (yet) target for Boost") -ENDIF() - -SET(_b2_command - ./b2 -) - -IF(RV_TARGET_WINDOWS) - SET(_bootstrap_command - ./bootstrap.bat - ) -ELSE() - SET(_bootstrap_command - ./bootstrap.sh - ) -ENDIF() - -IF(RV_TARGET_WINDOWS) - SET(_boost_python_bin - ${RV_DEPS_BASE_DIR}/RV_DEPS_PYTHON3/install/python.exe - ) -ELSE() - SET(_boost_python_bin - ${RV_DEPS_BASE_DIR}/RV_DEPS_PYTHON3/install/bin/python - ) -ENDIF() - -STRING(TOLOWER ${CMAKE_BUILD_TYPE} _boost_variant) - -LIST( - TRANSFORM _boost_libs - PREPEND "--with-" - OUTPUT_VARIABLE _boost_with_list -) - -SET(__boost_arch__ - x86 -) -IF(APPLE) - IF(RV_TARGET_APPLE_ARM64) - SET(__boost_arch__ - arm - ) + LIST(APPEND _boost_stage_output ${RV_STAGE_LIB_DIR}/${_boost_${_boost_lib}_lib_name}) + ENDFOREACH() + # Note: On Windows, Boost's b2 puts both .lib and .dll in lib/, so we copy _lib_dir to both RV_STAGE_LIB_DIR and RV_STAGE_BIN_DIR. + IF(RV_TARGET_WINDOWS) + RV_STAGE_DEPENDENCY_LIBS(TARGET ${_target} EXTRA_LIB_DIRS ${RV_STAGE_BIN_DIR} OUTPUTS ${_boost_stage_output}) + ELSE() + RV_STAGE_DEPENDENCY_LIBS(TARGET ${_target} OUTPUTS ${_boost_stage_output}) ENDIF() -ENDIF() - -EXTERNALPROJECT_ADD( - ${_target} - DEPENDS Python::Python - DOWNLOAD_NAME ${_target}_${_version}.tar.gz - DOWNLOAD_DIR ${RV_DEPS_DOWNLOAD_DIR} - DOWNLOAD_EXTRACT_TIMESTAMP TRUE - SOURCE_DIR ${RV_DEPS_BASE_DIR}/${_target}/src - INSTALL_DIR ${_install_dir} - URL ${_download_url} - URL_MD5 ${_download_hash} - CONFIGURE_COMMAND ${_bootstrap_command} --with-toolset=${_toolset} --with-python=${_boost_python_bin} - BUILD_COMMAND - # Ref.: https://www.boost.org/doc/libs/1_70_0/tools/build/doc/html/index.html#bbv2.builtin.features.cflags Ref.: - # https://www.boost.org/doc/libs/1_76_0/tools/build/doc/html/index.html#bbv2.builtin.features.cflags - ./b2 -a -q toolset=${_toolset} cxxstd=${RV_CPP_STANDARD} variant=${_boost_variant} link=shared threading=multi architecture=${__boost_arch__} - address-model=64 ${_boost_with_list} ${_boost_b2_options} -j${_cpu_count} install --prefix=${_install_dir} - INSTALL_COMMAND echo "Boost was both built and installed in the build stage" - BUILD_IN_SOURCE TRUE - BUILD_ALWAYS FALSE - BUILD_BYPRODUCTS ${_boost_byproducts} - USES_TERMINAL_BUILD TRUE -) - -IF(RV_TARGET_WINDOWS) - SET(_include_dir - ${_install_dir}/include/boost-${_major_minor_version} - ) ELSE() - SET(_include_dir - ${_install_dir}/include - ) -ENDIF() - -FILE(MAKE_DIRECTORY ${_include_dir}) - -FOREACH( - _boost_lib - ${_boost_libs} -) - ADD_LIBRARY(Boost::${_boost_lib} SHARED IMPORTED GLOBAL) - ADD_DEPENDENCIES(Boost::${_boost_lib} ${_target}) - SET_PROPERTY( - TARGET Boost::${_boost_lib} - PROPERTY IMPORTED_LOCATION ${_boost_${_boost_lib}_lib} - ) - SET_PROPERTY( - TARGET Boost::${_boost_lib} - PROPERTY IMPORTED_SONAME ${_boost_${_boost_lib}_lib_name} - ) - + # CONFIG found — Boost::xxx targets already exist with proper LOCATION. Create any missing targets as a safety net. IF(RV_TARGET_WINDOWS) - SET_PROPERTY( - TARGET Boost::${_boost_lib} - PROPERTY IMPORTED_IMPLIB ${_boost_${_boost_lib}_implib} + SET(_include_dir + ${_install_dir}/include/boost-${_major_minor_version} ) ENDIF() - TARGET_INCLUDE_DIRECTORIES( - Boost::${_boost_lib} - INTERFACE ${_include_dir} - ) - LIST(APPEND RV_DEPS_LIST Boost::${_boost_lib}) - LIST(APPEND _boost_stage_output ${RV_STAGE_LIB_DIR}/${_boost_${_boost_lib}_lib_name}) -ENDFOREACH() - -ADD_LIBRARY(Boost::headers INTERFACE IMPORTED GLOBAL) -SET_TARGET_PROPERTIES( - Boost::headers - PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "${_include_dir}" -) + # Boost::headers is not in DEPS_LIST_TARGETS so the general symlink fixup in RV_FIND_DEPENDENCY doesn't reach it. Fix it here: Boost's config has a symlink + # resolution bug where get_filename_component(REALPATH) on "../" normalizes before resolving, landing on the shared prefix include dir (e.g. + # /opt/homebrew/include) instead of the package-specific one. + IF(TARGET Boost::headers) + GET_TARGET_PROPERTY(_boost_headers_inc Boost::headers INTERFACE_INCLUDE_DIRECTORIES) + IF(_boost_headers_inc + AND NOT "${_boost_headers_inc}" STREQUAL "${_include_dir}" + ) + SET_TARGET_PROPERTIES( + Boost::headers + PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "${_include_dir}" + ) + MESSAGE(STATUS " Fixed Boost::headers include: ${_boost_headers_inc} -> ${_include_dir}") + ENDIF() + ELSE() + ADD_LIBRARY(Boost::headers INTERFACE IMPORTED GLOBAL) + SET_TARGET_PROPERTIES( + Boost::headers + PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "${_include_dir}" + ) + ENDIF() -# Note: On Windows, Boost's b2 puts both .lib and .dll in lib/, so we copy _lib_dir to both RV_STAGE_LIB_DIR and RV_STAGE_BIN_DIR. -IF(RV_TARGET_WINDOWS) - RV_STAGE_DEPENDENCY_LIBS(TARGET ${_target} EXTRA_LIB_DIRS ${RV_STAGE_BIN_DIR} OUTPUTS ${_boost_stage_output}) -ELSE() - RV_STAGE_DEPENDENCY_LIBS(TARGET ${_target} OUTPUTS ${_boost_stage_output}) + FOREACH( + _boost_lib + ${_boost_libs} + ) + IF(NOT TARGET Boost::${_boost_lib}) + RV_ADD_IMPORTED_LIBRARY( + NAME + Boost::${_boost_lib} + TYPE + SHARED + LOCATION + ${_boost_${_boost_lib}_lib} + SONAME + ${_boost_${_boost_lib}_lib_name} + IMPLIB + ${_boost_${_boost_lib}_implib} + INCLUDE_DIRS + ${_include_dir} + DEPENDS + ${_target} + ) + ENDIF() + ENDFOREACH() + + # Found path: actual filenames may differ (e.g. -mt suffix), use TARGET_LIBS to resolve at build time + RV_STAGE_DEPENDENCY_LIBS(TARGET ${_target} TARGET_LIBS ${_boost_deps_list_targets}) ENDIF() diff --git a/cmake/dependencies/build/boost.cmake b/cmake/dependencies/build/boost.cmake new file mode 100644 index 000000000..6235568a1 --- /dev/null +++ b/cmake/dependencies/build/boost.cmake @@ -0,0 +1,144 @@ +# +# Copyright (C) 2022 Autodesk, Inc. All Rights Reserved. +# +# SPDX-License-Identifier: Apache-2.0 +# +# Build Boost from source via ExternalProject_Add. Included by cmake/dependencies/boost.cmake when no installed package is found. +# +# Expects these variables from the caller (set by RV_CREATE_STANDARD_DEPS_VARIABLES): _target, _version, _install_dir, _lib_dir, _include_dir, _source_dir And +# these dep-specific variables from the caller: _major_minor_version, _boost_libs, _boost_byproducts, _boost_${lib}_lib, _boost_${lib}_lib_name, +# _boost_${lib}_implib (Windows) + +STRING(REPLACE "." "_" _version_with_underscore ${_version}) +SET(_download_url + "https://archives.boost.io/release/${_version}/source/boost_${_version_with_underscore}.tar.gz" +) +SET(_download_hash + ${RV_DEPS_BOOST_DOWNLOAD_HASH} +) + +LIST(APPEND _boost_b2_options "-s") +LIST(APPEND _boost_b2_options "NO_LZMA=1") + +IF(RV_VERBOSE_INVOCATION) + LIST(APPEND _boost_b2_options "-d+2") +ELSE() + LIST(APPEND _boost_b2_options "-d+0") +ENDIF() + +IF(RV_TARGET_DARWIN) + SET(_toolset + "clang" + ) +ELSEIF(RV_TARGET_LINUX) + SET(_toolset + "gcc" + ) +ELSEIF(RV_TARGET_WINDOWS) + SET(_toolset + "msvc-14.3" + ) +ELSE() + MESSAGE(FATAL_ERROR "Unsupported (yet) target for Boost") +ENDIF() + +IF(RV_TARGET_WINDOWS) + SET(_bootstrap_command + ./bootstrap.bat + ) +ELSE() + SET(_bootstrap_command + ./bootstrap.sh + ) +ENDIF() + +IF(RV_TARGET_WINDOWS) + SET(_boost_python_bin + ${RV_DEPS_BASE_DIR}/RV_DEPS_PYTHON3/install/python.exe + ) +ELSE() + SET(_boost_python_bin + ${RV_DEPS_BASE_DIR}/RV_DEPS_PYTHON3/install/bin/python + ) +ENDIF() + +STRING(TOLOWER ${CMAKE_BUILD_TYPE} _boost_variant) + +LIST( + TRANSFORM _boost_libs + PREPEND "--with-" + OUTPUT_VARIABLE _boost_with_list +) + +SET(__boost_arch__ + x86 +) +IF(APPLE) + IF(RV_TARGET_APPLE_ARM64) + SET(__boost_arch__ + arm + ) + ENDIF() +ENDIF() + +EXTERNALPROJECT_ADD( + ${_target} + DEPENDS Python::Python + DOWNLOAD_NAME ${_target}_${_version}.tar.gz + DOWNLOAD_DIR ${RV_DEPS_DOWNLOAD_DIR} + DOWNLOAD_EXTRACT_TIMESTAMP TRUE + SOURCE_DIR ${RV_DEPS_BASE_DIR}/${_target}/src + INSTALL_DIR ${_install_dir} + URL ${_download_url} + URL_MD5 ${_download_hash} + CONFIGURE_COMMAND ${_bootstrap_command} --with-toolset=${_toolset} --with-python=${_boost_python_bin} + BUILD_COMMAND + # Ref.: https://www.boost.org/doc/libs/1_70_0/tools/build/doc/html/index.html#bbv2.builtin.features.cflags Ref.: + # https://www.boost.org/doc/libs/1_76_0/tools/build/doc/html/index.html#bbv2.builtin.features.cflags + ./b2 -a -q toolset=${_toolset} cxxstd=${RV_CPP_STANDARD} variant=${_boost_variant} link=shared threading=multi architecture=${__boost_arch__} + address-model=64 ${_boost_with_list} ${_boost_b2_options} -j${_cpu_count} install --prefix=${_install_dir} + INSTALL_COMMAND echo "Boost was both built and installed in the build stage" + BUILD_IN_SOURCE TRUE + BUILD_ALWAYS FALSE + BUILD_BYPRODUCTS ${_boost_byproducts} + USES_TERMINAL_BUILD TRUE +) + +IF(RV_TARGET_WINDOWS) + SET(_include_dir + ${_install_dir}/include/boost-${_major_minor_version} + ) +ELSE() + SET(_include_dir + ${_install_dir}/include + ) +ENDIF() + +FOREACH( + _boost_lib + ${_boost_libs} +) + RV_ADD_IMPORTED_LIBRARY( + NAME + Boost::${_boost_lib} + TYPE + SHARED + LOCATION + ${_boost_${_boost_lib}_lib} + SONAME + ${_boost_${_boost_lib}_lib_name} + IMPLIB + ${_boost_${_boost_lib}_implib} + INCLUDE_DIRS + ${_include_dir} + DEPENDS + ${_target} + ADD_TO_DEPS_LIST + ) +ENDFOREACH() + +ADD_LIBRARY(Boost::headers INTERFACE IMPORTED GLOBAL) +SET_TARGET_PROPERTIES( + Boost::headers + PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "${_include_dir}" +) diff --git a/cmake/dependencies/build/dav1d.cmake b/cmake/dependencies/build/dav1d.cmake new file mode 100644 index 000000000..809114ce0 --- /dev/null +++ b/cmake/dependencies/build/dav1d.cmake @@ -0,0 +1,103 @@ +# +# Copyright (C) 2022 Autodesk, Inc. All Rights Reserved. +# +# SPDX-License-Identifier: Apache-2.0 +# +# Build dav1d from source via ExternalProject_Add. Included by cmake/dependencies/dav1d.cmake when no installed package is found. +# +# Expects these variables from the caller (set by RV_CREATE_STANDARD_DEPS_VARIABLES): _target, _version, _source_dir, _install_dir, _lib_dir, _bin_dir, +# _include_dir, _configure_command, _make_command And these dep-specific variables from the caller: _david_lib_name, _dav1d_lib +# + +SET(_download_url + "https://github.com/videolan/dav1d/archive/refs/tags/${_version}.zip" +) +SET(_download_hash + ${RV_DEPS_DAV1D_DOWNLOAD_HASH} +) + +# _lib_dir_name is needed for the meson --libdir option +IF(RHEL_VERBOSE) + SET(_lib_dir_name + lib64 + ) +ELSE() + SET(_lib_dir_name + lib + ) +ENDIF() + +IF(APPLE) + # Cross-file must be specified because if Rosetta is used to compile for x86_64 from ARM64, Meson still detects ARM64 as the default architecture. + IF(RV_TARGET_APPLE_X86_64) + SET(_meson_cross_file + "${PROJECT_SOURCE_DIR}/src/build/meson_arch_x86_64.txt" + ) + ELSEIF(RV_TARGET_APPLE_ARM64) + SET(_meson_cross_file + "${PROJECT_SOURCE_DIR}/src/build/meson_arch_arm64.txt" + ) + ENDIF() + + SET(_configure_command + ${_configure_command} "--cross-file" ${_meson_cross_file} + ) +ENDIF() + +SET(_default_library + shared +) + +EXTERNALPROJECT_ADD( + ${_target} + DOWNLOAD_NAME ${_target}_${_version}.zip + DOWNLOAD_DIR ${RV_DEPS_DOWNLOAD_DIR} + DOWNLOAD_EXTRACT_TIMESTAMP TRUE + SOURCE_DIR ${_source_dir} + INSTALL_DIR ${_install_dir} + URL ${_download_url} + URL_MD5 ${_download_hash} + CONFIGURE_COMMAND ${_configure_command} ./_build --libdir=${_lib_dir_name} --default-library=${_default_library} --prefix=${_install_dir} -Denable_tests=false + -Denable_tools=false + BUILD_COMMAND ${_make_command} -C _build + INSTALL_COMMAND ${_make_command} -C _build install + COMMAND ${CMAKE_COMMAND} -E copy_directory ${_lib_dir} ${RV_STAGE_LIB_DIR} + BUILD_IN_SOURCE TRUE + BUILD_ALWAYS FALSE + BUILD_BYPRODUCTS ${_dav1d_lib} + USES_TERMINAL_BUILD TRUE +) + +IF(RV_TARGET_WINDOWS) + RV_ADD_IMPORTED_LIBRARY( + NAME + dav1d::dav1d + TYPE + SHARED + LOCATION + ${_dav1d_lib} + IMPLIB + ${_dav1d_implib} + INCLUDE_DIRS + ${_include_dir} + DEPENDS + ${_target} + ADD_TO_DEPS_LIST + ) +ELSE() + RV_ADD_IMPORTED_LIBRARY( + NAME + dav1d::dav1d + TYPE + SHARED + LOCATION + ${_dav1d_lib} + SONAME + ${_david_lib_name} + INCLUDE_DIRS + ${_include_dir} + DEPENDS + ${_target} + ADD_TO_DEPS_LIST + ) +ENDIF() diff --git a/cmake/dependencies/build/glew.cmake b/cmake/dependencies/build/glew.cmake new file mode 100644 index 000000000..8f960c056 --- /dev/null +++ b/cmake/dependencies/build/glew.cmake @@ -0,0 +1,48 @@ +# +# Copyright (C) 2022 Autodesk, Inc. All Rights Reserved. +# +# SPDX-License-Identifier: Apache-2.0 +# +# Build GLEW from source via ExternalProject_Add (Makefile-based build). Included by cmake/dependencies/glew.cmake when no installed package is found. +# +# Expects these variables from the caller (set by RV_CREATE_STANDARD_DEPS_VARIABLES): _target, _version, _install_dir, _lib_dir, _include_dir, _make_command, +# _cpu_count And these dep-specific variables from the caller: _glew_lib, _glew_lib_name + +SET(_download_url + "https://github.com/nigels-com/glew/archive/refs/tags/glew-${_version}.tar.gz" +) + +SET(_download_hash + ${RV_DEPS_GLEW_DOWNLOAD_HASH} +) + +EXTERNALPROJECT_ADD( + ${_target} + SOURCE_DIR ${RV_DEPS_BASE_DIR}/${_target}/src + INSTALL_DIR ${_install_dir} + URL ${_download_url} + URL_MD5 ${_download_hash} + DOWNLOAD_NAME glew-glew-${_version}.tar.gz + DOWNLOAD_DIR ${RV_DEPS_DOWNLOAD_DIR} + CONFIGURE_COMMAND cd auto && ${_make_command} && cd .. && ${_make_command} + BUILD_COMMAND ${_make_command} -j${_cpu_count} GLEW_DEST=${_install_dir} + INSTALL_COMMAND ${_make_command} install LIBDIR=${_lib_dir} GLEW_DEST=${_install_dir} + BUILD_IN_SOURCE TRUE + BUILD_ALWAYS FALSE + BUILD_BYPRODUCTS ${_glew_lib} + USES_TERMINAL_BUILD TRUE +) + +RV_ADD_IMPORTED_LIBRARY( + NAME + GLEW::GLEW + TYPE + STATIC + LOCATION + ${_glew_lib} + INCLUDE_DIRS + ${_include_dir} + DEPENDS + ${_target} + ADD_TO_DEPS_LIST +) diff --git a/cmake/dependencies/build/imath.cmake b/cmake/dependencies/build/imath.cmake new file mode 100644 index 000000000..348d5e457 --- /dev/null +++ b/cmake/dependencies/build/imath.cmake @@ -0,0 +1,65 @@ +# +# Copyright (C) 2022 Autodesk, Inc. All Rights Reserved. +# +# SPDX-License-Identifier: Apache-2.0 +# +# Build Imath from source via ExternalProject_Add. Included by cmake/dependencies/imath.cmake when no installed package is found. +# +# Expects these variables from the caller (set by RV_CREATE_STANDARD_DEPS_VARIABLES): _target, _version, _install_dir, _configure_options, _cmake_build_command, +# _cmake_install_command And these dep-specific variables from the caller: _libname, _libpath, _implibpath (Windows only) +# + +SET(_download_url + "https://github.com/AcademySoftwareFoundation/Imath/archive/refs/tags/v${_version}.zip" +) + +SET(_download_hash + "${RV_DEPS_IMATH_DOWNLOAD_HASH}" +) + +# Override include dir to Imath subdirectory for build-from-source +SET(_include_dir + ${_install_dir}/include/Imath +) + +LIST(APPEND _imath_byproducts ${_libpath}) + +IF(RV_TARGET_WINDOWS) + LIST(APPEND _imath_byproducts ${_implibpath}) +ENDIF() + +EXTERNALPROJECT_ADD( + ${_target} + URL ${_download_url} + URL_MD5 ${_download_hash} + DOWNLOAD_NAME ${_target}_${_version}.zip + DOWNLOAD_DIR ${RV_DEPS_DOWNLOAD_DIR} + DOWNLOAD_EXTRACT_TIMESTAMP TRUE + SOURCE_DIR ${RV_DEPS_BASE_DIR}/${_target}/src + INSTALL_DIR ${_install_dir} + CONFIGURE_COMMAND ${CMAKE_COMMAND} ${_configure_options} + BUILD_COMMAND ${_cmake_build_command} + INSTALL_COMMAND ${_cmake_install_command} + BUILD_IN_SOURCE TRUE + BUILD_ALWAYS FALSE + BUILD_BYPRODUCTS ${_imath_byproducts} + USES_TERMINAL_BUILD TRUE +) + +RV_ADD_IMPORTED_LIBRARY( + NAME + Imath::Imath + TYPE + SHARED + LOCATION + ${_libpath} + SONAME + ${_libname} + IMPLIB + ${_implibpath} + INCLUDE_DIRS + ${_include_dir} + DEPENDS + ${_target} + ADD_TO_DEPS_LIST +) diff --git a/cmake/dependencies/build/openexr.cmake b/cmake/dependencies/build/openexr.cmake new file mode 100644 index 000000000..2bb1ca82f --- /dev/null +++ b/cmake/dependencies/build/openexr.cmake @@ -0,0 +1,149 @@ +# +# Copyright (C) 2022 Autodesk, Inc. All Rights Reserved. +# +# SPDX-License-Identifier: Apache-2.0 +# +# Build OpenEXR from source via ExternalProject_Add. Included by cmake/dependencies/openexr.cmake when no installed package is found. +# +# Expects these variables from the caller (set by RV_CREATE_STANDARD_DEPS_VARIABLES): _target, _version, _install_dir, _lib_dir, _bin_dir, _include_dir, +# _source_dir And these dep-specific variables from the caller: _openexr_libname_suffix_, LIB_VERSION_SUFFIX, _openexr_byproducts _openexr_lib, _openexr_name, +# _openexrcore_lib, _openexrcore_name _ilmthread_lib, _ilmthread_name, _iex_lib, _iex_name _openexr_implib, _ilmthread_implib, _iex_implib (Windows) + +SET(_make_command + make +) +IF(${RV_OSX_EMULATION}) + SET(_darwin_x86_64 + "arch" "${RV_OSX_EMULATION_ARCH}" + ) + SET(_make_command + ${_darwin_x86_64} ${_make_command} + ) +ENDIF() +IF(RV_TARGET_WINDOWS) + # MSYS2/CMake defaults to Ninja + SET(_make_command + ninja + ) +ENDIF() + +SET(_openexr_patch_file_ + "${CMAKE_CURRENT_SOURCE_DIR}/patch/${RV_DEPS_OPENEXR_PATCH_NAME}.patch" +) + +IF(EXISTS "${_openexr_patch_file_}") + SET(_patch_command + patch -p1 < ${_openexr_patch_file_} + ) + MESSAGE(STATUS "Patch command set for ${_target}: ${_patch_command}") +ELSE() + # If it does not exist, set the command to an empty string. ExternalProject_Add will skip the patch step if the command is empty. + SET(_patch_command + "" + ) + MESSAGE(STATUS "ERROR Patch file not found, skipping patch for ${_target}: ${_patch_file}") +ENDIF() + +LIST(APPEND _configure_options "-DCMAKE_PREFIX_PATH=${RV_DEPS_IMATH_CMAKE_DIR}") +LIST(APPEND _configure_options "-DBUILD_TESTING=OFF") +IF(RV_TARGET_WINDOWS) + GET_TARGET_PROPERTY(_zlib_implibpath ZLIB::ZLIB IMPORTED_IMPLIB) + LIST(APPEND _configure_options "-DZLIB_INCLUDE_DIR=${RV_DEPS_ZLIB_INCLUDE_DIR}") + LIST(APPEND _configure_options "-DZLIB_LIBRARY=${_zlib_implibpath}") +ENDIF() + +# OpenEXR tools are not needed. +LIST(APPEND _configure_options "-DOPENEXR_BUILD_TOOLS=OFF") + +# Disable OpenEXR's automatic rpath setup to avoid conflicts with RV's rpath management OpenEXR 3.3+ automatically adds @loader_path/../lib which conflicts with +# our install scripts +LIST(APPEND _configure_options "-DCMAKE_INSTALL_RPATH=") + +EXTERNALPROJECT_ADD( + ${_target} + URL "https://github.com/AcademySoftwareFoundation/openexr/archive/refs/tags/v${_version}.zip" + URL_MD5 ${RV_DEPS_OPENEXR_DOWNLOAD_HASH} + DOWNLOAD_NAME ${_target}_${_version}.zip + DOWNLOAD_DIR ${RV_DEPS_DOWNLOAD_DIR} + DOWNLOAD_EXTRACT_TIMESTAMP TRUE + SOURCE_DIR ${RV_DEPS_BASE_DIR}/${_target}/src + DEPENDS Imath::Imath ZLIB::ZLIB + INSTALL_DIR ${_install_dir} + PATCH_COMMAND ${_patch_command} + CONFIGURE_COMMAND ${CMAKE_COMMAND} ${_configure_options} + BUILD_COMMAND ${_cmake_build_command} + INSTALL_COMMAND ${_cmake_install_command} + BUILD_IN_SOURCE TRUE + BUILD_ALWAYS FALSE + BUILD_BYPRODUCTS ${_openexr_byproducts} + USES_TERMINAL_BUILD TRUE +) + +SET(_include_dir + ${_install_dir}/include/OpenEXR +) + +FILE(MAKE_DIRECTORY ${_include_dir}) + +ADD_LIBRARY(OpenEXR::IlmThread SHARED IMPORTED GLOBAL) +ADD_DEPENDENCIES(OpenEXR::IlmThread ${_target}) +SET_PROPERTY( + TARGET OpenEXR::IlmThread + PROPERTY IMPORTED_LOCATION ${_ilmthread_lib} +) +SET_PROPERTY( + TARGET OpenEXR::IlmThread + PROPERTY IMPORTED_SONAME ${_ilmthread_name} +) +IF(RV_TARGET_WINDOWS) + SET_PROPERTY( + TARGET OpenEXR::IlmThread + PROPERTY IMPORTED_IMPLIB ${_ilmthread_implib} + ) +ENDIF() +LIST(APPEND RV_DEPS_LIST OpenEXR::IlmThread) + +ADD_LIBRARY(OpenEXR::Iex SHARED IMPORTED GLOBAL) +ADD_DEPENDENCIES(OpenEXR::Iex ${_target}) +SET_PROPERTY( + TARGET OpenEXR::Iex + PROPERTY IMPORTED_LOCATION ${_iex_lib} +) +SET_PROPERTY( + TARGET OpenEXR::Iex + PROPERTY IMPORTED_SONAME ${_iex_name} +) +IF(RV_TARGET_WINDOWS) + SET_PROPERTY( + TARGET OpenEXR::Iex + PROPERTY IMPORTED_IMPLIB ${_iex_implib} + ) +ENDIF() +LIST(APPEND RV_DEPS_LIST OpenEXR::Iex) + +ADD_LIBRARY(OpenEXR::OpenEXR SHARED IMPORTED GLOBAL) +ADD_DEPENDENCIES(OpenEXR::OpenEXR ${_target}) +SET_PROPERTY( + TARGET OpenEXR::OpenEXR + PROPERTY IMPORTED_LOCATION ${_openexr_lib} +) +SET_PROPERTY( + TARGET OpenEXR::OpenEXR + PROPERTY IMPORTED_SONAME ${_openexr_name} +) +IF(RV_TARGET_WINDOWS) + SET_PROPERTY( + TARGET OpenEXR::OpenEXR + PROPERTY IMPORTED_IMPLIB ${_openexr_implib} + ) +ENDIF() + +TARGET_INCLUDE_DIRECTORIES( + OpenEXR::OpenEXR + INTERFACE ${_include_dir} +) +TARGET_LINK_LIBRARIES( + OpenEXR::OpenEXR + INTERFACE Imath::Imath OpenEXR::Iex OpenEXR::IlmThread ZLIB::ZLIB +) +LIST(APPEND RV_DEPS_LIST OpenEXR::OpenEXR) diff --git a/cmake/dependencies/build/openssl.cmake b/cmake/dependencies/build/openssl.cmake new file mode 100644 index 000000000..a7df4230c --- /dev/null +++ b/cmake/dependencies/build/openssl.cmake @@ -0,0 +1,129 @@ +# +# Copyright (C) 2022 Autodesk, Inc. All Rights Reserved. +# +# SPDX-License-Identifier: Apache-2.0 +# + +SET(RV_DEPS_WIN_PERL_ROOT + "" + CACHE STRING "Path to Windows perl root" +) + +STRING(REPLACE "." "_" _version_underscored ${_version}) + +IF(RV_TARGET_WINDOWS + AND (NOT RV_DEPS_WIN_PERL_ROOT + OR RV_DEPS_WIN_PERL_ROOT STREQUAL "") +) + MESSAGE( + FATAL_ERROR + "Unable to build without a RV_DEPS_WIN_PERL_ROOT. OpenSSL requires a Windows native perl interpreter to build (it recommends https://strawberryperl.com/). Example -DRV_DEPS_WIN_PERL_ROOT=c:/Strawberry/perl/bin" + ) +ENDIF() + +SET(_make_command_script + "${PROJECT_SOURCE_DIR}/src/build/make_openssl.py" +) +SET(_make_command + python3 "${_make_command_script}" +) + +LIST(APPEND _make_command "--source-dir") +LIST(APPEND _make_command ${_source_dir}) +LIST(APPEND _make_command "--output-dir") +LIST(APPEND _make_command ${_install_dir}) + +LIST(APPEND _make_command "--vfx_platform") +LIST(APPEND _make_command ${RV_VFX_CY_YEAR}) + +IF(RV_TARGET_WINDOWS) + LIST(APPEND _make_command "--perlroot") + LIST(APPEND _make_command ${RV_DEPS_WIN_PERL_ROOT}) +ENDIF() + +IF(APPLE) + IF(RV_TARGET_APPLE_X86_64) + SET(__openssl_arch__ + x86_64 + ) + ELSEIF(RV_TARGET_APPLE_ARM64) + SET(__openssl_arch__ + arm64 + ) + ENDIF() + LIST(APPEND _make_command --arch=-${__openssl_arch__}) +ENDIF() + +EXTERNALPROJECT_ADD( + ${_target} + DOWNLOAD_NAME ${_target}_${_version}.zip + DOWNLOAD_DIR ${RV_DEPS_DOWNLOAD_DIR} + DOWNLOAD_EXTRACT_TIMESTAMP TRUE + SOURCE_DIR ${_source_dir} + INSTALL_DIR ${_install_dir} + URL ${_download_url} + URL_MD5 ${_download_hash} + CONFIGURE_COMMAND ${_make_command} --configure + BUILD_COMMAND ${_make_command} --build + INSTALL_COMMAND ${_make_command} --install + BUILD_IN_SOURCE TRUE + BUILD_ALWAYS FALSE + BUILD_BYPRODUCTS ${_crypto_lib} ${_ssl_lib} + USES_TERMINAL_BUILD TRUE +) + +FILE(MAKE_DIRECTORY ${_include_dir}) + +IF(RV_TARGET_WINDOWS) + ADD_CUSTOM_COMMAND( + TARGET ${_target} + POST_BUILD + COMMENT "Renaming the openssl import libs to the name FFmpeg is expecting" + COMMAND ${CMAKE_COMMAND} -E copy ${_install_dir}/lib/libssl.lib ${_lib_dir}/ssl.lib + COMMAND ${CMAKE_COMMAND} -E copy ${_install_dir}/lib/libcrypto.lib ${_lib_dir}/crypto.lib + ) +ENDIF() + +ADD_LIBRARY(OpenSSL::Crypto SHARED IMPORTED GLOBAL) +ADD_DEPENDENCIES(OpenSSL::Crypto ${_target}) +SET_PROPERTY( + TARGET OpenSSL::Crypto + PROPERTY IMPORTED_LOCATION ${_crypto_lib} +) +SET_PROPERTY( + TARGET OpenSSL::Crypto + PROPERTY IMPORTED_SONAME ${_crypto_lib_name} +) +IF(RV_TARGET_WINDOWS) + SET_PROPERTY( + TARGET OpenSSL::Crypto + PROPERTY IMPORTED_IMPLIB ${_implibpath_crypto} + ) +ENDIF() +TARGET_INCLUDE_DIRECTORIES( + OpenSSL::Crypto + INTERFACE ${_include_dir} +) +LIST(APPEND RV_DEPS_LIST OpenSSL::Crypto) + +ADD_LIBRARY(OpenSSL::SSL SHARED IMPORTED GLOBAL) +ADD_DEPENDENCIES(OpenSSL::SSL ${_target}) +SET_PROPERTY( + TARGET OpenSSL::SSL + PROPERTY IMPORTED_LOCATION ${_ssl_lib} +) +SET_PROPERTY( + TARGET OpenSSL::SSL + PROPERTY IMPORTED_SONAME ${_ssl_lib_name} +) +IF(RV_TARGET_WINDOWS) + SET_PROPERTY( + TARGET OpenSSL::SSL + PROPERTY IMPORTED_IMPLIB ${_implibpath_ssl} + ) +ENDIF() +TARGET_INCLUDE_DIRECTORIES( + OpenSSL::SSL + INTERFACE ${_include_dir} +) +LIST(APPEND RV_DEPS_LIST OpenSSL::SSL) diff --git a/cmake/dependencies/build/zlib.cmake b/cmake/dependencies/build/zlib.cmake new file mode 100644 index 000000000..d51796e11 --- /dev/null +++ b/cmake/dependencies/build/zlib.cmake @@ -0,0 +1,61 @@ +# +# Copyright (C) 2022 Autodesk, Inc. All Rights Reserved. +# +# SPDX-License-Identifier: Apache-2.0 +# + +# The patch comes from the ZLIB port in Vcpkg repository. The name of the patch is kept as is. See https://github.com/microsoft/vcpkg/tree/master/ports/zlib +# Description: Fix unistd.h being incorrectly required when imported from a project defining HAVE_UNISTD_H=0 +SET(_patch_command + patch -p1 < ${CMAKE_CURRENT_SOURCE_DIR}/patch/zconf.h.cmakein_prevent_invalid_inclusions.patch && patch -p1 < + ${CMAKE_CURRENT_SOURCE_DIR}/patch/zconf.h.in_prevent_invalid_inclusions.patch +) + +EXTERNALPROJECT_ADD( + ${_target} + URL ${_download_url} + URL_MD5 ${_download_hash} + DOWNLOAD_NAME ${_target}_${_version}.zip + DOWNLOAD_DIR ${RV_DEPS_DOWNLOAD_DIR} + DOWNLOAD_EXTRACT_TIMESTAMP TRUE + SOURCE_DIR ${RV_DEPS_BASE_DIR}/${_target}/src + INSTALL_DIR ${_install_dir} + PATCH_COMMAND ${_patch_command} + CONFIGURE_COMMAND ${CMAKE_COMMAND} ${_configure_options} + BUILD_COMMAND ${_cmake_build_command} + INSTALL_COMMAND ${_cmake_install_command} + BUILD_IN_SOURCE TRUE + BUILD_ALWAYS FALSE + BUILD_BYPRODUCTS ${_zlib_byproducts} + USES_TERMINAL_BUILD TRUE +) + +IF(RV_TARGET_WINDOWS) + # FFmpeg expects "zlib" in Release and Debug. + IF(CMAKE_BUILD_TYPE MATCHES "^Debug$") + ADD_CUSTOM_COMMAND( + TARGET ${_target} + POST_BUILD + COMMENT "Renaming the ZLIB import debug lib to the name FFmpeg is expecting (release name)" + COMMAND ${CMAKE_COMMAND} -E copy ${_implibpath} ${_lib_dir}/zlib.lib + ) + ENDIF() +ENDIF() + +RV_ADD_IMPORTED_LIBRARY( + NAME + ZLIB::ZLIB + TYPE + SHARED + LOCATION + ${_libpath} + SONAME + ${_libname} + IMPLIB + ${_implibpath} + INCLUDE_DIRS + ${_include_dir} + DEPENDS + ${_target} + ADD_TO_DEPS_LIST +) diff --git a/cmake/dependencies/dav1d.cmake b/cmake/dependencies/dav1d.cmake index 6107578af..4f979a2d6 100644 --- a/cmake/dependencies/dav1d.cmake +++ b/cmake/dependencies/dav1d.cmake @@ -7,101 +7,96 @@ RV_CREATE_STANDARD_DEPS_VARIABLES("RV_DEPS_DAV1D" "${RV_DEPS_DAV1D_VERSION}" "ninja" "meson") RV_SHOW_STANDARD_DEPS_VARIABLES() -SET(_download_url - "https://github.com/videolan/dav1d/archive/refs/tags/${_version}.zip" -) -SET(_download_hash - ${RV_DEPS_DAV1D_DOWNLOAD_HASH} -) - -# _lib_dir_name is needed for the meson --libdir option -IF(RHEL_VERBOSE) - SET(_lib_dir_name - lib64 +# --- Library naming (shared on all platforms, same filenames for find and build) --- +IF(RV_TARGET_DARWIN) + SET(_david_lib_name + ${CMAKE_SHARED_LIBRARY_PREFIX}dav1d.${RV_DEPS_DAV1D_VERSION_LIB}${CMAKE_SHARED_LIBRARY_SUFFIX} ) -ELSE() - SET(_lib_dir_name - lib +ELSEIF(RV_TARGET_LINUX) + SET(_david_lib_name + ${CMAKE_SHARED_LIBRARY_PREFIX}dav1d${CMAKE_SHARED_LIBRARY_SUFFIX}.${RV_DEPS_DAV1D_VERSION_LIB} + ) +ELSEIF(RV_TARGET_WINDOWS) + SET(_david_lib_name + ${CMAKE_SHARED_LIBRARY_PREFIX}dav1d${CMAKE_SHARED_LIBRARY_SUFFIX} ) ENDIF() -SET(_david_lib_name - ${CMAKE_STATIC_LIBRARY_PREFIX}dav1d${CMAKE_STATIC_LIBRARY_SUFFIX} -) - -SET(_dav1d_lib - ${_lib_dir}/${_david_lib_name} +# --- Try to find installed package --- +RV_FIND_DEPENDENCY( + TARGET + ${_target} + PACKAGE + dav1d + VERSION + ${RV_DEPS_DAV1D_VERSION} + PKG_CONFIG_NAME + dav1d + DEPS_LIST_TARGETS + dav1d::dav1d ) -IF(APPLE) - # Cross-file must be specified because if Rosetta is used to compile for x86_64 from ARM64, Meson still detects ARM64 as the default architecture. - - IF(RV_TARGET_APPLE_X86_64) - SET(_meson_cross_file - "${PROJECT_SOURCE_DIR}/src/build/meson_arch_x86_64.txt" - ) - ELSEIF(RV_TARGET_APPLE_ARM64) - SET(_meson_cross_file - "${PROJECT_SOURCE_DIR}/src/build/meson_arch_arm64.txt" - ) - ENDIF() - - SET(_configure_command - ${_configure_command} "--cross-file" ${_meson_cross_file} - ) -ENDIF() - +# Compute full library paths AFTER RV_FIND_DEPENDENCY (which may override _lib_dir, _bin_dir) IF(RV_TARGET_WINDOWS) - SET(_default_library - shared + SET(_dav1d_lib + ${_bin_dir}/${_david_lib_name} + ) + SET(_dav1d_implib_name + dav1d${CMAKE_IMPORT_LIBRARY_SUFFIX} + ) + SET(_dav1d_implib + ${_lib_dir}/${_dav1d_implib_name} ) ELSE() - SET(_default_library - static + SET(_dav1d_lib + ${_lib_dir}/${_david_lib_name} ) ENDIF() -EXTERNALPROJECT_ADD( - ${_target} - DOWNLOAD_NAME ${_target}_${_version}.zip - DOWNLOAD_DIR ${RV_DEPS_DOWNLOAD_DIR} - DOWNLOAD_EXTRACT_TIMESTAMP TRUE - SOURCE_DIR ${_source_dir} - INSTALL_DIR ${_install_dir} - URL ${_download_url} - URL_MD5 ${_download_hash} - CONFIGURE_COMMAND ${_configure_command} ./_build --libdir=${_lib_dir_name} --default-library=${_default_library} --prefix=${_install_dir} -Denable_tests=false - -Denable_tools=false - BUILD_COMMAND ${_make_command} -C _build - INSTALL_COMMAND ${_make_command} -C _build install - COMMAND ${CMAKE_COMMAND} -E copy_directory ${_lib_dir} ${RV_STAGE_LIB_DIR} - BUILD_IN_SOURCE TRUE - BUILD_ALWAYS FALSE - BUILD_BYPRODUCTS ${_dav1d_lib} - USES_TERMINAL_BUILD TRUE -) - -RV_ADD_IMPORTED_LIBRARY( - NAME - dav1d::dav1d - TYPE - STATIC - LOCATION - ${_dav1d_lib} - INCLUDE_DIRS - ${_include_dir} - DEPENDS - ${_target} - ADD_TO_DEPS_LIST -) - -IF(RV_TARGET_WINDOWS) - RV_STAGE_DEPENDENCY_LIBS(TARGET ${_target} BIN_DIR ${_install_dir}/bin OUTPUTS ${RV_STAGE_LIB_DIR}/${_david_lib_name}) +# --- Build from source if not found --- +IF(NOT ${_target}_FOUND) + INCLUDE(${CMAKE_CURRENT_LIST_DIR}/build/dav1d.cmake) ELSE() - RV_STAGE_DEPENDENCY_LIBS(TARGET ${_target} OUTPUTS ${RV_STAGE_LIB_DIR}/${_david_lib_name}) + # Found via find_package or pkg-config — create proper imported target with LOCATION + IF(NOT TARGET dav1d::dav1d) + IF(RV_TARGET_WINDOWS) + RV_ADD_IMPORTED_LIBRARY( + NAME + dav1d::dav1d + TYPE + SHARED + LOCATION + ${_dav1d_lib} + IMPLIB + ${_dav1d_implib} + INCLUDE_DIRS + ${_include_dir} + DEPENDS + ${_target} + ) + ELSE() + RV_ADD_IMPORTED_LIBRARY( + NAME + dav1d::dav1d + TYPE + SHARED + LOCATION + ${_dav1d_lib} + SONAME + ${_david_lib_name} + INCLUDE_DIRS + ${_include_dir} + DEPENDS + ${_target} + ) + ENDIF() + ENDIF() ENDIF() -# FFmpeg customization adding dav1d codec support to FFmpeg +# --- Staging --- +RV_STAGE_DEPENDENCY_LIBS(TARGET ${_target} TARGET_LIBS dav1d::dav1d) + +# --- FFmpeg customization adding dav1d codec support --- SET_PROPERTY( GLOBAL APPEND PROPERTY "RV_FFMPEG_DEPENDS" RV_DEPS_DAV1D diff --git a/cmake/dependencies/ffmpeg.cmake b/cmake/dependencies/ffmpeg.cmake index 7f949bdf6..a676a2e84 100644 --- a/cmake/dependencies/ffmpeg.cmake +++ b/cmake/dependencies/ffmpeg.cmake @@ -410,13 +410,15 @@ ADD_CUSTOM_TARGET( COMMAND ${CMAKE_COMMAND} -E remove_directory ${RV_DEPS_BASE_DIR}/cmake/dependencies/${_target}-prefix ) -# Note: On Windows, FFmpeg stores both import libs and DLLs in the install bin directory, so we copy _lib_dir (which is install/bin on Windows) to both stage -# dirs. -IF(RV_TARGET_WINDOWS) - RV_STAGE_DEPENDENCY_LIBS(TARGET ${_target} EXTRA_LIB_DIRS ${RV_STAGE_BIN_DIR} USE_FLAG_FILE) -ELSE() - RV_STAGE_DEPENDENCY_LIBS(TARGET ${_target} USE_FLAG_FILE) -ENDIF() +SET(_ffmpeg_targets) +FOREACH( + _ffmpeg_lib + ${_ffmpeg_libs} +) + LIST(APPEND _ffmpeg_targets ffmpeg::${_ffmpeg_lib}) +ENDFOREACH() + +RV_STAGE_DEPENDENCY_LIBS(TARGET ${_target} TARGET_LIBS ${_ffmpeg_targets}) SET(RV_DEPS_FFMPEG_VERSION ${_version} diff --git a/cmake/dependencies/glew.cmake b/cmake/dependencies/glew.cmake index 6401858fd..27693fb2b 100644 --- a/cmake/dependencies/glew.cmake +++ b/cmake/dependencies/glew.cmake @@ -6,14 +6,11 @@ RV_CREATE_STANDARD_DEPS_VARIABLES("RV_DEPS_GLEW" "${RV_DEPS_GLEW_VERSION}" "make" "") -SET(_download_url - "https://github.com/nigels-com/glew/archive/refs/tags/glew-${_version}.tar.gz" -) - -SET(_download_hash - ${RV_DEPS_GLEW_DOWNLOAD_HASH} -) +# --- Try to find installed package --- +# GLEW's CMake config does not ship a version file, so omit VERSION to avoid find_package failure. +RV_FIND_DEPENDENCY(TARGET ${_target} PACKAGE GLEW DEPS_LIST_TARGETS GLEW::GLEW) +# --- Library naming (shared between find and build paths) --- IF(RV_TARGET_DARWIN) SET(_glew_lib_name ${CMAKE_SHARED_LIBRARY_PREFIX}GLEW.${RV_DEPS_GLEW_VERSION_LIB}${CMAKE_SHARED_LIBRARY_SUFFIX} @@ -27,35 +24,29 @@ SET(_glew_lib ${_lib_dir}/${_glew_lib_name} ) -EXTERNALPROJECT_ADD( - ${_target} - SOURCE_DIR ${RV_DEPS_BASE_DIR}/${_target}/src - INSTALL_DIR ${_install_dir} - URL ${_download_url} - URL_MD5 ${_download_hash} - DOWNLOAD_NAME glew-glew-${_version}.tar.gz - DOWNLOAD_DIR ${RV_DEPS_DOWNLOAD_DIR} - CONFIGURE_COMMAND cd auto && ${_make_command} && cd .. && ${_make_command} - BUILD_COMMAND ${_make_command} -j${_cpu_count} GLEW_DEST=${_install_dir} - INSTALL_COMMAND ${_make_command} install LIBDIR=${_lib_dir} GLEW_DEST=${_install_dir} - BUILD_IN_SOURCE TRUE - BUILD_ALWAYS FALSE - BUILD_BYPRODUCTS ${_glew_lib} - USES_TERMINAL_BUILD TRUE -) +# --- Build from source if not found --- +IF(NOT ${_target}_FOUND) + INCLUDE(${CMAKE_CURRENT_LIST_DIR}/build/glew.cmake) -RV_ADD_IMPORTED_LIBRARY( - NAME - GLEW::GLEW - TYPE - STATIC - LOCATION - ${_glew_lib} - INCLUDE_DIRS - ${_include_dir} - DEPENDS - ${_target} - ADD_TO_DEPS_LIST -) + RV_STAGE_DEPENDENCY_LIBS(TARGET ${_target} OUTPUTS ${RV_STAGE_LIB_DIR}/${_glew_lib_name}) +ELSE() + # CONFIG found — GLEW::GLEW target already exists (created by glew-config.cmake from GLEW::glew). + IF(NOT TARGET GLEW::GLEW) + RV_ADD_IMPORTED_LIBRARY( + NAME + GLEW::GLEW + TYPE + SHARED + LOCATION + ${_glew_lib} + INCLUDE_DIRS + ${_include_dir} + DEPENDS + ${_target} + ADD_TO_DEPS_LIST + ) + ENDIF() -RV_STAGE_DEPENDENCY_LIBS(TARGET ${_target} OUTPUTS ${RV_STAGE_LIB_DIR}/${_glew_lib_name}) + # Found path: use TARGET_LIBS to resolve actual library path at build time + RV_STAGE_DEPENDENCY_LIBS(TARGET ${_target} TARGET_LIBS GLEW::GLEW) +ENDIF() diff --git a/cmake/dependencies/imath.cmake b/cmake/dependencies/imath.cmake index c82458665..50a0e1683 100644 --- a/cmake/dependencies/imath.cmake +++ b/cmake/dependencies/imath.cmake @@ -6,18 +6,7 @@ RV_CREATE_STANDARD_DEPS_VARIABLES("RV_DEPS_IMATH" "${RV_DEPS_IMATH_VERSION}" "" "") -SET(_download_url - "https://github.com/AcademySoftwareFoundation/Imath/archive/refs/tags/v${_version}.zip" -) - -SET(_download_hash - "${RV_DEPS_IMATH_DOWNLOAD_HASH}" -) - -SET(_include_dir - ${_install_dir}/include/Imath -) - +# --- Library naming (shared on all platforms, same filenames for find and build) --- IF(RV_TARGET_DARWIN) SET(_libname ${CMAKE_SHARED_LIBRARY_PREFIX}Imath-${RV_DEPS_IMATH_LIB_MAJOR}${RV_DEBUG_POSTFIX}.${RV_DEPS_IMATH_LIB_VER}${CMAKE_SHARED_LIBRARY_SUFFIX} @@ -32,62 +21,61 @@ ELSEIF(RV_TARGET_WINDOWS) ) ENDIF() +# --- Try to find installed package --- +RV_FIND_DEPENDENCY( + TARGET + ${_target} + PACKAGE + Imath + VERSION + ${RV_DEPS_IMATH_VERSION} + DEPS_LIST_TARGETS + Imath::Imath +) + +# Compute paths AFTER RV_FIND_DEPENDENCY (which may override _lib_dir) +SET(_libpath + ${_lib_dir}/${_libname} +) + SET(RV_DEPS_IMATH_CMAKE_DIR ${_lib_dir}/cmake/Imath CACHE STRING "Path to Imath CMake files ${_target}" ) - SET(RV_DEPS_IMATH_CMAKE_DIR ${_lib_dir}/cmake/Imath ) -SET(_libpath - ${_lib_dir}/${_libname} -) - -LIST(APPEND _imath_byproducts ${_libpath}) - IF(RV_TARGET_WINDOWS) SET(_implibpath ${_install_dir}/lib/${CMAKE_IMPORT_LIBRARY_PREFIX}Imath-${RV_DEPS_IMATH_LIB_MAJOR}${RV_DEBUG_POSTFIX}${CMAKE_IMPORT_LIBRARY_SUFFIX} ) - LIST(APPEND _imath_byproducts ${_implibpath}) ENDIF() -EXTERNALPROJECT_ADD( - ${_target} - URL ${_download_url} - URL_MD5 ${_download_hash} - DOWNLOAD_NAME ${_target}_${_version}.zip - DOWNLOAD_DIR ${RV_DEPS_DOWNLOAD_DIR} - DOWNLOAD_EXTRACT_TIMESTAMP TRUE - SOURCE_DIR ${RV_DEPS_BASE_DIR}/${_target}/src - INSTALL_DIR ${_install_dir} - CONFIGURE_COMMAND ${CMAKE_COMMAND} ${_configure_options} - BUILD_COMMAND ${_cmake_build_command} - INSTALL_COMMAND ${_cmake_install_command} - BUILD_IN_SOURCE TRUE - BUILD_ALWAYS FALSE - BUILD_BYPRODUCTS ${_imath_byproducts} - USES_TERMINAL_BUILD TRUE -) +# --- Build from source if not found --- +IF(NOT ${_target}_FOUND) + INCLUDE(${CMAKE_CURRENT_LIST_DIR}/build/imath.cmake) +ELSE() + # CONFIG found — Imath::Imath target exists with proper LOCATION. For pkg-config (unlikely), create proper imported target. + IF(NOT TARGET Imath::Imath) + RV_ADD_IMPORTED_LIBRARY( + NAME + Imath::Imath + TYPE + SHARED + LOCATION + ${_libpath} + SONAME + ${_libname} + IMPLIB + ${_implibpath} + INCLUDE_DIRS + ${_include_dir} + DEPENDS + ${_target} + ) + ENDIF() +ENDIF() +# --- Staging (shared — same library name for both paths) --- RV_STAGE_DEPENDENCY_LIBS(TARGET ${_target} LIBNAME ${_libname}) - -RV_ADD_IMPORTED_LIBRARY( - NAME - Imath::Imath - TYPE - SHARED - LOCATION - ${_libpath} - SONAME - ${_libname} - IMPLIB - ${_implibpath} - INCLUDE_DIRS - ${_include_dir} - DEPENDS - ${_target} - ADD_TO_DEPS_LIST -) diff --git a/cmake/dependencies/ocio.cmake b/cmake/dependencies/ocio.cmake index 99c77312c..333c76c00 100644 --- a/cmake/dependencies/ocio.cmake +++ b/cmake/dependencies/ocio.cmake @@ -52,6 +52,12 @@ IF(RV_TARGET_WINDOWS) ${_bin_dir}/${_ocio_win_sharedlibname} ) LIST(APPEND _byproducts ${_ocio_win_sharedlib_path}) + + # Fix _libpath to match the actual version-suffixed DLL name that OCIO produces. RV_MAKE_STANDARD_LIB_NAME generates "OpenColorIO.dll" but OCIO builds + # "OpenColorIO_2.3.dll". + SET(_libpath + ${_ocio_win_sharedlib_path} + ) ENDIF() IF(RV_TARGET_WINDOWS) @@ -138,8 +144,9 @@ IF(NOT RV_TARGET_WINDOWS) ENDIF() LIST(APPEND _configure_options "-DOCIO_PYTHON_VERSION=${RV_DEPS_PYTHON_VERSION_SHORT}") -# Using Imath_ROOT because Imath_DIR does not seems to be enough on UNIX-based platform (at least Rocky linux). -LIST(APPEND _configure_options "-DImath_ROOT=${RV_DEPS_IMATH_ROOT_DIR}") +# Use explicit Imath_DIR for precise config resolution. Works for both built-from-source and found (e.g. Homebrew) packages. Use RV_DEPS_IMATH_CMAKE_DIR which +# accounts for lib vs lib64 (RHEL) rather than hardcoding lib/. +LIST(APPEND _configure_options "-DImath_DIR=${RV_DEPS_IMATH_CMAKE_DIR}") LIST(APPEND _configure_options "-DZLIB_ROOT=${RV_DEPS_ZLIB_ROOT_DIR}") @@ -202,7 +209,7 @@ ELSE() # Windows "-DZLIB_LIBRARY=${_zlib_library}" "-DZLIB_INCLUDE_DIR=${_zlib_include_dir}" "-Dexpat_ROOT=${RV_DEPS_EXPAT_ROOT_DIR}" - "-DImath_DIR=${RV_DEPS_IMATH_ROOT_DIR}/lib/cmake/Imath" + "-DImath_DIR=${RV_DEPS_IMATH_CMAKE_DIR}" "-DPython_ROOT=${RV_DEPS_BASE_DIR}/RV_DEPS_PYTHON3/install" # Mandatory param: OCIO CMake code finds Python. "-DPython_LIBRARY=${RV_DEPS_BASE_DIR}/RV_DEPS_PYTHON3/install/bin/python${PYTHON_VERSION_SHORT_NO_DOT}.lib" # with this param @@ -319,8 +326,6 @@ IF(RV_TARGET_WINDOWS) ENDIF() ENDIF() -RV_STAGE_DEPENDENCY_LIBS(TARGET ${_target} BIN_DIR ${_bin_dir} USE_FLAG_FILE) - RV_ADD_IMPORTED_LIBRARY( NAME OpenColorIO::OpenColorIO @@ -336,3 +341,5 @@ RV_ADD_IMPORTED_LIBRARY( ${_target} ADD_TO_DEPS_LIST ) + +RV_STAGE_DEPENDENCY_LIBS(TARGET ${_target} TARGET_LIBS OpenColorIO::OpenColorIO) diff --git a/cmake/dependencies/oiio.cmake b/cmake/dependencies/oiio.cmake index c364604f7..1c03af7c8 100644 --- a/cmake/dependencies/oiio.cmake +++ b/cmake/dependencies/oiio.cmake @@ -52,20 +52,11 @@ LIST(APPEND _configure_options "-DUSE_OCIO=0") LIST(APPEND _configure_options "-DUSE_FREETYPE=0") LIST(APPEND _configure_options "-DUSE_GIF=OFF") -LIST(APPEND _configure_options "-DBoost_ROOT=${RV_DEPS_BOOST_ROOT_DIR}") +# Use explicit *_DIR variables pointing directly to config file directories for more precise package resolution. Use RV_DEPS_*_CMAKE_DIR which accounts for lib +# vs lib64 (RHEL) rather than hardcoding lib/. +LIST(APPEND _configure_options "-DBoost_DIR=${RV_DEPS_BOOST_ROOT_DIR}/lib/cmake/Boost-${RV_DEPS_BOOST_VERSION}") LIST(APPEND _configure_options "-DOpenEXR_ROOT=${RV_DEPS_OPENEXR_ROOT_DIR}") - -IF(NOT RV_TARGET_WINDOWS) - GET_TARGET_PROPERTY(_imath_library Imath::Imath IMPORTED_LOCATION) - GET_TARGET_PROPERTY(_imath_include_dir Imath::Imath INTERFACE_INCLUDE_DIRECTORIES) - LIST(APPEND _configure_options "-DImath_LIBRARY=${_imath_library}") - LIST(APPEND _configure_options "-DImath_INCLUDE_DIR=${_imath_include_dir}/..") - GET_FILENAME_COMPONENT(_imath_library_path ${_imath_library} DIRECTORY) - LIST(APPEND _configure_options "-DImath_DIR=${_imath_library_path}/cmake/Imath") -ELSE() - # Must point to the IMath-config.cmake file which is a 'FindIMath.cmake' type of file. - LIST(APPEND _configure_options "-DImath_DIR=${RV_DEPS_IMATH_ROOT_DIR}/lib/cmake/Imath") -ENDIF() +LIST(APPEND _configure_options "-DImath_DIR=${RV_DEPS_IMATH_CMAKE_DIR}") GET_TARGET_PROPERTY(_png_library PNG::PNG IMPORTED_LOCATION) GET_TARGET_PROPERTY(_png_include_dir PNG::PNG INTERFACE_INCLUDE_DIRECTORIES) @@ -94,7 +85,14 @@ LIST(APPEND _configure_options "-DOPENJPEG_INCLUDE_DIR=${_openjpeg_include_dir}" LIST(APPEND _configure_options "-DTIFF_ROOT=${RV_DEPS_TIFF_ROOT_DIR}") -LIST(APPEND _configure_options "-USE_FFMPEG=0") +LIST(APPEND _configure_options "-DUSE_FFMPEG=0") + +# When Boost is built from source but other deps come from a shared prefix (e.g. Homebrew), their transitive -isystem includes can pull in a newer system +# Boost's headers, causing ABI mismatches at link time. Adding Boost's include as a non-system -I flag ensures it takes precedence over any -isystem paths, +# since compilers (GCC/Clang) always search -I before -isystem. +IF(RV_DEPS_IGNORE_PREFIXES) + LIST(APPEND _configure_options "-DCMAKE_CXX_FLAGS=-I${RV_DEPS_BOOST_ROOT_DIR}/include") +ENDIF() IF(RV_TARGET_LINUX) MESSAGE(STATUS "Building OpenImageIO using system's freetype library.") diff --git a/cmake/dependencies/openexr.cmake b/cmake/dependencies/openexr.cmake index d90318144..c7ec97d43 100644 --- a/cmake/dependencies/openexr.cmake +++ b/cmake/dependencies/openexr.cmake @@ -6,23 +6,23 @@ RV_CREATE_STANDARD_DEPS_VARIABLES("RV_DEPS_OPENEXR" "${RV_DEPS_OPENEXR_VERSION}" "" "") -SET(_make_command - make +SET(_openexr_deps_list_targets + OpenEXR::OpenEXR OpenEXR::IlmThread OpenEXR::Iex ) -IF(${RV_OSX_EMULATION}) - SET(_darwin_x86_64 - "arch" "${RV_OSX_EMULATION_ARCH}" - ) - SET(_make_command - ${_darwin_x86_64} ${_make_command} - ) -ENDIF() -IF(RV_TARGET_WINDOWS) - # MSYS2/CMake defaults to Ninja - SET(_make_command - ninja - ) -ENDIF() + +# --- Try to find installed package --- +RV_FIND_DEPENDENCY( + TARGET + ${_target} + PACKAGE + OpenEXR + VERSION + ${RV_DEPS_OPENEXR_VERSION} + DEPS_LIST_TARGETS + ${_openexr_deps_list_targets} +) + +# --- Library naming (shared between find and build paths) --- SET(_openexr_libname_suffix_ "${RV_DEPS_OPENEXR_LIBNAME_SUFFIX}" ) @@ -114,140 +114,60 @@ IF(RV_TARGET_WINDOWS) LIST(APPEND _openexr_byproducts ${_openexr_implib} ${_ilmthread_implib} ${_iex_implib}) ENDIF() -SET(_openexr_patch_file_ - "${CMAKE_CURRENT_SOURCE_DIR}/patch/${RV_DEPS_OPENEXR_PATCH_NAME}.patch" -) - -IF(EXISTS "${_openexr_patch_file_}") - SET(_patch_command - patch -p1 < ${_openexr_patch_file_} - ) - MESSAGE(STATUS "Patch command set for ${_target}: ${_patch_command}") +# --- Build from source if not found --- +IF(NOT ${_target}_FOUND) + INCLUDE(${CMAKE_CURRENT_LIST_DIR}/build/openexr.cmake) + + # Build path: we control the filenames, use OUTPUTS for precise tracking + IF(RV_TARGET_WINDOWS) + RV_STAGE_DEPENDENCY_LIBS( + TARGET + ${_target} + BIN_DIR + ${_bin_dir} + OUTPUTS + ${RV_STAGE_BIN_DIR}/${_openexr_name} + ${RV_STAGE_BIN_DIR}/${_openexrcore_name} + ${RV_STAGE_BIN_DIR}/${_ilmthread_name} + ${RV_STAGE_BIN_DIR}/${_iex_name} + ) + ELSE() + RV_STAGE_DEPENDENCY_LIBS( + TARGET ${_target} OUTPUTS ${RV_STAGE_LIB_DIR}/${_openexrcore_name} ${RV_STAGE_LIB_DIR}/${_ilmthread_name} ${RV_STAGE_LIB_DIR}/${_iex_name} + ) + ENDIF() ELSE() - # If it does not exist, set the command to an empty string. ExternalProject_Add will skip the patch step if the command is empty. - SET(_patch_command - "" - ) - MESSAGE(STATUS "ERROR Patch file not found, skipping patch for ${_target}: ${_patch_file}") + # CONFIG found — OpenEXR::xxx targets already exist. Create any missing targets as a safety net. + FOREACH( + _exr_target + ${_openexr_deps_list_targets} + ) + IF(NOT TARGET ${_exr_target}) + RV_ADD_IMPORTED_LIBRARY( + NAME + ${_exr_target} + TYPE + SHARED + LOCATION + ${_lib_dir}/${_openexr_name} + SONAME + ${_openexr_name} + INCLUDE_DIRS + ${_include_dir} + DEPENDS + ${_target} + ) + ENDIF() + ENDFOREACH() + + # Found path: actual filenames may differ, use TARGET_LIBS to resolve at build time. Include OpenEXRCore which exists when found via CONFIG but has no + # imported target in build path. + SET(_openexr_stage_targets + ${_openexr_deps_list_targets} + ) + IF(TARGET OpenEXR::OpenEXRCore) + LIST(APPEND _openexr_stage_targets OpenEXR::OpenEXRCore) + ENDIF() + + RV_STAGE_DEPENDENCY_LIBS(TARGET ${_target} TARGET_LIBS ${_openexr_stage_targets}) ENDIF() - -LIST(APPEND _configure_options "-DCMAKE_PREFIX_PATH=${RV_DEPS_IMATH_CMAKE_DIR}") -LIST(APPEND _configure_options "-DBUILD_TESTING=OFF") -IF(RV_TARGET_WINDOWS) - GET_TARGET_PROPERTY(_zlib_implibpath ZLIB::ZLIB IMPORTED_IMPLIB) - LIST(APPEND _configure_options "-DZLIB_INCLUDE_DIR=${RV_DEPS_ZLIB_INCLUDE_DIR}") - LIST(APPEND _configure_options "-DZLIB_LIBRARY=${_zlib_implibpath}") -ENDIF() - -# OpenEXR tools are not needed. -LIST(APPEND _configure_options "-DOPENEXR_BUILD_TOOLS=OFF") - -# Disable OpenEXR's automatic rpath setup to avoid conflicts with RV's rpath management OpenEXR 3.3+ automatically adds @loader_path/../lib which conflicts with -# our install scripts -LIST(APPEND _configure_options "-DCMAKE_INSTALL_RPATH=") - -EXTERNALPROJECT_ADD( - ${_target} - URL "https://github.com/AcademySoftwareFoundation/openexr/archive/refs/tags/v${_version}.zip" - URL_MD5 ${RV_DEPS_OPENEXR_DOWNLOAD_HASH} - DOWNLOAD_NAME ${_target}_${_version}.zip - DOWNLOAD_DIR ${RV_DEPS_DOWNLOAD_DIR} - DOWNLOAD_EXTRACT_TIMESTAMP TRUE - SOURCE_DIR ${RV_DEPS_BASE_DIR}/${_target}/src - DEPENDS Imath::Imath ZLIB::ZLIB - INSTALL_DIR ${_install_dir} - PATCH_COMMAND ${_patch_command} - CONFIGURE_COMMAND ${CMAKE_COMMAND} ${_configure_options} - BUILD_COMMAND ${_cmake_build_command} - INSTALL_COMMAND ${_cmake_install_command} - BUILD_IN_SOURCE TRUE - BUILD_ALWAYS FALSE - BUILD_BYPRODUCTS ${_openexr_byproducts} - USES_TERMINAL_BUILD TRUE -) - -SET(_include_dir - ${_install_dir}/include/OpenEXR -) - -FILE(MAKE_DIRECTORY ${_include_dir}) - -IF(RV_TARGET_WINDOWS) - RV_STAGE_DEPENDENCY_LIBS( - TARGET - ${_target} - BIN_DIR - ${_bin_dir} - OUTPUTS - ${RV_STAGE_BIN_DIR}/${_openexr_name} - ${RV_STAGE_BIN_DIR}/${_openexrcore_name} - ${RV_STAGE_BIN_DIR}/${_ilmthread_name} - ${RV_STAGE_BIN_DIR}/${_iex_name} - ) -ELSE() - RV_STAGE_DEPENDENCY_LIBS( - TARGET ${_target} OUTPUTS ${RV_STAGE_LIB_DIR}/${_openexrcore_name} ${RV_STAGE_LIB_DIR}/${_ilmthread_name} ${RV_STAGE_LIB_DIR}/${_iex_name} - ) -ENDIF() - -ADD_LIBRARY(OpenEXR::IlmThread SHARED IMPORTED GLOBAL) -SET_PROPERTY( - TARGET OpenEXR::IlmThread - PROPERTY IMPORTED_LOCATION ${_ilmthread_lib} -) -SET_PROPERTY( - TARGET OpenEXR::IlmThread - PROPERTY IMPORTED_SONAME ${_ilmthread_name} -) -IF(RV_TARGET_WINDOWS) - SET_PROPERTY( - TARGET OpenEXR::IlmThread - PROPERTY IMPORTED_IMPLIB ${_ilmthread_implib} - ) -ENDIF() -LIST(APPEND RV_DEPS_LIST OpenEXR::IlmThread) - -ADD_LIBRARY(OpenEXR::Iex SHARED IMPORTED GLOBAL) -SET_PROPERTY( - TARGET OpenEXR::Iex - PROPERTY IMPORTED_LOCATION ${_iex_lib} -) -SET_PROPERTY( - TARGET OpenEXR::Iex - PROPERTY IMPORTED_SONAME ${_iex_name} -) -IF(RV_TARGET_WINDOWS) - SET_PROPERTY( - TARGET OpenEXR::Iex - PROPERTY IMPORTED_IMPLIB ${_iex_implib} - ) -ENDIF() -LIST(APPEND RV_DEPS_LIST OpenEXR::Iex) - -ADD_LIBRARY(OpenEXR::OpenEXR SHARED IMPORTED GLOBAL) -SET_PROPERTY( - TARGET OpenEXR::OpenEXR - PROPERTY IMPORTED_LOCATION ${_openexr_lib} -) -SET_PROPERTY( - TARGET OpenEXR::OpenEXR - PROPERTY IMPORTED_SONAME ${_openexr_name} -) -IF(RV_TARGET_WINDOWS) - SET_PROPERTY( - TARGET OpenEXR::OpenEXR - PROPERTY IMPORTED_IMPLIB ${_openexr_implib} - ) -ENDIF() - -TARGET_INCLUDE_DIRECTORIES( - OpenEXR::OpenEXR - INTERFACE ${_include_dir} -) -TARGET_LINK_LIBRARIES( - OpenEXR::OpenEXR - INTERFACE Imath::Imath OpenEXR::Iex OpenEXR::IlmThread ZLIB::ZLIB -) -LIST(APPEND RV_DEPS_LIST OpenEXR::OpenEXR) - -ADD_DEPENDENCIES(OpenEXR::OpenEXR ${_target}) diff --git a/cmake/dependencies/openssl.cmake b/cmake/dependencies/openssl.cmake index 43ab27dfa..ff94a7c60 100644 --- a/cmake/dependencies/openssl.cmake +++ b/cmake/dependencies/openssl.cmake @@ -4,18 +4,12 @@ # SPDX-License-Identifier: Apache-2.0 # -SET(_target - "RV_DEPS_OPENSSL" -) -SET(_version - ${RV_DEPS_OPENSSL_VERSION} -) +RV_CREATE_STANDARD_DEPS_VARIABLES("RV_DEPS_OPENSSL" "${RV_DEPS_OPENSSL_VERSION}" "" "") IF(RV_TARGET_IS_RHEL8 AND RV_VFX_PLATFORM STREQUAL CY2023 ) - # VFX2023: Rocky Linux 8 - + # VFX2023 on Rocky Linux 8: use system OpenSSL 1.1.1 FIND_PACKAGE(OpenSSL 1.1.1 REQUIRED) SET(RV_DEPS_OPENSSL_VERSION @@ -28,125 +22,38 @@ IF(RV_TARGET_IS_RHEL8 ) GET_FILENAME_COMPONENT(_lib_dir "${OPENSSL_SSL_LIBRARY}" DIRECTORY) - MESSAGE(STATUS "_include_dir ${_include_dir}") - MESSAGE(STATUS "_lib_dir ${_lib_dir}") - ELSE() - # VFX2023: Rocky Linux 9, Windows and MacOS VFX2024: Rocky Linux 8/9, Windows and MacOS - - SET(RV_DEPS_WIN_PERL_ROOT - "" - CACHE STRING "Path to Windows perl root" - ) - - SET(_target - "RV_DEPS_OPENSSL" + # All other platforms: find-first dispatcher. Tries CONFIG, then MODULE (FindOpenSSL.cmake), then pkg-config. On systems where OpenSSL is not on the default + # search path (e.g., keg-only on macOS, custom install on Linux/Windows), hint with CMAKE_PREFIX_PATH, OPENSSL_ROOT_DIR, or PKG_CONFIG_PATH. + RV_FIND_DEPENDENCY( + TARGET + ${_target} + PACKAGE + OpenSSL + VERSION + ${_version} + PKG_CONFIG_NAME + openssl + ALLOW_MODULE + DEPS_LIST_TARGETS + OpenSSL::Crypto + OpenSSL::SSL ) - STRING(REPLACE "." "_" _version_underscored ${_version}) - - IF(RV_TARGET_WINDOWS - AND (NOT RV_DEPS_WIN_PERL_ROOT - OR RV_DEPS_WIN_PERL_ROOT STREQUAL "") + SET(_download_url + "https://github.com/openssl/openssl/releases/download/openssl-${_version}/openssl-${_version}.tar.gz" ) - MESSAGE( - FATAL_ERROR - "Unable to build without a RV_DEPS_WIN_PERL_ROOT. OpenSSL requires a Windows native perl interpreter to build (it recommends https://strawberryperl.com/). Example -DRV_DEPS_WIN_PERL_ROOT=c:/Strawberry/perl/bin" - ) - ENDIF() - - SET(RV_DEPS_OPENSSL_INSTALL_DIR - ${RV_DEPS_BASE_DIR}/${_target}/install - ) - SET(_include_dir - ${RV_DEPS_OPENSSL_INSTALL_DIR}/include - ) - SET(_source_dir - ${RV_DEPS_BASE_DIR}/${_target}/src - ) - SET(_build_dir - ${RV_DEPS_BASE_DIR}/${_target}/build - ) - - IF(RHEL_VERBOSE) - IF(RV_VFX_PLATFORM STREQUAL "CY2023") - SET(_lib_dir - "${RV_DEPS_OPENSSL_INSTALL_DIR}/lib" - ) - ELSEIF(RV_VFX_PLATFORM STRGREATER_EQUAL "CY2024") - SET(_lib_dir - "${RV_DEPS_OPENSSL_INSTALL_DIR}/lib64" - ) - ENDIF() - ELSE() - SET(_lib_dir - ${RV_DEPS_OPENSSL_INSTALL_DIR}/lib - ) - ENDIF() - - SET(_bin_dir - ${RV_DEPS_OPENSSL_INSTALL_DIR}/bin - ) - IF(${_version} STREQUAL "1.1.1u") + STRING(REPLACE "." "_" _version_underscored ${_version}) SET(_download_url "https://github.com/openssl/openssl/releases/download/OpenSSL_${_version_underscored}/openssl-${_version}.tar.gz" ) - ELSE() - SET(_download_url - "https://github.com/openssl/openssl/releases/download/openssl-${_version}/openssl-${_version}.tar.gz" - ) ENDIF() - SET(_download_hash ${RV_DEPS_OPENSSL_HASH} ) - SET(_make_command_script - "${PROJECT_SOURCE_DIR}/src/build/make_openssl.py" - ) - SET(_make_command - python3 "${_make_command_script}" - ) - - LIST(APPEND _make_command "--source-dir") - LIST(APPEND _make_command ${_source_dir}) - LIST(APPEND _make_command "--output-dir") - LIST(APPEND _make_command ${RV_DEPS_OPENSSL_INSTALL_DIR}) - - LIST(APPEND _make_command "--vfx_platform") - - LIST(APPEND _make_command ${RV_VFX_CY_YEAR}) - - IF(RV_TARGET_WINDOWS) - LIST(APPEND _make_command "--perlroot") - LIST(APPEND _make_command ${RV_DEPS_WIN_PERL_ROOT}) - ENDIF() - - IF(APPLE) - # This is needed because if Rosetta is used to compile for x86_64 from ARM64, openssl build system detects it as "linux-x86_64" and it causes issues. - - IF(RV_TARGET_APPLE_X86_64) - SET(__openssl_arch__ - x86_64 - ) - ELSEIF(RV_TARGET_APPLE_ARM64) - SET(__openssl_arch__ - arm64 - ) - ENDIF() - - LIST(APPEND _make_command --arch=-${__openssl_arch__}) - ENDIF() - - # On most POSIX platforms, shared libraries are named `libcrypto.so.1.1` and `libssl.so.1.1`. - - # On Windows build with MSVC or using MingW, shared libraries are named `libcrypto-1_1.dll` and `libssl-1_1.dll` for 32-bit Windows, `libcrypto-1_1-x64.dll` - # and `libssl-1_1-x64.dll` for 64-bit x86_64 Windows, and `libcrypto-1_1-ia64.dll` and `libssl-1_1-ia64.dll` for IA64 Windows. With MSVC, the import libraries - # are named `libcrypto.lib` and `libssl.lib`, while with MingW, they are named `libcrypto.dll.a` and `libssl.dll.a`. - - # Ref: https://github.com/openssl/openssl/blob/398011848468c7e8e481b295f7904afc30934217/INSTALL.md?plain=1#L1847-L1858 - + # Shared naming logic (used by both build and found paths) SET(_dot_version ${RV_DEPS_OPENSSL_VERSION_DOT} ) @@ -158,12 +65,10 @@ ELSE() SET(_crypto_lib_name ${CMAKE_SHARED_LIBRARY_PREFIX}crypto${CMAKE_SHARED_LIBRARY_SUFFIX}${_dot_version} ) - SET(_ssl_lib_name ${CMAKE_SHARED_LIBRARY_PREFIX}ssl${CMAKE_SHARED_LIBRARY_SUFFIX}${_dot_version} ) ELSEIF(RV_TARGET_WINDOWS) - # As stated in the openssl documentation, the names are libcrypto-1_1-x64 and libssl-1_1-x64 when OpenSSL is build with MSVC. SET(_crypto_lib_name libcrypto-${_underscore_version}-x64${CMAKE_SHARED_LIBRARY_SUFFIX} ) @@ -182,7 +87,6 @@ ELSE() SET(_crypto_lib ${_lib_dir}/${_crypto_lib_name} ) - SET(_ssl_lib ${_lib_dir}/${_ssl_lib_name} ) @@ -191,108 +95,77 @@ ELSE() SET(_implibpath_crypto ${_lib_dir}/${CMAKE_IMPORT_LIBRARY_PREFIX}crypto${CMAKE_IMPORT_LIBRARY_SUFFIX} ) - SET(_implibpath_ssl ${_lib_dir}/${CMAKE_IMPORT_LIBRARY_PREFIX}ssl${CMAKE_IMPORT_LIBRARY_SUFFIX} ) ENDIF() - EXTERNALPROJECT_ADD( - ${_target} - DOWNLOAD_NAME ${_target}_${_version}.zip - DOWNLOAD_DIR ${RV_DEPS_DOWNLOAD_DIR} - DOWNLOAD_EXTRACT_TIMESTAMP TRUE - SOURCE_DIR ${_source_dir} - INSTALL_DIR ${RV_DEPS_OPENSSL_INSTALL_DIR} - URL ${_download_url} - URL_MD5 ${_download_hash} - CONFIGURE_COMMAND ${_make_command} --configure - BUILD_COMMAND ${_make_command} --build - INSTALL_COMMAND ${_make_command} --install - BUILD_IN_SOURCE TRUE - BUILD_ALWAYS FALSE - BUILD_BYPRODUCTS ${_crypto_lib} ${_ssl_lib} - USES_TERMINAL_BUILD TRUE - ) - - FILE(MAKE_DIRECTORY ${_include_dir}) - - ADD_LIBRARY(OpenSSL::Crypto SHARED IMPORTED GLOBAL) - ADD_DEPENDENCIES(OpenSSL::Crypto ${_target}) - SET_PROPERTY( - TARGET OpenSSL::Crypto - PROPERTY IMPORTED_LOCATION ${_crypto_lib} - ) - SET_PROPERTY( - TARGET OpenSSL::Crypto - PROPERTY IMPORTED_SONAME ${_crypto_lib_name} - ) - IF(RV_TARGET_WINDOWS) - SET_PROPERTY( - TARGET OpenSSL::Crypto - PROPERTY IMPORTED_IMPLIB ${_implibpath_crypto} - ) - ENDIF() - TARGET_INCLUDE_DIRECTORIES( - OpenSSL::Crypto - INTERFACE ${_include_dir} - ) - LIST(APPEND RV_DEPS_LIST OpenSSL::Crypto) - - ADD_LIBRARY(OpenSSL::SSL SHARED IMPORTED GLOBAL) - ADD_DEPENDENCIES(OpenSSL::SSL ${_target}) - SET_PROPERTY( - TARGET OpenSSL::SSL - PROPERTY IMPORTED_LOCATION ${_ssl_lib} - ) - SET_PROPERTY( - TARGET OpenSSL::SSL - PROPERTY IMPORTED_SONAME ${_ssl_lib_name} - ) - IF(RV_TARGET_WINDOWS) - SET_PROPERTY( - TARGET OpenSSL::SSL - PROPERTY IMPORTED_IMPLIB ${_implibpath_ssl} - ) - ENDIF() - TARGET_INCLUDE_DIRECTORIES( - OpenSSL::SSL - INTERFACE ${_include_dir} - ) - LIST(APPEND RV_DEPS_LIST OpenSSL::SSL) - - SET(_openssl_stage_lib_dir - ${RV_STAGE_LIB_DIR} - ) - - IF(RV_TARGET_WINDOWS) - ADD_CUSTOM_COMMAND( - TARGET ${_target} - POST_BUILD - COMMENT "Renaming the openssl import libs to the name FFmpeg is expecting" - COMMAND ${CMAKE_COMMAND} -E copy ${RV_DEPS_OPENSSL_INSTALL_DIR}/lib/libssl.lib ${_lib_dir}/ssl.lib - COMMAND ${CMAKE_COMMAND} -E copy ${RV_DEPS_OPENSSL_INSTALL_DIR}/lib/libcrypto.lib ${_lib_dir}/crypto.lib - ) - # Copy import libs to stage lib dir and specific DLLs to stage bin dir - ADD_CUSTOM_COMMAND( - COMMENT "Staging ${_target} libs into ${RV_STAGE_LIB_DIR} and ${RV_STAGE_BIN_DIR}" - OUTPUT ${RV_STAGE_BIN_DIR}/${_crypto_lib_name} ${RV_STAGE_BIN_DIR}/${_ssl_lib_name} - COMMAND ${CMAKE_COMMAND} -E copy_directory ${_lib_dir} ${RV_STAGE_LIB_DIR} - COMMAND ${CMAKE_COMMAND} -E copy ${_bin_dir}/${_crypto_lib_name} ${RV_STAGE_BIN_DIR} - COMMAND ${CMAKE_COMMAND} -E copy ${_bin_dir}/${_ssl_lib_name} ${RV_STAGE_BIN_DIR} - DEPENDS ${_target} - ) - ADD_CUSTOM_TARGET( - ${_target}-stage-target ALL - DEPENDS ${RV_STAGE_BIN_DIR}/${_crypto_lib_name} ${RV_STAGE_BIN_DIR}/${_ssl_lib_name} - ) - ADD_DEPENDENCIES(dependencies ${_target}-stage-target) + IF(NOT ${_target}_FOUND) + INCLUDE(${CMAKE_CURRENT_LIST_DIR}/build/openssl.cmake) + + IF(RV_TARGET_WINDOWS) + ADD_CUSTOM_COMMAND( + COMMENT "Staging ${_target} libs into ${RV_STAGE_LIB_DIR} and ${RV_STAGE_BIN_DIR}" + OUTPUT ${RV_STAGE_BIN_DIR}/${_crypto_lib_name} ${RV_STAGE_BIN_DIR}/${_ssl_lib_name} + COMMAND ${CMAKE_COMMAND} -E copy_directory ${_lib_dir} ${RV_STAGE_LIB_DIR} + COMMAND ${CMAKE_COMMAND} -E copy ${_bin_dir}/${_crypto_lib_name} ${RV_STAGE_BIN_DIR} + COMMAND ${CMAKE_COMMAND} -E copy ${_bin_dir}/${_ssl_lib_name} ${RV_STAGE_BIN_DIR} + DEPENDS ${_target} + ) + ADD_CUSTOM_TARGET( + ${_target}-stage-target ALL + DEPENDS ${RV_STAGE_BIN_DIR}/${_crypto_lib_name} ${RV_STAGE_BIN_DIR}/${_ssl_lib_name} + ) + ADD_DEPENDENCIES(dependencies ${_target}-stage-target) + ELSE() + SET(_openssl_stage_lib_dir + ${RV_STAGE_LIB_DIR} + ) + IF(RV_TARGET_LINUX) + SET(_openssl_stage_lib_dir + ${_openssl_stage_lib_dir}/OpenSSL + ) + ENDIF() + + RV_STAGE_DEPENDENCY_LIBS( + TARGET + ${_target} + STAGE_LIB_DIR + ${_openssl_stage_lib_dir} + OUTPUTS + ${_openssl_stage_lib_dir}/${_crypto_lib_name} + ${_openssl_stage_lib_dir}/${_ssl_lib_name} + ) + ENDIF() ELSE() - - # Because RHEL8 has the same version of openssl library as we use but is not compatible with our library, we will copy openssl into its own seperate lib - # directory and conditionally add it to the LD_LIBRARY_PATH if the version we build does not match the system version. This will allow RHEL8 to use its own - # system version - # + FOREACH( + _ssl_target + OpenSSL::Crypto OpenSSL::SSL + ) + IF(NOT TARGET ${_ssl_target}) + STRING( + REGEX + REPLACE ".*::" "" _ssl_short ${_ssl_target} + ) + STRING(TOLOWER ${_ssl_short} _ssl_lower) + RV_ADD_IMPORTED_LIBRARY( + NAME + ${_ssl_target} + TYPE + SHARED + LOCATION + ${_lib_dir}/lib${_ssl_lower}${CMAKE_SHARED_LIBRARY_SUFFIX} + INCLUDE_DIRS + ${_include_dir} + DEPENDS + ${_target} + ) + ENDIF() + ENDFOREACH() + + SET(_openssl_stage_lib_dir + ${RV_STAGE_LIB_DIR} + ) IF(RV_TARGET_LINUX) SET(_openssl_stage_lib_dir ${_openssl_stage_lib_dir}/OpenSSL @@ -304,12 +177,16 @@ ELSE() ${_target} STAGE_LIB_DIR ${_openssl_stage_lib_dir} - OUTPUTS - ${_openssl_stage_lib_dir}/${_crypto_lib_name} - ${_openssl_stage_lib_dir}/${_ssl_lib_name} + TARGET_LIBS + OpenSSL::Crypto + OpenSSL::SSL ) ENDIF() + SET(RV_DEPS_OPENSSL_INSTALL_DIR + ${_install_dir} + ) + SET(RV_DEPS_OPENSSL_VERSION ${_version} CACHE INTERNAL "" FORCE diff --git a/cmake/dependencies/pcre2.cmake b/cmake/dependencies/pcre2.cmake index 1c8dc1474..c1352e139 100644 --- a/cmake/dependencies/pcre2.cmake +++ b/cmake/dependencies/pcre2.cmake @@ -17,18 +17,30 @@ SET(_download_hash # PCRE is not used for Linux and MacOS (Boost regex is used) in the current code. IF(RV_TARGET_WINDOWS) + # PCRE2's CMakeLists.txt sets CMAKE_DEBUG_POSTFIX to "d" internally, which cannot be overridden via cache variable. Account for it here. + IF(CMAKE_BUILD_TYPE MATCHES "^Debug$") + SET(_pcre2_debug_postfix + "d" + ) + ELSE() + SET(_pcre2_debug_postfix + "" + ) + ENDIF() + + # MSVC library naming (CMake build) SET(_pcre2_libname - libpcre2-8-0${CMAKE_SHARED_LIBRARY_SUFFIX} + ${CMAKE_SHARED_LIBRARY_PREFIX}pcre2-8${_pcre2_debug_postfix}${CMAKE_SHARED_LIBRARY_SUFFIX} ) SET(_pcre2_libname_posix - libpcre2-posix-3${CMAKE_SHARED_LIBRARY_SUFFIX} + ${CMAKE_SHARED_LIBRARY_PREFIX}pcre2-posix${_pcre2_debug_postfix}${CMAKE_SHARED_LIBRARY_SUFFIX} ) SET(_pcre2_implibname - libpcre2-8.dll.a + ${CMAKE_IMPORT_LIBRARY_PREFIX}pcre2-8${_pcre2_debug_postfix}${CMAKE_IMPORT_LIBRARY_SUFFIX} ) SET(_pcre2_implibname_posix - libpcre2-posix.dll.a + ${CMAKE_IMPORT_LIBRARY_PREFIX}pcre2-posix${_pcre2_debug_postfix}${CMAKE_IMPORT_LIBRARY_SUFFIX} ) SET(_pcre2_libpath @@ -50,22 +62,17 @@ SET(_pcre2_include_dir ${_install_dir}/include ) -SET(_pcre2_configure_command - sh ./configure -) - -SET(_pcre2_autogen_command - sh ./autogen.sh -) - -LIST(APPEND _pcre2_configure_args "--prefix=${_install_dir}") -# Build as shared library -LIST(APPEND _pcre2_configure_args "--disable-static") -LIST(APPEND _pcre2_configure_args "--disable-pcre2grep-libbz2") -LIST(APPEND _pcre2_configure_args "--disable-pcre2grep-libz") +# PCRE2-specific CMake options (replaces autotools configure args) +LIST(APPEND _configure_options "-DBUILD_SHARED_LIBS=ON") +LIST(APPEND _configure_options "-DBUILD_STATIC_LIBS=OFF") +LIST(APPEND _configure_options "-DPCRE2_BUILD_PCRE2GREP=OFF") +LIST(APPEND _configure_options "-DPCRE2_BUILD_TESTS=OFF") +LIST(APPEND _configure_options "-DPCRE2_SUPPORT_LIBBZ2=OFF") +LIST(APPEND _configure_options "-DPCRE2_SUPPORT_LIBZ=OFF") +LIST(APPEND _configure_options "-DINSTALL_MSVC_PDB=OFF") IF(CMAKE_BUILD_TYPE MATCHES "^Debug$") - LIST(APPEND _pcre2_configure_args "--enable-debug") + LIST(APPEND _configure_options "-DPCRE2_DEBUG=ON") ENDIF() EXTERNALPROJECT_ADD( @@ -78,66 +85,61 @@ EXTERNALPROJECT_ADD( SOURCE_DIR ${_source_dir} INSTALL_DIR ${_install_dir} DEPENDS ZLIB::ZLIB - CONFIGURE_COMMAND ${_pcre2_autogen_command} && ${_pcre2_configure_command} ${_pcre2_configure_args} - BUILD_COMMAND make -j${_cpu_count} + CONFIGURE_COMMAND ${CMAKE_COMMAND} ${_configure_options} + BUILD_COMMAND ${_cmake_build_command} + INSTALL_COMMAND ${_cmake_install_command} BUILD_IN_SOURCE TRUE BUILD_ALWAYS FALSE - BUILD_BYPRODUCTS ${_pcre2_libname} ${_pcre2_libname_posix} ${_pcre2_implibname} ${_pcre2_implibname_posix} + BUILD_BYPRODUCTS ${_pcre2_libpath} ${_pcre2_libpath_posix} ${_pcre2_implibpath} ${_pcre2_implibpath_posix} USES_TERMINAL_BUILD TRUE ) -# PCRE is not used for Linux and MacOS (Boost regex is used) in the current code. Copy library files manually since there are tools that are not needed in the -# bin folder. -ADD_CUSTOM_COMMAND( - COMMENT "Staging ${_target}'s shared library into ${RV_STAGE_BIN_DIR}" - OUTPUT ${RV_STAGE_BIN_DIR}/${_pcre2_libname} ${RV_STAGE_BIN_DIR}/${_pcre2_libname_posix} - COMMAND ${CMAKE_COMMAND} -E copy ${_pcre2_libpath} ${_pcre2_libpath_posix} -t ${RV_STAGE_BIN_DIR} - DEPENDS ${_target} -) - -ADD_CUSTOM_TARGET( - ${_target}-stage-target ALL - DEPENDS ${RV_STAGE_BIN_DIR}/${_pcre2_libname} ${RV_STAGE_BIN_DIR}/${_pcre2_libname_posix} -) - -ADD_DEPENDENCIES(dependencies ${_target}-stage-target) - -ADD_LIBRARY(pcre2-8 SHARED IMPORTED GLOBAL) -ADD_LIBRARY(pcre2-posix SHARED IMPORTED GLOBAL) - -ADD_DEPENDENCIES(pcre2-8 ${_target}) -ADD_DEPENDENCIES(pcre2-posix ${_target}) - -# Setup includes -SET(_pcre2_include_dir - ${_install_dir}/include +RV_STAGE_DEPENDENCY_LIBS( + TARGET + ${_target} + BIN_DIR + ${_bin_dir} + OUTPUTS + ${RV_STAGE_BIN_DIR}/${_pcre2_libname} + ${RV_STAGE_BIN_DIR}/${_pcre2_libname_posix} ) -FILE(MAKE_DIRECTORY ${_pcre2_include_dir}) -# Setup pcre2 8-bits target -SET_TARGET_PROPERTIES( - pcre2-8 - PROPERTIES IMPORTED_LOCATION ${_pcre2_libpath} - IMPORTED_IMPLIB ${_pcre2_implibpath} -) -TARGET_INCLUDE_DIRECTORIES( +RV_ADD_IMPORTED_LIBRARY( + NAME pcre2-8 - INTERFACE ${_pcre2_include_dir} + TYPE + SHARED + LOCATION + ${_pcre2_libpath} + SONAME + ${_pcre2_libname} + IMPLIB + ${_pcre2_implibpath} + INCLUDE_DIRS + ${_pcre2_include_dir} + DEPENDS + ${_target} + ADD_TO_DEPS_LIST ) TARGET_COMPILE_DEFINITIONS( pcre2-8 INTERFACE PCRE2_CODE_UNIT_WIDTH=8 ) -# Setup pcre2-posix target -SET_TARGET_PROPERTIES( - pcre2-posix - PROPERTIES IMPORTED_LOCATION ${_pcre2_libpath_posix} - IMPORTED_IMPLIB ${_pcre2_implibpath_posix} -) -TARGET_INCLUDE_DIRECTORIES( +RV_ADD_IMPORTED_LIBRARY( + NAME pcre2-posix - INTERFACE ${_pcre2_include_dir} + TYPE + SHARED + LOCATION + ${_pcre2_libpath_posix} + SONAME + ${_pcre2_libname_posix} + IMPLIB + ${_pcre2_implibpath_posix} + INCLUDE_DIRS + ${_pcre2_include_dir} + DEPENDS + ${_target} + ADD_TO_DEPS_LIST ) - -LIST(APPEND RV_DEPS_LIST pcre2-8 pcre2-posix) diff --git a/cmake/dependencies/zlib.cmake b/cmake/dependencies/zlib.cmake index cef51c747..4685c65b3 100644 --- a/cmake/dependencies/zlib.cmake +++ b/cmake/dependencies/zlib.cmake @@ -4,20 +4,32 @@ # SPDX-License-Identifier: Apache-2.0 # -RV_CREATE_STANDARD_DEPS_VARIABLES("RV_DEPS_ZLIB" "${RV_DEPS_ZLIB_VERSION}" "" "") +RV_CREATE_STANDARD_DEPS_VARIABLES("RV_DEPS_ZLIB" "${RV_DEPS_ZLIB_VERSION}" "" "" FORCE_LIB) + +# zlib has no CMake CONFIG files, so ALLOW_MODULE lets the built-in FindZLIB.cmake create ZLIB::ZLIB. Also falls back to pkg-config (useful if ZLIB_ROOT points +# to a keg-only Homebrew cellar). +RV_FIND_DEPENDENCY( + TARGET + ${_target} + PACKAGE + ZLIB + VERSION + ${_version} + PKG_CONFIG_NAME + zlib + ALLOW_MODULE + DEPS_LIST_TARGETS + ZLIB::ZLIB +) SET(_download_url "https://github.com/madler/zlib/archive/refs/tags/v${_version}.zip" ) - SET(_download_hash ${RV_DEPS_ZLIB_DOWNLOAD_HASH} ) -SET(_lib_dir - ${_install_dir}/lib -) - +# Shared naming logic (used by both build and found paths for staging) IF(RV_TARGET_WINDOWS) IF(CMAKE_BUILD_TYPE MATCHES "^Debug$") SET(_zlibname @@ -42,7 +54,6 @@ SET(_libpath ) IF(RV_TARGET_WINDOWS) - # Zlib is a Shared lib (see _libname): On Windows it'll go in the bin dir. SET(_libpath ${_bin_dir}/${_libname} ) @@ -60,79 +71,87 @@ IF(RV_TARGET_WINDOWS) LIST(APPEND _zlib_byproducts ${_implibpath}) ENDIF() -# The patch comes from the ZLIB port in Vcpkg repository. The name of the patch is kept as is. See https://github.com/microsoft/vcpkg/tree/master/ports/zlib -# Description: Fix unistd.h being incorrectly required when imported from a project defining HAVE_UNISTD_H=0 -SET(_patch_command - patch -p1 < ${CMAKE_CURRENT_SOURCE_DIR}/patch/zconf.h.cmakein_prevent_invalid_inclusions.patch && patch -p1 < - ${CMAKE_CURRENT_SOURCE_DIR}/patch/zconf.h.in_prevent_invalid_inclusions.patch -) +IF(NOT ${_target}_FOUND) + INCLUDE(${CMAKE_CURRENT_LIST_DIR}/build/zlib.cmake) -EXTERNALPROJECT_ADD( - ${_target} - URL ${_download_url} - URL_MD5 ${_download_hash} - DOWNLOAD_NAME ${_target}_${_version}.zip - DOWNLOAD_DIR ${RV_DEPS_DOWNLOAD_DIR} - DOWNLOAD_EXTRACT_TIMESTAMP TRUE - SOURCE_DIR ${RV_DEPS_BASE_DIR}/${_target}/src - INSTALL_DIR ${_install_dir} - PATCH_COMMAND ${_patch_command} - CONFIGURE_COMMAND ${CMAKE_COMMAND} ${_configure_options} - BUILD_COMMAND ${_cmake_build_command} - INSTALL_COMMAND ${_cmake_install_command} - BUILD_IN_SOURCE TRUE - BUILD_ALWAYS FALSE - BUILD_BYPRODUCTS ${_zlib_byproducts} - USES_TERMINAL_BUILD TRUE -) + IF(RV_TARGET_WINDOWS) + RV_STAGE_DEPENDENCY_LIBS(TARGET ${_target} BIN_DIR ${_bin_dir} OUTPUTS ${RV_STAGE_BIN_DIR}/${_libname}) + ELSEIF(RV_TARGET_DARWIN) + RV_STAGE_DEPENDENCY_LIBS(TARGET ${_target} OUTPUTS ${RV_STAGE_LIB_DIR}/${_libname}) + ELSE() + RV_STAGE_DEPENDENCY_LIBS(TARGET ${_target} OUTPUTS ${RV_STAGE_LIB_DIR}/${_libname}) + ENDIF() +ELSE() + IF(NOT TARGET ZLIB::ZLIB) + RV_ADD_IMPORTED_LIBRARY( + NAME + ZLIB::ZLIB + TYPE + SHARED + LOCATION + ${_lib_dir}/${_libname} + SONAME + ${_libname} + INCLUDE_DIRS + ${_include_dir} + DEPENDS + ${_target} + ) + ENDIF() + + IF(RV_TARGET_DARWIN) + # Found zlib may carry a versioned install name (e.g. libz.1.dylib from Homebrew, libz.1.3.1.dylib from MacPorts). Stage with the actual SONAME and create an + # unversioned symlink when needed. The install name rewrite to @rpath is deferred to install time (remove_absolute_rpath.py). + # + # Resolve the actual library path, then fall back to ZLIB_LIBRARIES (always set by FindZLIB.cmake). + RV_RESOLVE_IMPORTED_LOCATION(ZLIB::ZLIB _zlib_realpath) + IF(NOT _zlib_realpath + AND ZLIB_LIBRARIES + ) + SET(_zlib_realpath + "${ZLIB_LIBRARIES}" + ) + ENDIF() + + # Resolve symlinks to get the real file, then extract SONAME from install name + GET_FILENAME_COMPONENT(_zlib_realpath "${_zlib_realpath}" REALPATH) + EXECUTE_PROCESS( + COMMAND otool -D "${_zlib_realpath}" + OUTPUT_VARIABLE _zlib_otool_out + OUTPUT_STRIP_TRAILING_WHITESPACE + ) + # otool -D output: first line is the file path, second line is the install name + STRING(REGEX MATCH "[^\n]+$" _zlib_install_name "${_zlib_otool_out}") + GET_FILENAME_COMPONENT(_zlib_soname "${_zlib_install_name}" NAME) + + # Only create the unversioned symlink if the SONAME is versioned (e.g. libz.1.dylib != libz.dylib). When they match (e.g. vcpkg/Conan providing libz.dylib + # directly), skip to avoid a self-referencing symlink. + SET(_zlib_symlink_cmd + "" + ) + IF(NOT "${_zlib_soname}" STREQUAL "${_libname}") + SET(_zlib_symlink_cmd + COMMAND ${CMAKE_COMMAND} -E create_symlink ${_zlib_soname} ${RV_STAGE_LIB_DIR}/${_libname} + ) + ENDIF() -IF(RV_TARGET_WINDOWS) - # FFmpeg expect "zlib" in Release and Debug. - IF(CMAKE_BUILD_TYPE MATCHES "^Debug$") ADD_CUSTOM_COMMAND( - TARGET ${_target} - POST_BUILD - COMMENT "Renaming the ZLIB import debug lib to the name FFmpeg is expecting (release name)" - COMMAND ${CMAKE_COMMAND} -E copy ${_implibpath} ${_lib_dir}/zlib.lib + COMMENT "Staging ${_target}: ${_zlib_soname} -> ${RV_STAGE_LIB_DIR}" + OUTPUT ${RV_STAGE_LIB_DIR}/${_zlib_soname} + COMMAND ${CMAKE_COMMAND} -E make_directory ${RV_STAGE_LIB_DIR} + COMMAND ${CMAKE_COMMAND} -E copy_if_different "${_zlib_realpath}" "${RV_STAGE_LIB_DIR}/${_zlib_soname}" ${_zlib_symlink_cmd} + DEPENDS ${_target} ) + ADD_CUSTOM_TARGET( + ${_target}-stage-target ALL + DEPENDS ${RV_STAGE_LIB_DIR}/${_zlib_soname} + ) + ADD_DEPENDENCIES(dependencies ${_target}-stage-target) + ELSE() + RV_STAGE_DEPENDENCY_LIBS(TARGET ${_target} TARGET_LIBS ZLIB::ZLIB) ENDIF() - - RV_STAGE_DEPENDENCY_LIBS(TARGET ${_target} BIN_DIR ${_bin_dir} OUTPUTS ${RV_STAGE_BIN_DIR}/${_libname}) -ELSEIF(RV_TARGET_DARWIN) - RV_STAGE_DEPENDENCY_LIBS( - TARGET - ${_target} - OUTPUTS - ${RV_STAGE_LIB_DIR}/${_libname} - PRE_COMMANDS - COMMAND - ${CMAKE_INSTALL_NAME_TOOL} - -id - "@rpath/${_libname}" - "${_lib_dir}/${_libname}" - ) -ELSE() - RV_STAGE_DEPENDENCY_LIBS(TARGET ${_target} OUTPUTS ${RV_STAGE_LIB_DIR}/${_libname}) ENDIF() -RV_ADD_IMPORTED_LIBRARY( - NAME - ZLIB::ZLIB - TYPE - SHARED - LOCATION - ${_libpath} - SONAME - ${_libname} - IMPLIB - ${_implibpath} - INCLUDE_DIRS - ${_include_dir} - DEPENDS - ${_target} - ADD_TO_DEPS_LIST -) - SET(RV_DEPS_ZLIB_INCLUDE_DIR ${_include_dir} CACHE STRING "Path to installed includes for ${_target}" diff --git a/cmake/install/pre_install_darwin.cmake b/cmake/install/pre_install_darwin.cmake index fe22f0970..19e63a26e 100644 --- a/cmake/install/pre_install_darwin.cmake +++ b/cmake/install/pre_install_darwin.cmake @@ -91,9 +91,33 @@ MACRO(post_install_platform) ${FILES_TO_FIX_RPATH} ) - MESSAGE(STATUS "python3 ${CMAKE_CURRENT_LIST_DIR}/../../src/build/remove_absolute_rpath.py --files-list ${_output_file_to_fix_} --root ${CMAKE_SOURCE_DIR}") + MESSAGE(STATUS "Fixing rpaths: python3 ${CMAKE_CURRENT_LIST_DIR}/../../src/build/remove_absolute_rpath.py --files-list ${_output_file_to_fix_} --root ${CMAKE_SOURCE_DIR}") EXECUTE_PROCESS( COMMAND python3 ${CMAKE_CURRENT_LIST_DIR}/../../src/build/remove_absolute_rpath.py --files-list ${_output_file_to_fix_} --root ${CMAKE_SOURCE_DIR} COMMAND_ERROR_IS_FATAL ANY ) + + # Ad-hoc codesign all Mach-O binaries. install_name_tool modifications above invalidate any existing signatures. + MESSAGE(STATUS "Ad-hoc codesigning installed binaries...") + FILE(STRINGS ${_output_file_to_fix_} _files_to_sign) + FOREACH(_file_to_sign ${_files_to_sign}) + STRING(STRIP "${_file_to_sign}" _file_to_sign) + IF(_file_to_sign AND EXISTS "${_file_to_sign}") + EXECUTE_PROCESS( + COMMAND file -bh "${_file_to_sign}" + OUTPUT_VARIABLE _file_type + OUTPUT_STRIP_TRAILING_WHITESPACE + ) + IF(_file_type MATCHES "^Mach-O") + EXECUTE_PROCESS( + COMMAND codesign --force --sign - "${_file_to_sign}" + RESULT_VARIABLE _codesign_result + ) + IF(NOT _codesign_result EQUAL 0) + MESSAGE(WARNING "Failed to codesign ${_file_to_sign}") + ENDIF() + ENDIF() + ENDIF() + ENDFOREACH() + MESSAGE(STATUS "Ad-hoc codesigning complete.") ENDMACRO() diff --git a/cmake/macros/rv_create_std_deps_vars.cmake b/cmake/macros/rv_create_std_deps_vars.cmake index 55a46ff17..5d2c31a3d 100644 --- a/cmake/macros/rv_create_std_deps_vars.cmake +++ b/cmake/macros/rv_create_std_deps_vars.cmake @@ -8,6 +8,10 @@ # Create the standard variables common to most RV_DEPS_xyz modules MACRO(RV_CREATE_STANDARD_DEPS_VARIABLES target_name version make_command configure_command) + # Parse optional keyword arguments after the 4 positional params. FORCE_LIB: always use lib/ instead of lib64/ even when RHEL_VERBOSE is set. Use for + # dependencies whose CMake install does not honor GNUInstallDirs lib64 (e.g. zlib). + CMAKE_PARSE_ARGUMENTS(_RCSDV "FORCE_LIB" "" "" ${ARGN}) + SET(_target ${target_name} ) @@ -33,7 +37,11 @@ MACRO(RV_CREATE_STANDARD_DEPS_VARIABLES target_name version make_command configu SET(_source_dir ${_base_dir}/src ) - IF(RHEL_VERBOSE) + IF(_RCSDV_FORCE_LIB) + SET(_lib_dir + ${_install_dir}/lib + ) + ELSEIF(RHEL_VERBOSE) SET(_lib_dir ${_install_dir}/lib64 ) @@ -161,3 +169,260 @@ MACRO(RV_SHOW_STANDARD_DEPS_VARIABLES) MESSAGE(DEBUG " ${_target}_VERSION='${${_target}_VERSION}'") MESSAGE(DEBUG " ${_target}_ROOT_DIR='${${_target}_ROOT_DIR}'") ENDMACRO() + +# +# RV_RESOLVE_IMPORTED_LOCATION — Resolve an imported target's library path across config variants +# +# MODULE-found targets (e.g. FindZLIB) often only set config-specific variants (IMPORTED_LOCATION_RELEASE, IMPORTED_LOCATION_NOCONFIG) not plain +# IMPORTED_LOCATION. This macro tries each variant and stores the first hit. +# +# Must be a MACRO so the output variable propagates to the caller's scope. +# +# Usage: RV_RESOLVE_IMPORTED_LOCATION( ) +# +MACRO(RV_RESOLVE_IMPORTED_LOCATION _rril_target _rril_out_var) + SET(${_rril_out_var} + "" + ) + IF(TARGET ${_rril_target}) + STRING(TOUPPER "${CMAKE_BUILD_TYPE}" _rril_config_upper) + FOREACH( + _rril_prop + IMPORTED_LOCATION IMPORTED_LOCATION_${_rril_config_upper} IMPORTED_LOCATION_RELEASE IMPORTED_LOCATION_NOCONFIG + ) + IF(NOT ${_rril_out_var}) + GET_TARGET_PROPERTY(${_rril_out_var} ${_rril_target} ${_rril_prop}) + ENDIF() + ENDFOREACH() + ENDIF() +ENDMACRO() + +# +# RV_MAKE_TARGETS_GLOBAL — Promote imported targets to GLOBAL visibility +# +# Imported targets created by find_package(CONFIG) are scoped to the calling directory. This promotes them to GLOBAL so all subdirectories can reference them. +# +MACRO(RV_MAKE_TARGETS_GLOBAL) + FOREACH( + _rmtg_target + ${ARGN} + ) + IF(TARGET ${_rmtg_target}) + SET_PROPERTY( + TARGET ${_rmtg_target} + PROPERTY IMPORTED_GLOBAL TRUE + ) + ENDIF() + ENDFOREACH() +ENDMACRO() + +# +# RV_SET_FOUND_PACKAGE_DIRS + +# Set directory variables from a found package +# +# When a dependency is found via find_package() instead of built from source, the _lib_dir, _bin_dir, _include_dir, and _install_dir variables set by +# RV_CREATE_STANDARD_DEPS_VARIABLES point at the (non-existent) ExternalProject install dir. This macro overrides them to point at the found package's +# actuallocation, so downstream code (including RV_STAGE_DEPENDENCY_LIBS) works identically for both paths. +# +# Also creates a dummy custom target for the dependency so that DEPENDS ${_target} works in the find path where there's no ExternalProject target. +# +# Must be a MACRO so the variable overrides propagate to the caller's scope. +# +MACRO(RV_SET_FOUND_PACKAGE_DIRS rv_deps_target find_package_name) + # ${find_package_name}_DIR is set by find_package(CONFIG) to the directory containing the CMake config files, typically: /lib/cmake// (3 + # levels up) /share/cmake// (3 levels up) + # + # Resolve through symlinks FIRST, then walk up. This is critical for package managers like Homebrew that symlink cmake config dirs into a shared prefix (e.g., + # /opt/homebrew/lib/cmake/Boost-1.85.0 → Cellar/boost@1.85/.../lib/cmake/Boost-1.85.0). Without REALPATH, walking up lands on the shared prefix + # (/opt/homebrew) instead of the package-specific root, causing include path contamination. + GET_FILENAME_COMPONENT(_sfpd_config_dir "${${find_package_name}_DIR}" REALPATH) + + # Also compute the unresolved root so callers can detect and fix imported targets whose config files have the same symlink issue. + GET_FILENAME_COMPONENT(_sfpd_config_dir_unresolved "${${find_package_name}_DIR}" ABSOLUTE) + GET_FILENAME_COMPONENT(_sfpd_unresolved_root_3 "${_sfpd_config_dir_unresolved}/../../.." ABSOLUTE) + + # Walk up 3 levels from the resolved config dir to find the package root + GET_FILENAME_COMPONENT(_sfpd_root_3 "${_sfpd_config_dir}/../../.." ABSOLUTE) + GET_FILENAME_COMPONENT(_sfpd_root_2 "${_sfpd_config_dir}/../.." ABSOLUTE) + + IF(EXISTS "${_sfpd_root_3}/include" + OR EXISTS "${_sfpd_root_3}/lib" + ) + SET(_install_dir + "${_sfpd_root_3}" + ) + ELSEIF( + EXISTS "${_sfpd_root_2}/include" + OR EXISTS "${_sfpd_root_2}/lib" + ) + SET(_install_dir + "${_sfpd_root_2}" + ) + ELSE() + SET(_install_dir + "${_sfpd_root_3}" + ) + ENDIF() + + SET(_sfpd_unresolved_include_dir + "${_sfpd_unresolved_root_3}/include" + ) + + # Set both the regular variable (to override the one from RV_CREATE_STANDARD_DEPS_VARIABLES that would otherwise shadow the cache) and the cache variable (for + # persistence across reconfigures). + SET(${rv_deps_target}_ROOT_DIR + "${_install_dir}" + ) + SET(${rv_deps_target}_ROOT_DIR + "${_install_dir}" + CACHE INTERNAL "" FORCE + ) + SET(_include_dir + "${_install_dir}/include" + ) + SET(_bin_dir + "${_install_dir}/bin" + ) + IF(RHEL_VERBOSE + AND EXISTS "${_install_dir}/lib64" + ) + SET(_lib_dir + "${_install_dir}/lib64" + ) + ELSE() + SET(_lib_dir + "${_install_dir}/lib" + ) + ENDIF() + + IF(NOT TARGET ${rv_deps_target}) + ADD_CUSTOM_TARGET(${rv_deps_target}) + ENDIF() +ENDMACRO() + +# +# RV_SET_FOUND_PKGCONFIG_DIRS + +# Set directory variables from a pkg-config found package +# +# Parallel to RV_SET_FOUND_PACKAGE_DIRS but uses variables set by pkg_check_modules() instead of find_package(CONFIG). Sets _lib_dir, _include_dir, +# _install_dir, _bin_dir in the caller's scope and creates a dummy custom target. _install_dir, _bin_dir in the caller's scope and creates a dummy custom +# target. +# +# Must be a MACRO so the variable overrides propagate to the caller's scope. +# +MACRO(RV_SET_FOUND_PKGCONFIG_DIRS rv_deps_target pc_prefix) + LIST(GET ${pc_prefix}_LIBRARY_DIRS 0 _lib_dir) + LIST(GET ${pc_prefix}_INCLUDE_DIRS 0 _include_dir) + + GET_FILENAME_COMPONENT(_install_dir "${_lib_dir}/.." ABSOLUTE) + SET(_bin_dir + "${_install_dir}/bin" + ) + + SET(${rv_deps_target}_ROOT_DIR + "${_install_dir}" + ) + SET(${rv_deps_target}_ROOT_DIR + "${_install_dir}" + CACHE INTERNAL "" FORCE + ) + + IF(NOT TARGET ${rv_deps_target}) + ADD_CUSTOM_TARGET(${rv_deps_target}) + ENDIF() +ENDMACRO() + +# +# RV_SET_FOUND_MODULE_DIRS +# +# Set directory variables from a package found via CMake MODULE mode (built-in Find modules like FindZLIB, FindJPEG, etc.). +# +# Unlike CONFIG mode packages that provide ${Package}_DIR, MODULE mode results derive locations from the imported target's properties and standard +# _INCLUDE_DIRS / _LIBRARIES variables. +# +# Must be a MACRO so the variable overrides propagate to the caller's scope. +# +MACRO(RV_SET_FOUND_MODULE_DIRS rv_deps_target find_package_name deps_list_targets) + SET(_sfmd_inc_dir + "" + ) + SET(_sfmd_lib_dir + "" + ) + + # Derive include dir: prefer _INCLUDE_DIRS, fall back to first target's INTERFACE_INCLUDE_DIRECTORIES + IF(${find_package_name}_INCLUDE_DIRS) + LIST(GET ${find_package_name}_INCLUDE_DIRS 0 _sfmd_inc_dir) + ELSE() + SET(_sfmd_dep_targets + ${deps_list_targets} + ) + LIST(LENGTH _sfmd_dep_targets _sfmd_dep_count) + IF(_sfmd_dep_count GREATER 0) + LIST(GET _sfmd_dep_targets 0 _sfmd_primary_tgt) + IF(TARGET ${_sfmd_primary_tgt}) + GET_TARGET_PROPERTY(_sfmd_inc_dir ${_sfmd_primary_tgt} INTERFACE_INCLUDE_DIRECTORIES) + ENDIF() + ENDIF() + ENDIF() + + # Derive lib dir from the first imported target's IMPORTED_LOCATION + SET(_sfmd_dep_targets + ${deps_list_targets} + ) + LIST(LENGTH _sfmd_dep_targets _sfmd_dep_count) + IF(_sfmd_dep_count GREATER 0) + LIST(GET _sfmd_dep_targets 0 _sfmd_primary_tgt) + RV_RESOLVE_IMPORTED_LOCATION(${_sfmd_primary_tgt} _sfmd_loc) + IF(_sfmd_loc) + GET_FILENAME_COMPONENT(_sfmd_lib_dir "${_sfmd_loc}" DIRECTORY) + ENDIF() + ENDIF() + + IF(_sfmd_inc_dir) + SET(_include_dir + "${_sfmd_inc_dir}" + ) + ENDIF() + IF(_sfmd_lib_dir) + SET(_lib_dir + "${_sfmd_lib_dir}" + ) + ENDIF() + + GET_FILENAME_COMPONENT(_install_dir "${_lib_dir}/.." ABSOLUTE) + SET(_bin_dir + "${_install_dir}/bin" + ) + + SET(${rv_deps_target}_ROOT_DIR + "${_install_dir}" + ) + SET(${rv_deps_target}_ROOT_DIR + "${_install_dir}" + CACHE INTERNAL "" FORCE + ) + + IF(NOT TARGET ${rv_deps_target}) + ADD_CUSTOM_TARGET(${rv_deps_target}) + ENDIF() +ENDMACRO() + +# +# RV_PRINT_PACKAGE_INFO — Print diagnostic info for a found package +# +MACRO(RV_PRINT_PACKAGE_INFO package_name) + MESSAGE(STATUS " ${package_name} package info:") + IF(DEFINED ${package_name}_VERSION) + MESSAGE(STATUS " Version: ${${package_name}_VERSION}") + ENDIF() + IF(DEFINED ${package_name}_DIR) + MESSAGE(STATUS " Config dir: ${${package_name}_DIR}") + ENDIF() + MESSAGE(STATUS " Root: ${_install_dir}") + MESSAGE(STATUS " Include: ${_include_dir}") + MESSAGE(STATUS " Lib: ${_lib_dir}") + MESSAGE(STATUS " Bin: ${_bin_dir}") +ENDMACRO() diff --git a/cmake/macros/rv_find_dependency.cmake b/cmake/macros/rv_find_dependency.cmake new file mode 100644 index 000000000..c42182be6 --- /dev/null +++ b/cmake/macros/rv_find_dependency.cmake @@ -0,0 +1,228 @@ +# +# Copyright (C) 2026 Autodesk, Inc. All Rights Reserved. +# +# SPDX-License-Identifier: Apache-2.0 +# + +# +# RV_FIND_DEPENDENCY — Try to find a pre-built package, set ${TARGET}_FOUND +# +# Checks RV_DEPS_PREFER_INSTALLED and per-dep RV_DEPS__FORCE_BUILD. Tries find_package(CONFIG) first, then falls back to pkg-config if PKG_CONFIG_NAME is +# set. If found: sets up directory vars, adds to RV_DEPS_LIST. If not found or finding disabled: sets ${TARGET}_FOUND=FALSE (caller handles fallback). +# +# Imported targets from find_package(CONFIG) are automatically GLOBAL via CMAKE_FIND_PACKAGE_TARGETS_GLOBAL (set in cmake/dependencies/CMakeLists.txt). For +# pkg-config, the caller is responsible for creating proper imported targets (with LOCATION) after this macro returns — INTERFACE targets won't work because +# rv_stage.cmake expects LOCATION on all RV_DEPS_LIST entries. +# +# Must be a MACRO because RV_SET_FOUND_PACKAGE_DIRS / RV_SET_FOUND_PKGCONFIG_DIRS set _lib_dir, _bin_dir, _include_dir in the caller's scope via text +# substitution. +# +# cmake-format: off +# Usage: +# RV_FIND_DEPENDENCY( +# TARGET # REQUIRED: e.g., RV_DEPS_IMATH +# PACKAGE # REQUIRED: e.g., Imath +# [VERSION ] # Optional version (EXACT or MINIMUM per RV_DEPS_VERSION_MATCH) +# [PKG_CONFIG_NAME ] # Optional pkg-config module name for fallback +# [ALLOW_MODULE] # Also try find_package(MODULE) for packages with built-in Find modules +# [COMPONENTS ...] # Optional find_package COMPONENTS (e.g., Boost components) +# [DEPS_LIST_TARGETS ...] # Targets to append to RV_DEPS_LIST +# ) +# cmake-format: on +MACRO(RV_FIND_DEPENDENCY) + CMAKE_PARSE_ARGUMENTS(_RFD "ALLOW_MODULE" "TARGET;PACKAGE;VERSION;PKG_CONFIG_NAME" "DEPS_LIST_TARGETS;COMPONENTS" ${ARGN}) + + SET(${_RFD_TARGET}_FOUND + FALSE + ) + SET(_RFD_FOUND_VIA + "" + ) + + IF(RV_DEPS_PREFER_INSTALLED + AND NOT ${_RFD_TARGET}_FORCE_BUILD + ) + + # Determine version match mode: per-dep override > global default + IF(DEFINED ${_RFD_TARGET}_VERSION_MATCH) + SET(_rfd_match_mode + "${${_RFD_TARGET}_VERSION_MATCH}" + ) + ELSE() + SET(_rfd_match_mode + "${RV_DEPS_VERSION_MATCH}" + ) + ENDIF() + + # Build optional COMPONENTS argument list + SET(_rfd_components_args + "" + ) + IF(_RFD_COMPONENTS) + SET(_rfd_components_args + COMPONENTS ${_RFD_COMPONENTS} + ) + ENDIF() + + # Strategy 1: CMake CONFIG mode + IF(_RFD_VERSION) + IF(_rfd_match_mode STREQUAL "EXACT") + FIND_PACKAGE(${_RFD_PACKAGE} ${_RFD_VERSION} EXACT CONFIG ${_rfd_components_args}) + ELSE() + FIND_PACKAGE(${_RFD_PACKAGE} ${_RFD_VERSION} CONFIG ${_rfd_components_args}) + ENDIF() + ELSE() + FIND_PACKAGE(${_RFD_PACKAGE} CONFIG ${_rfd_components_args}) + ENDIF() + + IF(${_RFD_PACKAGE}_FOUND) + SET(_RFD_FOUND_VIA + "config" + ) + ENDIF() + + # Strategy 1.5: CMake MODULE mode (built-in Find modules) Some packages (e.g., zlib) don't ship CMake config files but have built-in Find modules + # (FindZLIB.cmake) that create proper imported targets. Opt-in via ALLOW_MODULE to avoid unexpected matches. + IF(NOT ${_RFD_PACKAGE}_FOUND + AND _RFD_ALLOW_MODULE + ) + IF(_RFD_VERSION) + IF(_rfd_match_mode STREQUAL "EXACT") + FIND_PACKAGE(${_RFD_PACKAGE} ${_RFD_VERSION} EXACT MODULE ${_rfd_components_args}) + ELSE() + FIND_PACKAGE(${_RFD_PACKAGE} ${_RFD_VERSION} MODULE ${_rfd_components_args}) + ENDIF() + ELSE() + FIND_PACKAGE(${_RFD_PACKAGE} MODULE ${_rfd_components_args}) + ENDIF() + + IF(${_RFD_PACKAGE}_FOUND) + SET(_RFD_FOUND_VIA + "module" + ) + ENDIF() + ENDIF() + + # Strategy 2: pkg-config fallback + IF(NOT ${_RFD_PACKAGE}_FOUND + AND _RFD_PKG_CONFIG_NAME + ) + FIND_PACKAGE(PkgConfig QUIET) + IF(PKG_CONFIG_FOUND) + # pkg_check_modules only searches PKG_CONFIG_PATH by default. Temporarily enable CMAKE_PREFIX_PATH integration so that + # -DCMAKE_PREFIX_PATH=/opt/homebrew/opt/zlib also exposes /lib/pkgconfig/*.pc to pkg-config. Restored afterwards to avoid side-effects on + # unrelated pkg_check_modules calls. + SET(_rfd_saved_pc_prefix_path + "${PKG_CONFIG_USE_CMAKE_PREFIX_PATH}" + ) + SET(PKG_CONFIG_USE_CMAKE_PREFIX_PATH + ON + ) + + IF(_RFD_VERSION) + IF(_rfd_match_mode STREQUAL "EXACT") + PKG_CHECK_MODULES(${_RFD_TARGET}_PC QUIET IMPORTED_TARGET "${_RFD_PKG_CONFIG_NAME}=${_RFD_VERSION}") + ELSE() + PKG_CHECK_MODULES(${_RFD_TARGET}_PC QUIET IMPORTED_TARGET "${_RFD_PKG_CONFIG_NAME}>=${_RFD_VERSION}") + ENDIF() + ELSE() + PKG_CHECK_MODULES(${_RFD_TARGET}_PC QUIET IMPORTED_TARGET ${_RFD_PKG_CONFIG_NAME}) + ENDIF() + + SET(PKG_CONFIG_USE_CMAKE_PREFIX_PATH + "${_rfd_saved_pc_prefix_path}" + ) + + IF(${_RFD_TARGET}_PC_FOUND) + SET(${_RFD_PACKAGE}_FOUND + TRUE + ) + SET(${_RFD_PACKAGE}_VERSION + "${${_RFD_TARGET}_PC_VERSION}" + ) + SET(_RFD_FOUND_VIA + "pkgconfig" + ) + ENDIF() + ENDIF() + ENDIF() + + # Common handling for found packages + IF(${_RFD_PACKAGE}_FOUND) + SET(${_RFD_TARGET}_FOUND + TRUE + ) + + IF(_RFD_FOUND_VIA STREQUAL "config") + MESSAGE(STATUS "Found ${_RFD_PACKAGE} ${${_RFD_PACKAGE}_VERSION} via CMake config. Using installed package.") + RV_SET_FOUND_PACKAGE_DIRS(${_RFD_TARGET} ${_RFD_PACKAGE}) + + # Package config files may suffer the same symlink resolution bug as our walk-up logic (CMake normalizes "../" before resolving symlinks). If the + # resolved and unresolved include dirs differ, fix ALL imported targets from this package (not just DEPS_LIST_TARGETS). Config files often create + # auxiliary interface targets (e.g. Imath::ImathConfig, OpenEXR::OpenEXRConfig) that carry include dirs and are linked transitively by the library + # targets. + IF(NOT "${_sfpd_unresolved_include_dir}" STREQUAL "${_include_dir}") + # Record the contaminating prefix so ExternalProject sub-builds can exclude it via CMAKE_IGNORE_PREFIX_PATH. This is how we propagate the symlink fix + # to sub-builds that run their own CMake and would otherwise rediscover packages at the unresolved (shared) prefix. + LIST(APPEND RV_DEPS_IGNORE_PREFIXES "${_sfpd_unresolved_root_3}") + LIST(REMOVE_DUPLICATES RV_DEPS_IGNORE_PREFIXES) + SET(RV_DEPS_IGNORE_PREFIXES + "${RV_DEPS_IGNORE_PREFIXES}" + CACHE INTERNAL "Contaminating prefixes detected during symlink resolution (for ExternalProject sub-builds)" FORCE + ) + MESSAGE(STATUS " Added ${_sfpd_unresolved_root_3} to RV_DEPS_IGNORE_PREFIXES (symlink resolved to ${_install_dir})") + + GET_PROPERTY( + _rfd_all_imported_targets + DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" + PROPERTY IMPORTED_TARGETS + ) + IF(NOT _rfd_all_imported_targets) + MESSAGE(WARNING "RV_FIND_DEPENDENCY: IMPORTED_TARGETS is empty for ${_RFD_PACKAGE} — symlink include fixup skipped") + ENDIF() + FOREACH( + _rfd_dep + ${_rfd_all_imported_targets} + ) + IF("${_rfd_dep}" MATCHES "^${_RFD_PACKAGE}::") + GET_TARGET_PROPERTY(_rfd_inc_dirs ${_rfd_dep} INTERFACE_INCLUDE_DIRECTORIES) + IF(_rfd_inc_dirs) + STRING(REPLACE "${_sfpd_unresolved_include_dir}" "${_include_dir}" _rfd_fixed_inc_dirs "${_rfd_inc_dirs}") + IF(NOT "${_rfd_fixed_inc_dirs}" STREQUAL "${_rfd_inc_dirs}") + SET_TARGET_PROPERTIES( + ${_rfd_dep} + PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "${_rfd_fixed_inc_dirs}" + ) + MESSAGE(STATUS " Fixed ${_rfd_dep} include: ${_rfd_inc_dirs} -> ${_rfd_fixed_inc_dirs}") + ENDIF() + ENDIF() + ENDIF() + ENDFOREACH() + ENDIF() + ELSEIF(_RFD_FOUND_VIA STREQUAL "module") + MESSAGE(STATUS "Found ${_RFD_PACKAGE} ${${_RFD_PACKAGE}_VERSION} via CMake Find module. Using installed package.") + RV_SET_FOUND_MODULE_DIRS(${_RFD_TARGET} ${_RFD_PACKAGE} "${_RFD_DEPS_LIST_TARGETS}") + ELSEIF(_RFD_FOUND_VIA STREQUAL "pkgconfig") + MESSAGE(STATUS "Found ${_RFD_PACKAGE} ${${_RFD_PACKAGE}_VERSION} via pkg-config. Using installed package.") + RV_SET_FOUND_PKGCONFIG_DIRS(${_RFD_TARGET} ${_RFD_TARGET}_PC) + ENDIF() + + FOREACH( + _rfd_dep + ${_RFD_DEPS_LIST_TARGETS} + ) + LIST(APPEND RV_DEPS_LIST ${_rfd_dep}) + ENDFOREACH() + + STRING(TOUPPER ${_RFD_PACKAGE} _RFD_PKG_UPPER) + SET(RV_DEPS_${_RFD_PKG_UPPER}_VERSION + ${${_RFD_PACKAGE}_VERSION} + CACHE INTERNAL "" FORCE + ) + + RV_PRINT_PACKAGE_INFO(${_RFD_PACKAGE}) + ELSE() + MESSAGE(STATUS "${_RFD_PACKAGE} ${_RFD_VERSION} not found. It will be build from source") + ENDIF() + ENDIF() +ENDMACRO() diff --git a/cmake/macros/rv_stage.cmake b/cmake/macros/rv_stage.cmake index 399fc3dd9..15a168ffc 100644 --- a/cmake/macros/rv_stage.cmake +++ b/cmake/macros/rv_stage.cmake @@ -82,33 +82,11 @@ FUNCTION(rv_stage) IF(TARGET ${arg_TARGET}) GET_TARGET_PROPERTY(_native_target_type ${arg_TARGET} TYPE) IF(_native_target_type STREQUAL "EXECUTABLE") - ADD_CUSTOM_COMMAND( - COMMENT "Fixing ${arg_TARGET}'s RPATHs" TARGET ${arg_TARGET} POST_BUILD - COMMAND ${CMAKE_INSTALL_NAME_TOOL} -add_rpath "@executable_path/../Frameworks" "$" - COMMAND ${CMAKE_INSTALL_NAME_TOOL} -add_rpath "@executable_path/../lib" "$" - ) - ENDIF() - - IF(_native_target_type STREQUAL "EXECUTABLE" - OR _native_target_type STREQUAL "SHARED_LIBRARY" - ) - FOREACH( - dep - ${RV_DEPS_LIST} - ) - IF(TARGET ${dep}) - GET_PROPERTY( - dep_file_path - TARGET ${dep} - PROPERTY LOCATION - ) - GET_FILENAME_COMPONENT(dep_file_name ${dep_file_path} NAME) - ADD_CUSTOM_COMMAND( - COMMENT "Fixing ${dep_file_name}'s rpath in ${arg_TARGET}" TARGET ${arg_TARGET} POST_BUILD - COMMAND ${CMAKE_INSTALL_NAME_TOOL} -change "${dep_file_path}" "@rpath/${dep_file_name}" "$" - ) - ENDIF() - ENDFOREACH() + # Add rpaths via linker flags instead of POST_BUILD install_name_tool. + # CMAKE_SKIP_RPATH is ON so CMake's rpath machinery is disabled; + # direct linker flags bypass it. This avoids modifying the auto-signed + # binary after linking (which fails on arm64 macOS 26+). + TARGET_LINK_OPTIONS(${arg_TARGET} PRIVATE "LINKER:-rpath,@executable_path/../Frameworks" "LINKER:-rpath,@executable_path/../lib") ENDIF() ENDIF() ENDIF() @@ -351,6 +329,7 @@ FUNCTION(rv_stage) ENDIF() ADD_DEPENDENCIES(mu_plugins ${arg_TARGET}) + ADD_DEPENDENCIES(${arg_TARGET} dependencies) ADD_SHARED_LIBRARY_LIST(${arg_TARGET}) @@ -385,6 +364,7 @@ FUNCTION(rv_stage) ENDIF() ADD_DEPENDENCIES(python_plugins ${arg_TARGET}) + ADD_DEPENDENCIES(${arg_TARGET} dependencies) ADD_SHARED_LIBRARY_LIST(${arg_TARGET}) @@ -501,6 +481,7 @@ FUNCTION(rv_stage) ENDIF() ADD_DEPENDENCIES(image_formats ${arg_TARGET}) + ADD_DEPENDENCIES(${arg_TARGET} dependencies) ADD_SHARED_LIBRARY_LIST(${arg_TARGET}) @@ -530,6 +511,7 @@ FUNCTION(rv_stage) ENDIF() ADD_DEPENDENCIES(movie_formats ${arg_TARGET}) + ADD_DEPENDENCIES(${arg_TARGET} dependencies) ADD_SHARED_LIBRARY_LIST(${arg_TARGET}) @@ -560,6 +542,7 @@ FUNCTION(rv_stage) ENDIF() ADD_DEPENDENCIES(oiio_plugins ${arg_TARGET}) + ADD_DEPENDENCIES(${arg_TARGET} dependencies) ADD_SHARED_LIBRARY_LIST(${arg_TARGET}) @@ -589,6 +572,7 @@ FUNCTION(rv_stage) ENDIF() ADD_DEPENDENCIES(output_plugins ${arg_TARGET}) + ADD_DEPENDENCIES(${arg_TARGET} dependencies) ADD_SHARED_LIBRARY_LIST(${arg_TARGET}) diff --git a/cmake/macros/rv_stage_dependency_libs.cmake b/cmake/macros/rv_stage_dependency_libs.cmake index 7f6979b3b..185383030 100644 --- a/cmake/macros/rv_stage_dependency_libs.cmake +++ b/cmake/macros/rv_stage_dependency_libs.cmake @@ -20,6 +20,11 @@ # [STAGE_LIB_DIR ] # Override RV_STAGE_LIB_DIR (e.g., for OpenSSL/Linux) # [EXTRA_LIB_DIRS ...] # Additional lib dirs to copy to # [FILES [file2...]] # Individual files to copy_if_different to STAGE_LIB_DIR (alternative to LIB_DIR) +# [TARGET_LIBS [t2...]] # Imported targets to stage via $ generators. +# # At configure time, resolves IMPORTED_LOCATION to derive output filenames +# # for proper incremental tracking (re-stages if files are deleted). +# # Falls back to USE_FLAG_FILE if location cannot be resolved. +# # On Windows: DLL→STAGE_BIN_DIR, import lib→STAGE_LIB_DIR. # [DEPENDS [dep2...]] # Dependencies (default: ${TARGET}) # [PRE_COMMANDS COMMAND [COMMAND ...]] # Commands to run before copy; each must be prefixed with COMMAND keyword # [LIBNAME ] # Platform-aware shorthand: on Windows uses BIN_DIR+STAGE_BIN_DIR, otherwise STAGE_LIB_DIR @@ -29,7 +34,7 @@ # cmake-format: on FUNCTION(RV_STAGE_DEPENDENCY_LIBS) CMAKE_PARSE_ARGUMENTS( - _ARG "USE_FLAG_FILE" "TARGET;LIB_DIR;BIN_DIR;INCLUDE_DIR;STAGE_LIB_DIR;LIBNAME" "OUTPUTS;EXTRA_LIB_DIRS;DEPENDS;PRE_COMMANDS;FILES" ${ARGN} + _ARG "USE_FLAG_FILE" "TARGET;LIB_DIR;BIN_DIR;INCLUDE_DIR;STAGE_LIB_DIR;LIBNAME" "OUTPUTS;EXTRA_LIB_DIRS;DEPENDS;PRE_COMMANDS;FILES;TARGET_LIBS" ${ARGN} ) IF(_ARG_UNPARSED_ARGUMENTS) @@ -71,6 +76,7 @@ FUNCTION(RV_STAGE_DEPENDENCY_LIBS) # Default LIB_DIR to caller's _lib_dir if not explicitly passed IF(NOT _ARG_LIB_DIR AND NOT _ARG_FILES + AND NOT _ARG_TARGET_LIBS ) IF(DEFINED _lib_dir) SET(_ARG_LIB_DIR @@ -81,8 +87,9 @@ FUNCTION(RV_STAGE_DEPENDENCY_LIBS) IF(NOT _ARG_LIB_DIR AND NOT _ARG_FILES + AND NOT _ARG_TARGET_LIBS ) - MESSAGE(FATAL_ERROR "RV_STAGE_DEPENDENCY_LIBS: Either LIB_DIR or FILES is required") + MESSAGE(FATAL_ERROR "RV_STAGE_DEPENDENCY_LIBS: Either LIB_DIR, FILES, or TARGET_LIBS is required") ENDIF() # Default depends to TARGET @@ -99,6 +106,68 @@ FUNCTION(RV_STAGE_DEPENDENCY_LIBS) ) ENDIF() + # When TARGET_LIBS is used without explicit USE_FLAG_FILE or OUTPUTS, resolve IMPORTED_LOCATION at configure time to derive output filenames for proper + # incremental tracking. This way Ninja detects deleted staged files and re-runs the staging command automatically. + IF(_ARG_TARGET_LIBS + AND NOT _ARG_USE_FLAG_FILE + AND NOT _ARG_OUTPUTS + ) + SET(_rsdl_resolved_outputs) + SET(_rsdl_failed + FALSE + ) + STRING(TOUPPER "${CMAKE_BUILD_TYPE}" _rsdl_config_upper) + + FOREACH( + _rsdl_tgt + ${_ARG_TARGET_LIBS} + ) + RV_RESOLVE_IMPORTED_LOCATION(${_rsdl_tgt} _rsdl_loc) + + IF(_rsdl_loc) + GET_FILENAME_COMPONENT(_rsdl_fname "${_rsdl_loc}" NAME) + IF(RV_TARGET_WINDOWS) + LIST(APPEND _rsdl_resolved_outputs "${RV_STAGE_BIN_DIR}/${_rsdl_fname}") + SET(_rsdl_implib + "" + ) + FOREACH( + _rsdl_prop + IMPORTED_IMPLIB IMPORTED_IMPLIB_${_rsdl_config_upper} IMPORTED_IMPLIB_RELEASE IMPORTED_IMPLIB_NOCONFIG + ) + IF(NOT _rsdl_implib) + GET_TARGET_PROPERTY(_rsdl_implib ${_rsdl_tgt} ${_rsdl_prop}) + ENDIF() + ENDFOREACH() + IF(_rsdl_implib) + GET_FILENAME_COMPONENT(_rsdl_impfname "${_rsdl_implib}" NAME) + LIST(APPEND _rsdl_resolved_outputs "${_ARG_STAGE_LIB_DIR}/${_rsdl_impfname}") + ENDIF() + ELSE() + LIST(APPEND _rsdl_resolved_outputs "${_ARG_STAGE_LIB_DIR}/${_rsdl_fname}") + ENDIF() + ELSE() + SET(_rsdl_failed + TRUE + ) + MESSAGE(AUTHOR_WARNING "RV_STAGE_DEPENDENCY_LIBS: Cannot resolve IMPORTED_LOCATION for ${_rsdl_tgt}; falling back to flag file") + BREAK() + ENDIF() + ENDFOREACH() + + IF(NOT _rsdl_failed + AND _rsdl_resolved_outputs + ) + SET(_ARG_OUTPUTS + ${_rsdl_resolved_outputs} + ) + ELSE() + SET(_ARG_USE_FLAG_FILE + TRUE + ) + ENDIF() + ENDIF() + # Build the command list SET(_commands) @@ -126,6 +195,114 @@ FUNCTION(RV_STAGE_DEPENDENCY_LIBS) ENDFOREACH() ENDIF() + # Copy imported target libraries via generator expressions (resolved at build time). Ensure destination dirs exist first — copy_if_different doesn't create + # them, and for found packages (no ExternalProject) the stage dirs may not yet exist at this point in the build. + IF(_ARG_TARGET_LIBS) + LIST( + APPEND + _commands + COMMAND + ${CMAKE_COMMAND} + -E + make_directory + ${_ARG_STAGE_LIB_DIR} + ) + IF(RV_TARGET_WINDOWS) + LIST( + APPEND + _commands + COMMAND + ${CMAKE_COMMAND} + -E + make_directory + ${RV_STAGE_BIN_DIR} + ) + ENDIF() + + FOREACH( + _tgt + ${_ARG_TARGET_LIBS} + ) + IF(RV_TARGET_WINDOWS) + LIST( + APPEND + _commands + COMMAND + ${CMAKE_COMMAND} + -E + echo + " Staging $ -> ${RV_STAGE_BIN_DIR}/" + ) + LIST( + APPEND + _commands + COMMAND + ${CMAKE_COMMAND} + -E + copy_if_different + $ + ${RV_STAGE_BIN_DIR}/ + ) + LIST( + APPEND + _commands + COMMAND + ${CMAKE_COMMAND} + -E + echo + " Staging $ -> ${_ARG_STAGE_LIB_DIR}/" + ) + LIST( + APPEND + _commands + COMMAND + ${CMAKE_COMMAND} + -E + copy_if_different + $ + ${_ARG_STAGE_LIB_DIR}/ + ) + ELSE() + LIST( + APPEND + _commands + COMMAND + ${CMAKE_COMMAND} + -E + echo + " Staging $ -> ${_ARG_STAGE_LIB_DIR}/" + ) + LIST( + APPEND + _commands + COMMAND + ${CMAKE_COMMAND} + -E + copy_if_different + $ + ${_ARG_STAGE_LIB_DIR}/ + ) + + # Create a SONAME symlink if the library's recorded name differs from its filename (e.g. libFoo.2.3.dylib -> libFoo.2.3.2.dylib on macOS, + # libFoo.so.2.3 -> libFoo.so.2.3.2 on Linux). The linker records this name in dependent binaries, so the runtime loader needs a file matching it. + IF(RV_TARGET_DARWIN OR RV_TARGET_LINUX) + LIST( + APPEND + _commands + COMMAND + ${CMAKE_COMMAND} + -DLIB_FILE=${_ARG_STAGE_LIB_DIR}/$ + -DRV_TARGET_DARWIN=${RV_TARGET_DARWIN} + -DRV_TARGET_LINUX=${RV_TARGET_LINUX} + -P + ${PROJECT_SOURCE_DIR}/cmake/scripts/create_soname_symlink.cmake + ) + ENDIF() + ENDIF() + + ENDFOREACH() + ENDIF() + # Copy lib dir if specified IF(_ARG_LIB_DIR) LIST( @@ -213,11 +390,13 @@ FUNCTION(RV_STAGE_DEPENDENCY_LIBS) ENDIF() # Create the custom command (always OUTPUT-based for proper incremental builds). Note: ${_commands} contains COMMAND keywords which CMake's keyword parser - # uses as section boundaries, so they are correctly parsed as commands, not as additional OUTPUT entries. + # uses as section boundaries, so they are correctly parsed as commands, not as additional OUTPUT entries. VERBATIM ensures arguments with shell metacharacters + # (e.g., ">" in echo messages) are properly escaped. ADD_CUSTOM_COMMAND( COMMENT "Staging ${_ARG_TARGET} libs into ${_ARG_STAGE_LIB_DIR}" OUTPUT ${_tracking_outputs} ${_commands} DEPENDS ${_ARG_DEPENDS} + VERBATIM ) # Create the stage target diff --git a/cmake/scripts/create_soname_symlink.cmake b/cmake/scripts/create_soname_symlink.cmake new file mode 100644 index 000000000..af5ae9239 --- /dev/null +++ b/cmake/scripts/create_soname_symlink.cmake @@ -0,0 +1,91 @@ +# +# Copyright (C) 2026 Autodesk, Inc. All Rights Reserved. +# +# SPDX-License-Identifier: Apache-2.0 +# + +# +# create_soname_symlink.cmake — Create a SONAME symlink for a staged shared library +# +# On macOS, a shared library's install name (LC_ID_DYLIB) may differ from its filename +# (e.g. libOpenColorIO.2.3.dylib vs libOpenColorIO.2.3.2.dylib). On Linux, the ELF SONAME +# may differ similarly (e.g. libOpenColorIO.so.2.3 vs libOpenColorIO.so.2.3.2). The linker +# records this name in dependent binaries, so the runtime loader needs a file matching it. +# This script creates a symlink from the recorded name to the actual file if they differ. +# +# Usage: +# cmake -DLIB_FILE=/path/to/staged/libFoo.1.2.3.dylib -DRV_TARGET_DARWIN=TRUE -P create_soname_symlink.cmake +# cmake -DLIB_FILE=/path/to/staged/libFoo.so.1.2.3 -DRV_TARGET_LINUX=TRUE -P create_soname_symlink.cmake +# + +IF(NOT DEFINED LIB_FILE) + MESSAGE(FATAL_ERROR "LIB_FILE is required") +ENDIF() + +IF(NOT EXISTS "${LIB_FILE}") + MESSAGE(FATAL_ERROR "LIB_FILE does not exist: ${LIB_FILE}") +ENDIF() + +# Extract the library's recorded name using platform-specific tooling. +SET(_soname_basename "") + +IF(RV_TARGET_DARWIN) + EXECUTE_PROCESS( + COMMAND otool -D "${LIB_FILE}" + OUTPUT_VARIABLE _otool_out + OUTPUT_STRIP_TRAILING_WHITESPACE + ERROR_QUIET + RESULT_VARIABLE _otool_rc + ) + + IF(NOT _otool_rc EQUAL 0) + RETURN() + ENDIF() + + # otool -D output: first line is the file path, second line is the install name + STRING(REGEX MATCH "[^\n]+$" _install_name "${_otool_out}") + + IF(NOT _install_name) + RETURN() + ENDIF() + + GET_FILENAME_COMPONENT(_soname_basename "${_install_name}" NAME) + +ELSEIF(RV_TARGET_LINUX) + EXECUTE_PROCESS( + COMMAND readelf -d "${LIB_FILE}" + OUTPUT_VARIABLE _readelf_out + OUTPUT_STRIP_TRAILING_WHITESPACE + ERROR_QUIET + RESULT_VARIABLE _readelf_rc + ) + + IF(NOT _readelf_rc EQUAL 0) + RETURN() + ENDIF() + + # readelf -d output contains a line like: + # 0x000000000000000e (SONAME) Library soname: [libOpenColorIO.so.2.3] + STRING(REGEX MATCH "\\(SONAME\\)[^\n]*\\[([^\n]+)\\]" _soname_match "${_readelf_out}") + + IF(NOT CMAKE_MATCH_1) + RETURN() + ENDIF() + + SET(_soname_basename "${CMAKE_MATCH_1}") + +ELSE() + # Unsupported platform — nothing to do. + RETURN() +ENDIF() + +GET_FILENAME_COMPONENT(_file_basename "${LIB_FILE}" NAME) +GET_FILENAME_COMPONENT(_file_dir "${LIB_FILE}" DIRECTORY) + +IF(NOT "${_soname_basename}" STREQUAL "${_file_basename}") + SET(_symlink_path "${_file_dir}/${_soname_basename}") + IF(NOT EXISTS "${_symlink_path}") + MESSAGE(STATUS " Creating SONAME symlink: ${_soname_basename} -> ${_file_basename}") + FILE(CREATE_LINK "${_file_basename}" "${_symlink_path}" SYMBOLIC) + ENDIF() +ENDIF() diff --git a/src/build/remove_absolute_rpath.py b/src/build/remove_absolute_rpath.py index a34e7062c..379924310 100755 --- a/src/build/remove_absolute_rpath.py +++ b/src/build/remove_absolute_rpath.py @@ -103,6 +103,42 @@ def change_shared_library_path(object_file_path, old_library_path): return new_library_path +def get_install_name(object_file_path): + """Get a dylib's own install name (LC_ID_DYLIB) via otool -D. + Returns None for executables or if no install name is found.""" + try: + otool_output = subprocess.check_output( + ["otool", "-D", object_file_path], stderr=subprocess.DEVNULL + ).decode().splitlines() + except subprocess.CalledProcessError: + return None + # otool -D output: first line is the file path, second line is the install name + if len(otool_output) >= 2: + return otool_output[1].strip() + return None + + +def change_install_name_id(object_file_path, old_id): + new_id = f"@rpath/{os.path.basename(old_id)}" + command = [ + "install_name_tool", + "-id", + new_id, + object_file_path, + ] + subprocess.run(command).check_returncode() + return new_id + + +# System library prefixes that must keep their absolute paths. +# These are Apple-provided frameworks/dylibs that are always present at these paths. +SYSTEM_LIBRARY_PREFIXES = ("/usr/lib/", "/System/") + + +def is_system_library(library_path): + return any(library_path.startswith(p) for p in SYSTEM_LIBRARY_PREFIXES) + + def fix_rpath(target, root): for file in get_object_files(target): logging.info(f"Fixing rpaths for {file}") @@ -111,7 +147,7 @@ def fix_rpath(target, root): # become invalid due to our rpath fixing logic. # IMPORTANT: # Numpy was the only issue found, but it could happend again if new dependencies are added. - is_numpy = file._str.find("numpy") + is_numpy = file.find("numpy") if is_numpy > -1: logging.info(f"Skipping {file} because numpy") return @@ -128,13 +164,19 @@ def fix_rpath(target, root): logging.info(output) + # Fix library self-ID: for dylibs with absolute install names, rewrite to @rpath/ + install_name = get_install_name(file) + if install_name and not install_name.startswith("@") and not is_system_library(install_name): + new_id = change_install_name_id(file, install_name) + logging.info(f"\tinstall name: {install_name} (Changed ID to {new_id})") + logging.info(f"Fixing shared library paths for {file}") for library_path in get_shared_library_paths(file): output = f"\tlibrary path: {library_path}" shared_library_name = os.path.basename(library_path) - if library_path.startswith(root) or library_path == shared_library_name: + if (library_path.startswith("/") and not is_system_library(library_path)) or library_path == shared_library_name: new_library_path = change_shared_library_path(file, library_path) output += f" (Changed to {new_library_path})"