From 89997b45057f2cd4d9afdca32e853a90939cbaab Mon Sep 17 00:00:00 2001 From: Doug Walker Date: Sun, 20 Sep 2026 23:46:58 -0400 Subject: [PATCH 01/11] Add fix for missing glu on Windows Signed-off-by: Doug Walker --- src/libutils/oglapphelpers/glsl.cpp | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/libutils/oglapphelpers/glsl.cpp b/src/libutils/oglapphelpers/glsl.cpp index 791648e66e..0b73ea4c49 100644 --- a/src/libutils/oglapphelpers/glsl.cpp +++ b/src/libutils/oglapphelpers/glsl.cpp @@ -9,7 +9,18 @@ #elif _WIN32 +// GLEW undefines APIENTRY/CALLBACK/WINGDIAPI at the end of glew.h unless windows.h +// already defined them, which glu.h needs for its declarations. NOMINMAX avoids +// windows.h's min/max macros clashing with std::numeric_limits<>::min/max(). +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif +#ifndef NOMINMAX +#define NOMINMAX +#endif +#include #include +#include #else From 9d73d1fca26ebe63af20558a4a672dae0efe4ee6 Mon Sep 17 00:00:00 2001 From: Doug Walker Date: Mon, 21 Sep 2026 00:31:09 -0400 Subject: [PATCH 02/11] Add support for Yaml 0.9.0 Signed-off-by: Doug Walker --- .github/workflows/ci_workflow.yml | 8 ++++---- .github/workflows/dependencies_latest.yml | 6 +++--- .github/workflows/platform_latest.yml | 6 +++--- share/ci/scripts/multi/install_yaml-cpp.sh | 7 +++++-- share/cmake/modules/FindExtPackages.cmake | 2 +- share/cmake/modules/install/Installyaml-cpp.cmake | 9 +++++++-- tests/cpu/Config_tests.cpp | 2 +- 7 files changed, 24 insertions(+), 16 deletions(-) diff --git a/.github/workflows/ci_workflow.yml b/.github/workflows/ci_workflow.yml index 68d02377ae..496c00fe90 100644 --- a/.github/workflows/ci_workflow.yml +++ b/.github/workflows/ci_workflow.yml @@ -313,7 +313,7 @@ jobs: -Dpystring_ROOT=${{ env.ocio_build_path }}/ext/dist \ -Dyaml-cpp_ROOT=${{ env.ocio_build_path }}/ext/dist \ -Dyaml-cpp_STATIC_LIBRARY=ON \ - -Dyaml-cpp_VERSION=0.8.0 \ + -Dyaml-cpp_VERSION=0.9.0 \ -DZLIB_ROOT=${{ env.ocio_build_path }}/ext/dist \ -DZLIB_STATIC_LIBRARY=ON \ -Dminizip-ng_ROOT=${{ env.ocio_build_path }}/ext/dist \ @@ -440,7 +440,7 @@ jobs: -Dpystring_ROOT=${{ env.ocio_build_path }}/ext/dist \ -Dyaml-cpp_ROOT=${{ env.ocio_build_path }}/ext/dist \ -Dyaml-cpp_STATIC_LIBRARY=ON \ - -Dyaml-cpp_VERSION=0.8.0 \ + -Dyaml-cpp_VERSION=0.9.0 \ -DZLIB_ROOT=${{ env.ocio_build_path }}/ext/dist \ -DZLIB_STATIC_LIBRARY=ON \ -Dminizip-ng_ROOT=${{ env.ocio_build_path }}/ext/dist \ @@ -579,7 +579,7 @@ jobs: -Dpystring_ROOT=${{ env.ocio_build_path }}/ext/dist \ -Dyaml-cpp_ROOT=${{ env.ocio_build_path }}/ext/dist \ -Dyaml-cpp_STATIC_LIBRARY=ON \ - -Dyaml-cpp_VERSION=0.8.0 \ + -Dyaml-cpp_VERSION=0.9.0 \ -DZLIB_ROOT=${{ env.ocio_build_path }}/ext/dist \ -DZLIB_STATIC_LIBRARY=ON \ -Dminizip-ng_ROOT=${{ env.ocio_build_path }}/ext/dist \ @@ -733,7 +733,7 @@ jobs: -Dpystring_ROOT=${{ env.ocio_build_path }}/ext/dist \ -Dyaml-cpp_ROOT=${{ env.ocio_build_path }}/ext/dist \ -Dyaml-cpp_STATIC_LIBRARY=ON \ - -Dyaml-cpp_VERSION=0.8.0 \ + -Dyaml-cpp_VERSION=0.9.0 \ -DZLIB_ROOT=${{ env.ocio_build_path }}/ext/dist \ -DZLIB_STATIC_LIBRARY=ON \ -Dminizip-ng_ROOT=${{ env.ocio_build_path }}/ext/dist \ diff --git a/.github/workflows/dependencies_latest.yml b/.github/workflows/dependencies_latest.yml index 5757377159..2bc43d472b 100644 --- a/.github/workflows/dependencies_latest.yml +++ b/.github/workflows/dependencies_latest.yml @@ -102,7 +102,7 @@ jobs: run: | share/ci/scripts/multi/install_expat.sh 2.7.2 $EXT_PATH share/ci/scripts/multi/install_lcms2.sh 2.17 $EXT_PATH - share/ci/scripts/multi/install_yaml-cpp.sh 0.8.0 $EXT_PATH + share/ci/scripts/multi/install_yaml-cpp.sh 0.9.0 $EXT_PATH share/ci/scripts/multi/install_pystring.sh 1.1.4 $EXT_PATH share/ci/scripts/multi/install_pybind11.sh 3.0.1 $EXT_PATH share/ci/scripts/multi/install_zlib.sh 1.3.1 $EXT_PATH @@ -200,7 +200,7 @@ jobs: run: | share/ci/scripts/multi/install_expat.sh 2.7.2 $EXT_PATH share/ci/scripts/multi/install_lcms2.sh 2.17 $EXT_PATH - share/ci/scripts/multi/install_yaml-cpp.sh 0.8.0 $EXT_PATH + share/ci/scripts/multi/install_yaml-cpp.sh 0.9.0 $EXT_PATH share/ci/scripts/multi/install_pystring.sh 1.1.4 $EXT_PATH share/ci/scripts/multi/install_pybind11.sh 3.0.1 $EXT_PATH share/ci/scripts/multi/install_zlib.sh 1.3.1 $EXT_PATH @@ -314,7 +314,7 @@ jobs: # Minizip-ng depends on ZLIB. ZLIB must be installed first. run: | share/ci/scripts/multi/install_lcms2.sh 2.17 $EXT_PATH - share/ci/scripts/multi/install_yaml-cpp.sh 0.8.0 $EXT_PATH + share/ci/scripts/multi/install_yaml-cpp.sh 0.9.0 $EXT_PATH share/ci/scripts/multi/install_pystring.sh 1.1.4 $EXT_PATH share/ci/scripts/multi/install_pybind11.sh 3.0.1 $EXT_PATH share/ci/scripts/multi/install_expat.sh 2.7.2 $EXT_PATH diff --git a/.github/workflows/platform_latest.yml b/.github/workflows/platform_latest.yml index 1d173b2e65..35e56f8bec 100644 --- a/.github/workflows/platform_latest.yml +++ b/.github/workflows/platform_latest.yml @@ -153,7 +153,7 @@ jobs: -Dpystring_ROOT=${{ env.ocio_build_path }}/ext/dist \ -Dyaml-cpp_ROOT=${{ env.ocio_build_path }}/ext/dist \ -Dyaml-cpp_STATIC_LIBRARY=ON \ - -Dyaml-cpp_VERSION=0.8.0 \ + -Dyaml-cpp_VERSION=0.9.0 \ -DZLIB_ROOT=${{ env.ocio_build_path }}/ext/dist \ -DZLIB_STATIC_LIBRARY=ON \ -Dminizip-ng_ROOT=${{ env.ocio_build_path }}/ext/dist \ @@ -266,7 +266,7 @@ jobs: -Dpystring_ROOT=${{ env.ocio_build_path }}/ext/dist \ -Dyaml-cpp_ROOT=${{ env.ocio_build_path }}/ext/dist \ -Dyaml-cpp_STATIC_LIBRARY=ON \ - -Dyaml-cpp_VERSION=0.8.0 \ + -Dyaml-cpp_VERSION=0.9.0 \ -DZLIB_ROOT=${{ env.ocio_build_path }}/ext/dist \ -DZLIB_STATIC_LIBRARY=ON \ -Dminizip-ng_ROOT=${{ env.ocio_build_path }}/ext/dist \ @@ -383,7 +383,7 @@ jobs: -Dpystring_ROOT=${{ env.ocio_build_path }}/ext/dist \ -Dyaml-cpp_ROOT=${{ env.ocio_build_path }}/ext/dist \ -Dyaml-cpp_STATIC_LIBRARY=ON \ - -Dyaml-cpp_VERSION=0.8.0 \ + -Dyaml-cpp_VERSION=0.9.0 \ -DZLIB_ROOT=${{ env.ocio_build_path }}/ext/dist \ -DZLIB_STATIC_LIBRARY=ON \ -Dminizip-ng_ROOT=${{ env.ocio_build_path }}/ext/dist \ diff --git a/share/ci/scripts/multi/install_yaml-cpp.sh b/share/ci/scripts/multi/install_yaml-cpp.sh index f379ec584c..df15f0259f 100755 --- a/share/ci/scripts/multi/install_yaml-cpp.sh +++ b/share/ci/scripts/multi/install_yaml-cpp.sh @@ -20,9 +20,12 @@ cd yaml-cpp if [ "$YAMLCPP_VERSION" == "latest" ]; then LATEST_TAG=$(git describe --abbrev=0 --tags) git checkout tags/${LATEST_TAG} -b ${LATEST_TAG} -else - # From 0.8.0, tags are now simply the version number. +elif [ "$YAMLCPP_VERSION" == "0.8.0" ]; then + # 0.8.0 briefly used the bare version number as its tag; all other + # releases (including 0.9.0+) use the "yaml-cpp-" prefix. git checkout tags/${YAMLCPP_VERSION} -b ${YAMLCPP_VERSION} +else + git checkout tags/yaml-cpp-${YAMLCPP_VERSION} -b ${YAMLCPP_VERSION} fi mkdir build diff --git a/share/cmake/modules/FindExtPackages.cmake b/share/cmake/modules/FindExtPackages.cmake index df0d7af486..fa5680d1bb 100644 --- a/share/cmake/modules/FindExtPackages.cmake +++ b/share/cmake/modules/FindExtPackages.cmake @@ -63,7 +63,7 @@ ocio_handle_dependency( expat REQUIRED ALLOW_INSTALL # https://github.com/jbeder/yaml-cpp ocio_handle_dependency( yaml-cpp REQUIRED ALLOW_INSTALL MIN_VERSION 0.8.0 - RECOMMENDED_VERSION 0.8.0 + RECOMMENDED_VERSION 0.9.0 RECOMMENDED_VERSION_REASON "Latest version tested with OCIO") # pystring diff --git a/share/cmake/modules/install/Installyaml-cpp.cmake b/share/cmake/modules/install/Installyaml-cpp.cmake index 3ec993969c..fef885a157 100644 --- a/share/cmake/modules/install/Installyaml-cpp.cmake +++ b/share/cmake/modules/install/Installyaml-cpp.cmake @@ -113,8 +113,13 @@ if(NOT yaml-cpp_FOUND AND OCIO_INSTALL_EXT_PACKAGES AND NOT OCIO_INSTALL_EXT_PAC -DANDROID_STL=${ANDROID_STL}) endif() - # in v0.8.0 yaml switched from "yaml-cpp-vA.B.C" to "vA.B.C" format for tags. - set(yaml-cpp_GIT_TAG "${yaml-cpp_VERSION}") + # v0.8.0 briefly switched tags to the bare "A.B.C" format, but v0.9.0 reverted + # to the "yaml-cpp-A.B.C" format used by all other releases. + if(yaml-cpp_VERSION VERSION_EQUAL "0.8.0") + set(yaml-cpp_GIT_TAG "${yaml-cpp_VERSION}") + else() + set(yaml-cpp_GIT_TAG "yaml-cpp-${yaml-cpp_VERSION}") + endif() # Hack to let imported target be built from ExternalProject_Add file(MAKE_DIRECTORY ${yaml-cpp_INCLUDE_DIR}) diff --git a/tests/cpu/Config_tests.cpp b/tests/cpu/Config_tests.cpp index 5fc9300564..6a029bba2d 100644 --- a/tests/cpu/Config_tests.cpp +++ b/tests/cpu/Config_tests.cpp @@ -6536,7 +6536,7 @@ OCIO_ADD_TEST(Config, inactive_color_space_read_write) std::string configStr; configStr += InactiveCSConfigStart; // Test a multi-line list. - configStr += "inactive_colorspaces: [cs1\t\n \n, \ncs2]\n"; + configStr += "inactive_colorspaces: [cs1\t\n \n , \ncs2]\n"; configStr += InactiveCSConfigEnd; std::istringstream is; From 710be4b29191ed0741bed020c89254522c96e4aa Mon Sep 17 00:00:00 2001 From: Doug Walker Date: Mon, 21 Sep 2026 00:46:15 -0400 Subject: [PATCH 03/11] Fix minizip version compare bug Signed-off-by: Doug Walker --- src/OpenColorIO/OCIOZArchive.cpp | 10 +++++----- src/apps/ocioarchive/main.cpp | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/OpenColorIO/OCIOZArchive.cpp b/src/OpenColorIO/OCIOZArchive.cpp index 1caed9ecba..e1a1da79c4 100644 --- a/src/OpenColorIO/OCIOZArchive.cpp +++ b/src/OpenColorIO/OCIOZArchive.cpp @@ -226,7 +226,7 @@ void archiveConfig(std::ostream & ostream, const Config & config, const char * c std::string configStr = ss.str(); // Write zip to memory stream. -#if MZ_VERSION_BUILD >= 040000 +#if MZ_VERSION_BUILD >= 0x040000 write_mem_stream = mz_stream_mem_create(); #else mz_stream_mem_create(&write_mem_stream); @@ -242,7 +242,7 @@ void archiveConfig(std::ostream & ostream, const Config & config, const char * c options.compress_level = ArchiveCompressionLevels::BEST; // Create the writer handle. -#if MZ_VERSION_BUILD >= 040000 +#if MZ_VERSION_BUILD >= 0x040000 archiver = mz_zip_writer_create(); #else mz_zip_writer_create(&archiver); @@ -341,7 +341,7 @@ void ExtractOCIOZArchive(const char * archivePath, const char * destination) std::string outputDestination = pystring::os::path::normpath(destination); // Create zip reader. -#if MZ_VERSION_BUILD >= 040000 +#if MZ_VERSION_BUILD >= 0x040000 extracter = mz_zip_reader_create(); #else mz_zip_reader_create(&extracter); @@ -472,7 +472,7 @@ std::vector getFileStringFromArchiveFile(const std::string & filepath, std::vector buffer; // Create the reader object. -#if MZ_VERSION_BUILD >= 040000 +#if MZ_VERSION_BUILD >= 0x040000 reader = mz_zip_reader_create(); #else mz_zip_reader_create(&reader); @@ -536,7 +536,7 @@ void getEntriesMappingFromArchiveFile(const std::string & archivePath, void *reader = NULL; // Create the reader object. -#if MZ_VERSION_BUILD >= 040000 +#if MZ_VERSION_BUILD >= 0x040000 reader = mz_zip_reader_create(); #else mz_zip_reader_create(&reader); diff --git a/src/apps/ocioarchive/main.cpp b/src/apps/ocioarchive/main.cpp index 68054a6daf..a5abd03a10 100644 --- a/src/apps/ocioarchive/main.cpp +++ b/src/apps/ocioarchive/main.cpp @@ -235,7 +235,7 @@ int main(int argc, const char **argv) } std::string path = args[0]; -#if MZ_VERSION_BUILD >= 040000 +#if MZ_VERSION_BUILD >= 0x040000 reader = mz_zip_reader_create(); #else mz_zip_reader_create(&reader); From 82fb7f72c674443ed49babea1430b91151274977 Mon Sep 17 00:00:00 2001 From: Doug Walker Date: Mon, 21 Sep 2026 18:42:14 -0400 Subject: [PATCH 04/11] Update recommended versions Signed-off-by: Doug Walker --- .github/workflows/dependencies_latest.yml | 18 +++++++++--------- share/cmake/modules/FindExtPackages.cmake | 14 +++++++------- .../modules/install/Installminizip-ng.cmake | 1 + 3 files changed, 17 insertions(+), 16 deletions(-) diff --git a/.github/workflows/dependencies_latest.yml b/.github/workflows/dependencies_latest.yml index 2bc43d472b..c6adf82b35 100644 --- a/.github/workflows/dependencies_latest.yml +++ b/.github/workflows/dependencies_latest.yml @@ -100,13 +100,13 @@ jobs: - name: Install fixed ext package versions # Minizip-ng depends on ZLIB. ZLIB must be installed first. run: | - share/ci/scripts/multi/install_expat.sh 2.7.2 $EXT_PATH + share/ci/scripts/multi/install_expat.sh 2.8.4 $EXT_PATH share/ci/scripts/multi/install_lcms2.sh 2.17 $EXT_PATH share/ci/scripts/multi/install_yaml-cpp.sh 0.9.0 $EXT_PATH share/ci/scripts/multi/install_pystring.sh 1.1.4 $EXT_PATH share/ci/scripts/multi/install_pybind11.sh 3.0.1 $EXT_PATH - share/ci/scripts/multi/install_zlib.sh 1.3.1 $EXT_PATH - share/ci/scripts/multi/install_minizip-ng.sh 4.0.10 $EXT_PATH + share/ci/scripts/multi/install_zlib.sh 1.3.2 $EXT_PATH + share/ci/scripts/multi/install_minizip-ng.sh 4.2.2 $EXT_PATH - name: Install latest ext package versions run: | share/ci/scripts/multi/install_imath.sh latest $EXT_PATH @@ -198,13 +198,13 @@ jobs: - name: Install fixed ext package versions # Minizip-ng depends on ZLIB. ZLIB must be installed first. run: | - share/ci/scripts/multi/install_expat.sh 2.7.2 $EXT_PATH + share/ci/scripts/multi/install_expat.sh 2.8.4 $EXT_PATH share/ci/scripts/multi/install_lcms2.sh 2.17 $EXT_PATH share/ci/scripts/multi/install_yaml-cpp.sh 0.9.0 $EXT_PATH share/ci/scripts/multi/install_pystring.sh 1.1.4 $EXT_PATH share/ci/scripts/multi/install_pybind11.sh 3.0.1 $EXT_PATH - share/ci/scripts/multi/install_zlib.sh 1.3.1 $EXT_PATH - share/ci/scripts/multi/install_minizip-ng.sh 4.0.10 $EXT_PATH + share/ci/scripts/multi/install_zlib.sh 1.3.2 $EXT_PATH + share/ci/scripts/multi/install_minizip-ng.sh 4.2.2 $EXT_PATH - name: Install latest ext package versions run: | share/ci/scripts/multi/install_imath.sh latest $EXT_PATH @@ -317,9 +317,9 @@ jobs: share/ci/scripts/multi/install_yaml-cpp.sh 0.9.0 $EXT_PATH share/ci/scripts/multi/install_pystring.sh 1.1.4 $EXT_PATH share/ci/scripts/multi/install_pybind11.sh 3.0.1 $EXT_PATH - share/ci/scripts/multi/install_expat.sh 2.7.2 $EXT_PATH - share/ci/scripts/multi/install_zlib.sh 1.3.1 $EXT_PATH - share/ci/scripts/multi/install_minizip-ng.sh 4.0.10 $EXT_PATH + share/ci/scripts/multi/install_expat.sh 2.8.4 $EXT_PATH + share/ci/scripts/multi/install_zlib.sh 1.3.2 $EXT_PATH + share/ci/scripts/multi/install_minizip-ng.sh 4.2.2 $EXT_PATH shell: bash # OSL not installed due to LLVM compilation time. - name: Install latest ext package versions diff --git a/share/cmake/modules/FindExtPackages.cmake b/share/cmake/modules/FindExtPackages.cmake index fa5680d1bb..8896ee3492 100644 --- a/share/cmake/modules/FindExtPackages.cmake +++ b/share/cmake/modules/FindExtPackages.cmake @@ -56,8 +56,8 @@ message(STATUS "Checking for mandatory dependencies...") # https://github.com/libexpat/libexpat ocio_handle_dependency( expat REQUIRED ALLOW_INSTALL MIN_VERSION 2.6.0 - RECOMMENDED_VERSION 2.7.2 - RECOMMENDED_VERSION_REASON "CVE fixes and Latest version tested with OCIO") + RECOMMENDED_VERSION 2.8.4 + RECOMMENDED_VERSION_REASON "CVE fixes and latest version tested with OCIO") # yaml-cpp # https://github.com/jbeder/yaml-cpp @@ -77,7 +77,7 @@ ocio_handle_dependency( pystring REQUIRED ALLOW_INSTALL # https://github.com/AcademySoftwareFoundation/Imath ocio_handle_dependency( Imath REQUIRED ALLOW_INSTALL MIN_VERSION 3.1.1 - RECOMMENDED_VERSION 3.2.1 + RECOMMENDED_VERSION 3.2.3 RECOMMENDED_VERSION_REASON "Latest version tested with OCIO") ############################################################################### @@ -102,8 +102,8 @@ ocio_handle_dependency( Imath REQUIRED ALLOW_INSTALL # See https://github.com/madler/zlib/releases/tag/v1.2.13 ocio_handle_dependency( ZLIB REQUIRED ALLOW_INSTALL MIN_VERSION 1.2.13 # CVE fixes - RECOMMENDED_VERSION 1.3.1 - RECOMMENDED_VERSION_REASON "Latest version tested with OCIO" + RECOMMENDED_VERSION 1.3.2 + RECOMMENDED_VERSION_REASON "CVE fixes and latest version tested with OCIO" VERSION_VARS ZLIB_VERSION_STRING ZLIB_VERSION ) ############################################################################### @@ -111,8 +111,8 @@ ocio_handle_dependency( ZLIB REQUIRED ALLOW_INSTALL # minizip-ng # https://github.com/zlib-ng/minizip-ng ocio_handle_dependency( minizip-ng REQUIRED ALLOW_INSTALL - MIN_VERSION 4.0.0 - RECOMMENDED_VERSION 4.0.10 + MIN_VERSION 4.0.5 + RECOMMENDED_VERSION 4.2.2 RECOMMENDED_VERSION_REASON "Latest version tested with OCIO") ############################################################################### diff --git a/share/cmake/modules/install/Installminizip-ng.cmake b/share/cmake/modules/install/Installminizip-ng.cmake index 28b6e6a782..a790405a1e 100644 --- a/share/cmake/modules/install/Installminizip-ng.cmake +++ b/share/cmake/modules/install/Installminizip-ng.cmake @@ -78,6 +78,7 @@ if(NOT minizip-ng_FOUND AND OCIO_INSTALL_EXT_PACKAGES AND NOT OCIO_INSTALL_EXT_P -DMZ_LZMA=OFF -DMZ_LIBCOMP=OFF -DMZ_ZSTD=OFF + -DMZ_PPMD=OFF -DMZ_PKCRYPT=OFF -DMZ_WZAES=OFF -DMZ_SIGNING=OFF From 362003af7f896f8edc1ea87cf3082587845a7f47 Mon Sep 17 00:00:00 2001 From: Doug Walker Date: Mon, 21 Sep 2026 21:34:09 -0400 Subject: [PATCH 05/11] Build fixes Signed-off-by: Doug Walker --- share/ci/scripts/multi/install_minizip-ng.sh | 1 + share/cmake/modules/install/InstallZLIB.cmake | 14 +++++++++++++- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/share/ci/scripts/multi/install_minizip-ng.sh b/share/ci/scripts/multi/install_minizip-ng.sh index b8c5aab75a..6870479cbf 100755 --- a/share/ci/scripts/multi/install_minizip-ng.sh +++ b/share/ci/scripts/multi/install_minizip-ng.sh @@ -38,6 +38,7 @@ cmake -DCMAKE_BUILD_TYPE=Release \ -DMZ_LZMA=OFF \ -DMZ_LIBCOMP=OFF \ -DMZ_ZSTD=OFF \ + -DMZ_PPMD=OFF \ -DMZ_PKCRYPT=OFF \ -DMZ_WZAES=OFF \ -DMZ_SIGNING=OFF \ diff --git a/share/cmake/modules/install/InstallZLIB.cmake b/share/cmake/modules/install/InstallZLIB.cmake index 96f09bb09f..6d28583d29 100644 --- a/share/cmake/modules/install/InstallZLIB.cmake +++ b/share/cmake/modules/install/InstallZLIB.cmake @@ -37,7 +37,14 @@ if(NOT ZLIB_FOUND AND OCIO_INSTALL_EXT_PACKAGES AND NOT OCIO_INSTALL_EXT_PACKAGE if(WIN32) set(_ZLIB_LIB_NAME "zlib") - set(_ZLIB_STATIC_LIB_NAME "zlibstatic") + # zlib 1.3.2 rewrote its CMakeLists.txt and renamed the Windows static + # library output from "zlibstatic" to "zs" (OUTPUT_NAME z${zlib_static_suffix}). + # Keep the old name for versions prior to that so older pins still work. + if(ZLIB_VERSION VERSION_LESS "1.3.2") + set(_ZLIB_STATIC_LIB_NAME "zlibstatic") + else() + set(_ZLIB_STATIC_LIB_NAME "zs") + endif() else() set(_ZLIB_LIB_NAME "z") set(_ZLIB_STATIC_LIB_NAME "z") @@ -82,6 +89,11 @@ if(NOT ZLIB_FOUND AND OCIO_INSTALL_EXT_PACKAGES AND NOT OCIO_INSTALL_EXT_PACKAGE -DCMAKE_INSTALL_MESSAGE=${CMAKE_INSTALL_MESSAGE} -DCMAKE_INSTALL_PREFIX=${_EXT_DIST_ROOT} -DCMAKE_OBJECT_PATH_MAX=${CMAKE_OBJECT_PATH_MAX} + # ZLIB versions <=1.3.1 always install to ${CMAKE_INSTALL_PREFIX}/lib and ignore + # CMAKE_INSTALL_LIBDIR. Starting with 1.3.2, ZLIB uses GNUInstallDirs, which without + # this pre-seeded value would default to a platform-specific dir (e.g. lib64), + # breaking the hardcoded "${_ZLIB_INSTALL_LIBDIR}" path used below. + -DCMAKE_INSTALL_LIBDIR=${_ZLIB_INSTALL_LIBDIR} ) if(CMAKE_TOOLCHAIN_FILE) From 1a04bfab23ed1d157c19a9e6eb128ee50cf1a2ba Mon Sep 17 00:00:00 2001 From: Doug Walker Date: Mon, 21 Sep 2026 22:38:39 -0400 Subject: [PATCH 06/11] Fix expat 2.8 exception handling Signed-off-by: Doug Walker --- share/cmake/modules/install/InstallZLIB.cmake | 16 +- src/OpenColorIO/fileformats/FileFormatCTF.cpp | 101 +++++- .../fileformats/FileFormatIridasLook.cpp | 94 ++++- src/OpenColorIO/fileformats/cdl/CDLParser.cpp | 335 +++++++++++------- 4 files changed, 411 insertions(+), 135 deletions(-) diff --git a/share/cmake/modules/install/InstallZLIB.cmake b/share/cmake/modules/install/InstallZLIB.cmake index 6d28583d29..60a36766e1 100644 --- a/share/cmake/modules/install/InstallZLIB.cmake +++ b/share/cmake/modules/install/InstallZLIB.cmake @@ -35,6 +35,14 @@ if(NOT ZLIB_FOUND AND OCIO_INSTALL_EXT_PACKAGES AND NOT OCIO_INSTALL_EXT_PACKAGE set(_EXT_DIST_ROOT "${PROJECT_BINARY_DIR}/ext/dist") set(_EXT_BUILD_ROOT "${PROJECT_BINARY_DIR}/ext/build") + # Set find_package standard args + set(ZLIB_FOUND TRUE) + if(OCIO_ZLIB_RECOMMENDED_VERSION) + set(ZLIB_VERSION ${OCIO_ZLIB_RECOMMENDED_VERSION}) + else() + set(ZLIB_VERSION ${ZLIB_FIND_VERSION}) + endif() + if(WIN32) set(_ZLIB_LIB_NAME "zlib") # zlib 1.3.2 rewrote its CMakeLists.txt and renamed the Windows static @@ -50,14 +58,6 @@ if(NOT ZLIB_FOUND AND OCIO_INSTALL_EXT_PACKAGES AND NOT OCIO_INSTALL_EXT_PACKAGE set(_ZLIB_STATIC_LIB_NAME "z") endif() - # Set find_package standard args - set(ZLIB_FOUND TRUE) - if(OCIO_ZLIB_RECOMMENDED_VERSION) - set(ZLIB_VERSION ${OCIO_ZLIB_RECOMMENDED_VERSION}) - else() - set(ZLIB_VERSION ${ZLIB_FIND_VERSION}) - endif() - set(ZLIB_INCLUDE_DIRS "${_EXT_DIST_ROOT}/${CMAKE_INSTALL_INCLUDEDIR}") # Windows need the "d" suffix at the end (only for MSVC). diff --git a/src/OpenColorIO/fileformats/FileFormatCTF.cpp b/src/OpenColorIO/fileformats/FileFormatCTF.cpp index d4a0e7e42e..1373ba6e30 100644 --- a/src/OpenColorIO/fileformats/FileFormatCTF.cpp +++ b/src/OpenColorIO/fileformats/FileFormatCTF.cpp @@ -2,6 +2,7 @@ // Copyright Contributors to the OpenColorIO Project. #include +#include #include #include #include @@ -256,9 +257,22 @@ class XMLParserHelper { const int done = lastLine?1:0; - if (XML_STATUS_ERROR == XML_Parse(m_parser, - buffer.c_str(), - (int)buffer.size(), done)) + const XML_Status status = XML_Parse(m_parser, + buffer.c_str(), + (int)buffer.size(), done); + + // A handler may have thrown a C++ exception. Rethrowing it here, + // rather than letting it escape the expat callback directly, keeps + // expat's own bookkeeping (see XML_ParserFree's isCalledFromInsideHandler + // guard) consistent; otherwise XML_ParserFree silently leaks the parser. + if (m_pendingException) + { + std::exception_ptr pendingException = m_pendingException; + m_pendingException = nullptr; + std::rethrow_exception(pendingException); + } + + if (XML_STATUS_ERROR == status) { XML_Error eXpatErrorCode = XML_GetErrorCode(m_parser); if (eXpatErrorCode == XML_ERROR_TAG_MISMATCH) @@ -436,9 +450,37 @@ class XMLParserHelper } // Start the parsing of one element. + // Trampoline registered with expat. Catching exceptions here (rather than + // letting them escape into expat's C call stack) keeps expat's internal + // handler-depth bookkeeping consistent; see XML_ParserFree's + // isCalledFromInsideHandler guard, which otherwise leaks the parser. static void StartElementHandler(void * userData, const XML_Char * name_full, const XML_Char ** atts) + { + XMLParserHelper * pImpl = (XMLParserHelper*)userData; + try + { + StartElementHandlerImpl(userData, name_full, atts); + } + catch (...) + { + // Keep the first exception: for a self-closing element, expat may + // still invoke the matching EndElementHandler before it honors + // XML_StopParser, and that call can itself throw (since the + // element was never pushed onto m_elms); don't let that spurious, + // secondary exception overwrite the original one. + if (pImpl && !pImpl->m_pendingException) + { + pImpl->m_pendingException = std::current_exception(); + XML_StopParser(pImpl->m_parser, XML_FALSE); + } + } + } + + static void StartElementHandlerImpl(void * userData, + const XML_Char * name_full, + const XML_Char ** atts) { static const std::vector rangeSubElements = { TAG_MIN_IN_VALUE, @@ -1030,6 +1072,29 @@ class XMLParserHelper // End the parsing of one element. static void EndElementHandler(void * userData, const XML_Char * name_full) + { + XMLParserHelper * pImpl = (XMLParserHelper*)userData; + try + { + EndElementHandlerImpl(userData, name_full); + } + catch (...) + { + // Keep the first exception: for a self-closing element, expat may + // still invoke the matching EndElementHandler before it honors + // XML_StopParser, and that call can itself throw (since the + // element was never pushed onto m_elms); don't let that spurious, + // secondary exception overwrite the original one. + if (pImpl && !pImpl->m_pendingException) + { + pImpl->m_pendingException = std::current_exception(); + XML_StopParser(pImpl->m_parser, XML_FALSE); + } + } + } + + static void EndElementHandlerImpl(void * userData, + const XML_Char * name_full) { XMLParserHelper * pImpl = (XMLParserHelper*)userData; if (!pImpl || !name_full || !*name_full) @@ -1110,8 +1175,32 @@ class XMLParserHelper // Handle of strings within an element. static void CharacterDataHandler(void * userData, - const XML_Char * s, + const XML_Char * s, int len) + { + XMLParserHelper * pImpl = (XMLParserHelper*)userData; + try + { + CharacterDataHandlerImpl(userData, s, len); + } + catch (...) + { + // Keep the first exception: for a self-closing element, expat may + // still invoke the matching EndElementHandler before it honors + // XML_StopParser, and that call can itself throw (since the + // element was never pushed onto m_elms); don't let that spurious, + // secondary exception overwrite the original one. + if (pImpl && !pImpl->m_pendingException) + { + pImpl->m_pendingException = std::current_exception(); + XML_StopParser(pImpl->m_parser, XML_FALSE); + } + } + } + + static void CharacterDataHandlerImpl(void * userData, + const XML_Char * s, + int len) { XMLParserHelper * pImpl = (XMLParserHelper*)userData; if (!pImpl) @@ -1227,6 +1316,10 @@ class XMLParserHelper CTFReaderTransformPtr m_transform; int m_keepNamespaces = 0; // if >0, name spaces will be preserved + // Exception thrown from within an expat callback, deferred until control + // returns to Parse() so it is not thrown across expat's own C call stack. + std::exception_ptr m_pendingException; + }; bool isLoadableCTF(std::istream & istream) diff --git a/src/OpenColorIO/fileformats/FileFormatIridasLook.cpp b/src/OpenColorIO/fileformats/FileFormatIridasLook.cpp index e2c30cbf5f..db9b93fcef 100755 --- a/src/OpenColorIO/fileformats/FileFormatIridasLook.cpp +++ b/src/OpenColorIO/fileformats/FileFormatIridasLook.cpp @@ -3,6 +3,7 @@ #include #include +#include #include #include @@ -174,7 +175,20 @@ class XMLParserHelper { const int done = lastLine?1:0; - if (XML_STATUS_ERROR == XML_Parse(m_parser, buffer.c_str(), (int)buffer.size(), done)) + const XML_Status status = XML_Parse(m_parser, buffer.c_str(), (int)buffer.size(), done); + + // A handler may have thrown a C++ exception. Rethrowing it here, + // rather than letting it escape the expat callback directly, keeps + // expat's own bookkeeping (see XML_ParserFree's isCalledFromInsideHandler + // guard) consistent; otherwise XML_ParserFree silently leaks the parser. + if (m_pendingException) + { + std::exception_ptr pendingException = m_pendingException; + m_pendingException = nullptr; + std::rethrow_exception(pendingException); + } + + if (XML_STATUS_ERROR == status) { XML_Error eXpatErrorCode = XML_GetErrorCode(m_parser); if (eXpatErrorCode == XML_ERROR_TAG_MISMATCH) @@ -246,8 +260,35 @@ class XMLParserHelper throw Exception(os.str().c_str()); } - // Start the parsing of one element + // Trampoline registered with expat. Catching exceptions here (rather than + // letting them escape into expat's C call stack) keeps expat's internal + // handler-depth bookkeeping consistent; see XML_ParserFree's + // isCalledFromInsideHandler guard, which otherwise leaks the parser. static void StartElementHandler(void *userData, + const XML_Char *name, + const XML_Char **atts) + { + XMLParserHelper * pImpl = (XMLParserHelper*)userData; + try + { + StartElementHandlerImpl(userData, name, atts); + } + catch (...) + { + // Keep the first exception: for a self-closing element, expat may + // still invoke the matching EndElementHandler before it honors + // XML_StopParser, and that call can itself throw; don't let that + // spurious, secondary exception overwrite the original one. + if (pImpl && !pImpl->m_pendingException) + { + pImpl->m_pendingException = std::current_exception(); + XML_StopParser(pImpl->m_parser, XML_FALSE); + } + } + } + + // Start the parsing of one element + static void StartElementHandlerImpl(void *userData, const XML_Char *name, const XML_Char ** /*atts*/) { @@ -333,6 +374,28 @@ class XMLParserHelper // End the parsing of one element static void EndElementHandler(void *userData, const XML_Char *name) + { + XMLParserHelper * pImpl = (XMLParserHelper*)userData; + try + { + EndElementHandlerImpl(userData, name); + } + catch (...) + { + // Keep the first exception: for a self-closing element, expat may + // still invoke the matching EndElementHandler before it honors + // XML_StopParser, and that call can itself throw; don't let that + // spurious, secondary exception overwrite the original one. + if (pImpl && !pImpl->m_pendingException) + { + pImpl->m_pendingException = std::current_exception(); + XML_StopParser(pImpl->m_parser, XML_FALSE); + } + } + } + + static void EndElementHandlerImpl(void *userData, + const XML_Char *name) { XMLParserHelper * pImpl = (XMLParserHelper*)userData; if (!pImpl || !name || !*name) @@ -398,6 +461,29 @@ class XMLParserHelper static void CharacterDataHandler(void *userData, const XML_Char *s, int len) + { + XMLParserHelper * pImpl = (XMLParserHelper*)userData; + try + { + CharacterDataHandlerImpl(userData, s, len); + } + catch (...) + { + // Keep the first exception: for a self-closing element, expat may + // still invoke the matching EndElementHandler before it honors + // XML_StopParser, and that call can itself throw; don't let that + // spurious, secondary exception overwrite the original one. + if (pImpl && !pImpl->m_pendingException) + { + pImpl->m_pendingException = std::current_exception(); + XML_StopParser(pImpl->m_parser, XML_FALSE); + } + } + } + + static void CharacterDataHandlerImpl(void *userData, + const XML_Char *s, + int len) { XMLParserHelper * pImpl = (XMLParserHelper*)userData; if (!pImpl) @@ -467,6 +553,10 @@ class XMLParserHelper return m_fileName; } + // Exception thrown from within an expat callback, deferred until control + // returns to Parse() so it is not thrown across expat's own C call stack. + std::exception_ptr m_pendingException; + XML_Parser m_parser; unsigned m_lineNumber; std::string m_fileName; diff --git a/src/OpenColorIO/fileformats/cdl/CDLParser.cpp b/src/OpenColorIO/fileformats/cdl/CDLParser.cpp index e2945bdfbb..d832e8278e 100644 --- a/src/OpenColorIO/fileformats/cdl/CDLParser.cpp +++ b/src/OpenColorIO/fileformats/cdl/CDLParser.cpp @@ -1,6 +1,7 @@ // SPDX-License-Identifier: BSD-3-Clause // Copyright Contributors to the OpenColorIO Project. +#include #include #include "expat.h" @@ -157,6 +158,10 @@ class CDLParser::Impl std::string m_fileName; bool m_isCC; bool m_isCCC; + + // Exception thrown from within an expat callback, deferred until control + // returns to parse() so it is not thrown across expat's own C call stack. + std::exception_ptr m_pendingException; }; CDLParser::Impl::Impl(const std::string & fileName) @@ -242,10 +247,23 @@ void CDLParser::Impl::parse(const std::string & buffer, bool lastLine) { const int done = lastLine?1:0; - if (XML_STATUS_ERROR == XML_Parse(m_parser, - buffer.c_str(), - (int)buffer.size(), - done)) + const XML_Status status = XML_Parse(m_parser, + buffer.c_str(), + (int)buffer.size(), + done); + + // A handler may have thrown a C++ exception. Rethrowing it here, rather + // than letting it escape the expat callback directly, keeps expat's own + // bookkeeping (see XML_ParserFree's isCalledFromInsideHandler guard) + // consistent; otherwise XML_ParserFree silently leaks the parser. + if (m_pendingException) + { + std::exception_ptr pendingException = m_pendingException; + m_pendingException = nullptr; + std::rethrow_exception(pendingException); + } + + if (XML_STATUS_ERROR == status) { XML_Error eXpatErrorCode = XML_GetErrorCode(m_parser); if (eXpatErrorCode == XML_ERROR_TAG_MISMATCH) @@ -425,17 +443,32 @@ void CDLParser::Impl::StartElementHandlerCDL(void *userData, const XML_Char **atts) { CDLParser::Impl* pImpl = (CDLParser::Impl*)userData; - if (IsValidStartElement(pImpl, name)) + try { - if (HandleColorDecisionListStartElement(pImpl, name) || - HandleColorDecisionStartElement(pImpl, name) || - HandleColorCorrectionCDLStartElement(pImpl, name) || - HandleSOPNodeStartElement(pImpl, name) || - HandleSatNodeStartElement(pImpl, name) || - HandleTerminalStartElement(pImpl, name) || - HandleUnknownStartElement(pImpl, name)) + if (IsValidStartElement(pImpl, name)) { - pImpl->m_elms.back()->start(atts); + if (HandleColorDecisionListStartElement(pImpl, name) || + HandleColorDecisionStartElement(pImpl, name) || + HandleColorCorrectionCDLStartElement(pImpl, name) || + HandleSOPNodeStartElement(pImpl, name) || + HandleSatNodeStartElement(pImpl, name) || + HandleTerminalStartElement(pImpl, name) || + HandleUnknownStartElement(pImpl, name)) + { + pImpl->m_elms.back()->start(atts); + } + } + } + catch (...) + { + // Keep the first exception: for a self-closing element, expat may + // still invoke the matching EndElementHandler before it honors + // XML_StopParser, and that call can itself throw; don't let that + // spurious, secondary exception overwrite the original one. + if (pImpl && !pImpl->m_pendingException) + { + pImpl->m_pendingException = std::current_exception(); + XML_StopParser(pImpl->m_parser, XML_FALSE); } } } @@ -445,16 +478,31 @@ void CDLParser::Impl::StartElementHandlerCCC(void *userData, const XML_Char **atts) { CDLParser::Impl* pImpl = (CDLParser::Impl*)userData; - if (IsValidStartElement(pImpl, name)) + try + { + if (IsValidStartElement(pImpl, name)) + { + if (HandleColorCorrectionCollectionStartElement(pImpl, name) || + HandleColorCorrectionCCCStartElement(pImpl, name) || + HandleSOPNodeStartElement(pImpl, name) || + HandleSatNodeStartElement(pImpl, name) || + HandleTerminalStartElement(pImpl, name) || + HandleUnknownStartElement(pImpl, name)) + { + pImpl->m_elms.back()->start(atts); + } + } + } + catch (...) { - if (HandleColorCorrectionCollectionStartElement(pImpl, name) || - HandleColorCorrectionCCCStartElement(pImpl, name) || - HandleSOPNodeStartElement(pImpl, name) || - HandleSatNodeStartElement(pImpl, name) || - HandleTerminalStartElement(pImpl, name) || - HandleUnknownStartElement(pImpl, name)) + // Keep the first exception: for a self-closing element, expat may + // still invoke the matching EndElementHandler before it honors + // XML_StopParser, and that call can itself throw; don't let that + // spurious, secondary exception overwrite the original one. + if (pImpl && !pImpl->m_pendingException) { - pImpl->m_elms.back()->start(atts); + pImpl->m_pendingException = std::current_exception(); + XML_StopParser(pImpl->m_parser, XML_FALSE); } } } @@ -464,15 +512,30 @@ void CDLParser::Impl::StartElementHandlerCC(void *userData, const XML_Char **atts) { CDLParser::Impl* pImpl = (CDLParser::Impl*)userData; - if (IsValidStartElement(pImpl, name)) + try { - if (HandleColorCorrectionCCStartElement(pImpl, name) || - HandleSOPNodeStartElement(pImpl, name) || - HandleSatNodeStartElement(pImpl, name) || - HandleTerminalStartElement(pImpl, name) || - HandleUnknownStartElement(pImpl, name)) + if (IsValidStartElement(pImpl, name)) { - pImpl->m_elms.back()->start(atts); + if (HandleColorCorrectionCCStartElement(pImpl, name) || + HandleSOPNodeStartElement(pImpl, name) || + HandleSatNodeStartElement(pImpl, name) || + HandleTerminalStartElement(pImpl, name) || + HandleUnknownStartElement(pImpl, name)) + { + pImpl->m_elms.back()->start(atts); + } + } + } + catch (...) + { + // Keep the first exception: for a self-closing element, expat may + // still invoke the matching EndElementHandler before it honors + // XML_StopParser, and that call can itself throw; don't let that + // spurious, secondary exception overwrite the original one. + if (pImpl && !pImpl->m_pendingException) + { + pImpl->m_pendingException = std::current_exception(); + XML_StopParser(pImpl->m_parser, XML_FALSE); } } } @@ -817,65 +880,80 @@ bool CDLParser::Impl::HandleUnknownStartElement(CDLParser::Impl* pImpl, void CDLParser::Impl::EndElementHandler(void *userData, const XML_Char *name) { CDLParser::Impl* pImpl = (CDLParser::Impl*)userData; - if (!pImpl) + try { - throw Exception("Internal CDL parsing error."); - } - - if (!name || !*name) - { - pImpl->throwMessage("Internal parsing error"); - } - - // Is the expected element present? - ElementRcPtr pElt = pImpl->getBackElement(); - if (!pElt) - { - pImpl->throwMessage("Missing element"); - } - - // Is it the expected element? - if (pElt->getName() != name) - { - std::ostringstream os; - os << "Unexpected element ("; - os << name << "). "; - os << "Expecting ("; - os << pElt->getName() << "). "; + if (!pImpl) + { + throw Exception("Internal CDL parsing error."); + } - pImpl->throwMessage(os.str()); - } + if (!name || !*name) + { + pImpl->throwMessage("Internal parsing error"); + } - pImpl->m_elms.pop_back(); + // Is the expected element present? + ElementRcPtr pElt = pImpl->getBackElement(); + if (!pElt) + { + pImpl->throwMessage("Missing element"); + } - if (!pElt->isContainer() && !pElt->isDummy()) - { - // Is it a plain element? - auto pPlainElt = std::dynamic_pointer_cast(pElt); - if (!pPlainElt) + // Is it the expected element? + if (pElt->getName() != name) { std::ostringstream os; - os << "Unexpected attribute ("; - os << name << ")"; + os << "Unexpected element ("; + os << name << "). "; + os << "Expecting ("; + os << pElt->getName() << "). "; pImpl->throwMessage(os.str()); } - ElementRcPtr pParent = pImpl->getBackElement(); + pImpl->m_elms.pop_back(); - // Is it at the right location in the stack? - if ((!pParent || !pParent->isContainer() || - pParent != pPlainElt->getParent())) + if (!pElt->isContainer() && !pElt->isDummy()) { - std::ostringstream os; - os << "Parsing error ("; - os << name << ")"; + // Is it a plain element? + auto pPlainElt = std::dynamic_pointer_cast(pElt); + if (!pPlainElt) + { + std::ostringstream os; + os << "Unexpected attribute ("; + os << name << ")"; - pImpl->throwMessage(os.str()); + pImpl->throwMessage(os.str()); + } + + ElementRcPtr pParent = pImpl->getBackElement(); + + // Is it at the right location in the stack? + if ((!pParent || !pParent->isContainer() || + pParent != pPlainElt->getParent())) + { + std::ostringstream os; + os << "Parsing error ("; + os << name << ")"; + + pImpl->throwMessage(os.str()); + } } - } - pElt->end(); + pElt->end(); + } + catch (...) + { + // Keep the first exception: for a self-closing element, expat may + // still invoke the matching EndElementHandler before it honors + // XML_StopParser, and that call can itself throw; don't let that + // spurious, secondary exception overwrite the original one. + if (pImpl && !pImpl->m_pendingException) + { + pImpl->m_pendingException = std::current_exception(); + XML_StopParser(pImpl->m_parser, XML_FALSE); + } + } } void CDLParser::Impl::CharacterDataHandler(void *userData, @@ -883,61 +961,52 @@ void CDLParser::Impl::CharacterDataHandler(void *userData, int len) { CDLParser::Impl* pImpl = (CDLParser::Impl*)userData; - if (!pImpl) + try { - throw Exception("Internal CDL parsing error."); - } - - if (len == 0) return; - if (len<0 || !s || !*s) - { - pImpl->throwMessage("Empty attribute data"); - } - // Parsing a single new line. This is valid. - if (len == 1 && s[0] == '\n') return; + if (!pImpl) + { + throw Exception("Internal CDL parsing error."); + } - if (pImpl->m_elms.empty()) - { - pImpl->throwMessage("Unexpected character data before root element"); - } + if (len == 0) return; + if (len<0 || !s || !*s) + { + pImpl->throwMessage("Empty attribute data"); + } + // Parsing a single new line. This is valid. + if (len == 1 && s[0] == '\n') return; - ElementRcPtr pElt = pImpl->m_elms.back(); - if (!pElt) - { - std::ostringstream os; - os << "Missing eng tag ("; - os << std::string(s, len) << ")"; + if (pImpl->m_elms.empty()) + { + pImpl->throwMessage("Unexpected character data before root element"); + } - pImpl->throwMessage(os.str()); - } + ElementRcPtr pElt = pImpl->m_elms.back(); + if (!pElt) + { + std::ostringstream os; + os << "Missing eng tag ("; + os << std::string(s, len) << ")"; - auto pDescElt = std::dynamic_pointer_cast(pElt); - if (pDescElt) - { - // For description we keep the all the text. - pDescElt->setRawData(s, len, pImpl->getXmlLocation()); - } - else - { - // Ignore white-spaces. - size_t start = 0; - size_t end = len; - FindSubString(s, len, start, end); + pImpl->throwMessage(os.str()); + } - if (end>0) + auto pDescElt = std::dynamic_pointer_cast(pElt); + if (pDescElt) { - if (pElt->isContainer()) - { - std::ostringstream os; - os << "Illegal attribute ("; - os << std::string(s, len) << ")"; + // For description we keep the all the text. + pDescElt->setRawData(s, len, pImpl->getXmlLocation()); + } + else + { + // Ignore white-spaces. + size_t start = 0; + size_t end = len; + FindSubString(s, len, start, end); - pImpl->throwMessage(os.str()); - } - else + if (end>0) { - auto pPlainElt = std::dynamic_pointer_cast(pElt); - if (!pPlainElt) + if (pElt->isContainer()) { std::ostringstream os; os << "Illegal attribute ("; @@ -945,12 +1014,36 @@ void CDLParser::Impl::CharacterDataHandler(void *userData, pImpl->throwMessage(os.str()); } - pPlainElt->setRawData(s + start, - end - start, - pImpl->getXmlLocation()); + else + { + auto pPlainElt = std::dynamic_pointer_cast(pElt); + if (!pPlainElt) + { + std::ostringstream os; + os << "Illegal attribute ("; + os << std::string(s, len) << ")"; + + pImpl->throwMessage(os.str()); + } + pPlainElt->setRawData(s + start, + end - start, + pImpl->getXmlLocation()); + } } } } + catch (...) + { + // Keep the first exception: for a self-closing element, expat may + // still invoke the matching EndElementHandler before it honors + // XML_StopParser, and that call can itself throw; don't let that + // spurious, secondary exception overwrite the original one. + if (pImpl && !pImpl->m_pendingException) + { + pImpl->m_pendingException = std::current_exception(); + XML_StopParser(pImpl->m_parser, XML_FALSE); + } + } } CDLParser::CDLParser(const std::string& xmlFile) From a90c08adb6b7e0e7efe19b5894389734f3af79f4 Mon Sep 17 00:00:00 2001 From: Doug Walker Date: Tue, 22 Sep 2026 00:08:01 -0400 Subject: [PATCH 07/11] Add build diagnostics Signed-off-by: Doug Walker --- .github/workflows/ci_workflow.yml | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci_workflow.yml b/.github/workflows/ci_workflow.yml index 496c00fe90..da6f1e37f5 100644 --- a/.github/workflows/ci_workflow.yml +++ b/.github/workflows/ci_workflow.yml @@ -738,9 +738,19 @@ jobs: -DZLIB_STATIC_LIBRARY=ON \ -Dminizip-ng_ROOT=${{ env.ocio_build_path }}/ext/dist \ -Dminizip-ng_STATIC_LIBRARY=ON + set +e cmake --build . \ - --config ${{ matrix.build-type }} + --config ${{ matrix.build-type }} \ + -- -verbosity:detailed + build_rc=$? + echo "cmake --build exit code: $build_rc" + ls -la "./${{ matrix.build-type }}" export PATH=../../../_install/bin:$PATH ./${{ matrix.build-type }}/consumer + consumer_rc=$? + echo "consumer exit code: $consumer_rc" + set -e + test "$build_rc" -eq 0 + test "$consumer_rc" -eq 0 shell: bash working-directory: _build/tests/cmake-consumer-dist From faee6feb161c0ed8cc4ab4b319a6e0d7acb91087 Mon Sep 17 00:00:00 2001 From: Doug Walker Date: Tue, 22 Sep 2026 00:45:54 -0400 Subject: [PATCH 08/11] Add more diagnostics Signed-off-by: Doug Walker --- .github/workflows/ci_workflow.yml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci_workflow.yml b/.github/workflows/ci_workflow.yml index da6f1e37f5..2e345c7866 100644 --- a/.github/workflows/ci_workflow.yml +++ b/.github/workflows/ci_workflow.yml @@ -745,7 +745,11 @@ jobs: build_rc=$? echo "cmake --build exit code: $build_rc" ls -la "./${{ matrix.build-type }}" - export PATH=../../../_install/bin:$PATH + echo "--- ext/dist/lib ---" + ls -la "${{ env.ocio_build_path }}/ext/dist/lib" + echo "--- ext/dist/bin ---" + ls -la "${{ env.ocio_build_path }}/ext/dist/bin" 2>&1 + export PATH=../../../_install/bin:${{ env.ocio_build_path }}/ext/dist/bin:$PATH ./${{ matrix.build-type }}/consumer consumer_rc=$? echo "consumer exit code: $consumer_rc" From 8478c9c411a251df0ce1283d191f0842aa96bbf6 Mon Sep 17 00:00:00 2001 From: Doug Walker Date: Tue, 22 Sep 2026 01:19:53 -0400 Subject: [PATCH 09/11] Properly remove zlib library, revert diagnostics Signed-off-by: Doug Walker --- .github/workflows/ci_workflow.yml | 18 ++---------------- share/cmake/modules/install/InstallZLIB.cmake | 13 +++++++------ 2 files changed, 9 insertions(+), 22 deletions(-) diff --git a/.github/workflows/ci_workflow.yml b/.github/workflows/ci_workflow.yml index 2e345c7866..496c00fe90 100644 --- a/.github/workflows/ci_workflow.yml +++ b/.github/workflows/ci_workflow.yml @@ -738,23 +738,9 @@ jobs: -DZLIB_STATIC_LIBRARY=ON \ -Dminizip-ng_ROOT=${{ env.ocio_build_path }}/ext/dist \ -Dminizip-ng_STATIC_LIBRARY=ON - set +e cmake --build . \ - --config ${{ matrix.build-type }} \ - -- -verbosity:detailed - build_rc=$? - echo "cmake --build exit code: $build_rc" - ls -la "./${{ matrix.build-type }}" - echo "--- ext/dist/lib ---" - ls -la "${{ env.ocio_build_path }}/ext/dist/lib" - echo "--- ext/dist/bin ---" - ls -la "${{ env.ocio_build_path }}/ext/dist/bin" 2>&1 - export PATH=../../../_install/bin:${{ env.ocio_build_path }}/ext/dist/bin:$PATH + --config ${{ matrix.build-type }} + export PATH=../../../_install/bin:$PATH ./${{ matrix.build-type }}/consumer - consumer_rc=$? - echo "consumer exit code: $consumer_rc" - set -e - test "$build_rc" -eq 0 - test "$consumer_rc" -eq 0 shell: bash working-directory: _build/tests/cmake-consumer-dist diff --git a/share/cmake/modules/install/InstallZLIB.cmake b/share/cmake/modules/install/InstallZLIB.cmake index 60a36766e1..b935cb2c10 100644 --- a/share/cmake/modules/install/InstallZLIB.cmake +++ b/share/cmake/modules/install/InstallZLIB.cmake @@ -44,13 +44,14 @@ if(NOT ZLIB_FOUND AND OCIO_INSTALL_EXT_PACKAGES AND NOT OCIO_INSTALL_EXT_PACKAGE endif() if(WIN32) - set(_ZLIB_LIB_NAME "zlib") - # zlib 1.3.2 rewrote its CMakeLists.txt and renamed the Windows static - # library output from "zlibstatic" to "zs" (OUTPUT_NAME z${zlib_static_suffix}). - # Keep the old name for versions prior to that so older pins still work. + # zlib 1.3.2 rewrote its CMakeLists.txt and renamed the Windows shared and + # static library outputs from "zlib"/"zlibstatic" to "z"/"zs". + # Keep the old names for versions prior to that so older pins still work. if(ZLIB_VERSION VERSION_LESS "1.3.2") + set(_ZLIB_LIB_NAME "zlib") set(_ZLIB_STATIC_LIB_NAME "zlibstatic") else() + set(_ZLIB_LIB_NAME "z") set(_ZLIB_STATIC_LIB_NAME "zs") endif() else() @@ -143,9 +144,9 @@ if(NOT ZLIB_FOUND AND OCIO_INSTALL_EXT_PACKAGES AND NOT OCIO_INSTALL_EXT_PACKAGE ExternalProject_Add_Step( ZLIB_install zlib_remove_dll - COMMENT "Remove zlib.lib and zlib.dll, leaves only zlibstatic.lib" + COMMENT "Remove ${_ZLIB_LIB_NAME}.lib and ${_ZLIB_LIB_NAME}.dll, leaves only ${_ZLIB_STATIC_LIB_NAME}.lib" DEPENDEES install - COMMAND ${CMAKE_COMMAND} -E remove -f ${_EXT_DIST_ROOT}/${_ZLIB_INSTALL_LIBDIR}/zlib.lib ${_EXT_DIST_ROOT}/bin/zlib.dll + COMMAND ${CMAKE_COMMAND} -E remove -f ${_EXT_DIST_ROOT}/${_ZLIB_INSTALL_LIBDIR}/${_ZLIB_LIB_NAME}.lib ${_EXT_DIST_ROOT}/bin/${_ZLIB_LIB_NAME}.dll ) add_dependencies(ZLIB::ZLIB ZLIB_install) From c8e801eeda6f978149bf8e45bfee7c86f95d9901 Mon Sep 17 00:00:00 2001 From: Doug Walker Date: Tue, 22 Sep 2026 01:55:52 -0400 Subject: [PATCH 10/11] More zlib fixes Signed-off-by: Doug Walker --- share/cmake/modules/install/InstallZLIB.cmake | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/share/cmake/modules/install/InstallZLIB.cmake b/share/cmake/modules/install/InstallZLIB.cmake index b935cb2c10..13094b31a0 100644 --- a/share/cmake/modules/install/InstallZLIB.cmake +++ b/share/cmake/modules/install/InstallZLIB.cmake @@ -149,6 +149,20 @@ if(NOT ZLIB_FOUND AND OCIO_INSTALL_EXT_PACKAGES AND NOT OCIO_INSTALL_EXT_PACKAGE COMMAND ${CMAKE_COMMAND} -E remove -f ${_EXT_DIST_ROOT}/${_ZLIB_INSTALL_LIBDIR}/${_ZLIB_LIB_NAME}.lib ${_EXT_DIST_ROOT}/bin/${_ZLIB_LIB_NAME}.dll ) + if(WIN32 AND NOT _ZLIB_STATIC_LIB_NAME STREQUAL "zlibstatic") + # CMake's own FindZLIB.cmake (used by downstream consumers of OpenColorIOConfig.cmake) + # only ever looks for the static library under the name "zlibstatic", so alias the + # renamed 1.3.2+ output ("zs") to that name too. + ExternalProject_Add_Step( + ZLIB_install zlib_alias_static_lib + COMMENT "Alias ${_ZLIB_STATIC_LIB_NAME}.lib as zlibstatic.lib for downstream find_package(ZLIB)" + DEPENDEES zlib_remove_dll + COMMAND ${CMAKE_COMMAND} -E copy + ${_EXT_DIST_ROOT}/${_ZLIB_INSTALL_LIBDIR}/${_ZLIB_STATIC_LIB_NAME}${_ZLIB_LIB_SUFFIX}${CMAKE_STATIC_LIBRARY_SUFFIX} + ${_EXT_DIST_ROOT}/${_ZLIB_INSTALL_LIBDIR}/zlibstatic${_ZLIB_LIB_SUFFIX}${CMAKE_STATIC_LIBRARY_SUFFIX} + ) + endif() + add_dependencies(ZLIB::ZLIB ZLIB_install) # FindZLIB from CMake needs ZLIB_LIBRARY and ZLIB_INCLUDE_DIR. From c2d7de659f84941702a8b8fe50d903efdd4f903e Mon Sep 17 00:00:00 2001 From: Doug Walker Date: Tue, 22 Sep 2026 23:24:59 -0400 Subject: [PATCH 11/11] Address numpy problem in wheel workflow Signed-off-by: Doug Walker --- .github/workflows/wheel_workflow.yml | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/.github/workflows/wheel_workflow.yml b/.github/workflows/wheel_workflow.yml index d2602978da..56d65afba8 100644 --- a/.github/workflows/wheel_workflow.yml +++ b/.github/workflows/wheel_workflow.yml @@ -84,45 +84,62 @@ jobs: manylinux: manylinux_2_28 python: cp310-manylinux_x86_64 arch: x86_64 + numpy: numpy - build: CPython 3.11 64 bits manylinux_2_28 manylinux: manylinux_2_28 python: cp311-manylinux_x86_64 arch: x86_64 + numpy: numpy - build: CPython 3.12 64 bits manylinux_2_28 manylinux: manylinux_2_28 python: cp312-manylinux_x86_64 arch: x86_64 + numpy: numpy - build: CPython 3.13 64 bits manylinux_2_28 manylinux: manylinux_2_28 python: cp313-manylinux_x86_64 arch: x86_64 + numpy: numpy - build: CPython 3.14 64 bits manylinux_2_28 manylinux: manylinux_2_28 python: cp314-manylinux_x86_64 arch: x86_64 + numpy: numpy # ------------------------------------------------------------------- # CPython 64 bits manylinux2014 # ------------------------------------------------------------------- + # NumPy dropped manylinux2014 wheels as of 2.3.0 (and manylinux2014's + # toolchain is too old to build newer NumPy from source), so the test + # environment here must be pinned to the last version that still + # published manylinux2014 wheels. No NumPy release supports both + # manylinux2014 and cp314, so that combination skips NumPy entirely; + # the Python test suite tolerates its absence and skips only the + # NumPy-dependent cases. - build: CPython 3.10 64 bits manylinux2014 manylinux: manylinux2014 python: cp310-manylinux_x86_64 arch: x86_64 + numpy: "numpy<2.3" - build: CPython 3.11 64 bits manylinux2014 manylinux: manylinux2014 python: cp311-manylinux_x86_64 arch: x86_64 + numpy: "numpy<2.3" - build: CPython 3.12 64 bits manylinux2014 manylinux: manylinux2014 python: cp312-manylinux_x86_64 arch: x86_64 + numpy: "numpy<2.3" - build: CPython 3.13 64 bits manylinux2014 manylinux: manylinux2014 python: cp313-manylinux_x86_64 arch: x86_64 + numpy: "numpy<2.3" - build: CPython 3.14 64 bits manylinux2014 manylinux: manylinux2014 python: cp314-manylinux_x86_64 arch: x86_64 + numpy: "" steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 @@ -139,6 +156,7 @@ jobs: CIBW_ARCHS: ${{ matrix.arch }} CIBW_MANYLINUX_X86_64_IMAGE: ${{ matrix.manylinux }} CIBW_BEFORE_BUILD: "pip install sphinx-press-theme" + CIBW_TEST_REQUIRES: ${{ matrix.numpy }} - uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 with: @@ -162,26 +180,33 @@ jobs: # ------------------------------------------------------------------- # CPython ARM 64 bits manylinux2014 # ------------------------------------------------------------------- + # See the x86_64 manylinux2014 rows above for why the NumPy test + # dependency is pinned (and dropped for cp314) here. - build: CPython 3.10 ARM 64 bits manylinux2014 manylinux: manylinux2014 python: cp310-manylinux_aarch64 arch: aarch64 + numpy: "numpy<2.3" - build: CPython 3.11 ARM 64 bits manylinux2014 manylinux: manylinux2014 python: cp311-manylinux_aarch64 arch: aarch64 + numpy: "numpy<2.3" - build: CPython 3.12 ARM 64 bits manylinux2014 manylinux: manylinux2014 python: cp312-manylinux_aarch64 arch: aarch64 + numpy: "numpy<2.3" - build: CPython 3.13 ARM 64 bits manylinux2014 manylinux: manylinux2014 python: cp313-manylinux_aarch64 arch: aarch64 + numpy: "numpy<2.3" - build: CPython 3.14 ARM 64 bits manylinux2014 manylinux: manylinux2014 python: cp314-manylinux_aarch64 arch: aarch64 + numpy: "" steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 @@ -198,6 +223,7 @@ jobs: CIBW_ARCHS: ${{ matrix.arch }} CIBW_MANYLINUX_AARCH64_IMAGE: ${{ matrix.manylinux }} CIBW_BEFORE_BUILD: "pip install sphinx-press-theme" + CIBW_TEST_REQUIRES: ${{ matrix.numpy }} - uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 with: