diff --git a/.bazelversion b/.bazelversion index 66ce77b7ea..a8907c025d 100644 --- a/.bazelversion +++ b/.bazelversion @@ -1 +1 @@ -7.0.0 +7.0.2 diff --git a/.github/workflows/bazel_build.yml b/.github/workflows/bazel_build.yml index fc18837ed5..6db8eb5c11 100644 --- a/.github/workflows/bazel_build.yml +++ b/.github/workflows/bazel_build.yml @@ -45,10 +45,10 @@ jobs: runs-on: ubuntu-22.04 steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4.1.1 - name: Mount Bazel cache - uses: actions/cache@v3 + uses: actions/cache@v4.0.0 with: path: "/home/runner/.cache/bazel" key: bazel-ubuntu-22 @@ -66,10 +66,10 @@ jobs: runs-on: windows-2022 steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4.1.1 - name: Mount Bazel cache - uses: actions/cache@v3 + uses: actions/cache@v4.0.0 with: path: "/home/runner/.cache/bazel" key: bazel-windows-2022 @@ -87,10 +87,10 @@ jobs: runs-on: macos-13 steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4.1.1 - name: Mount Bazel cache - uses: actions/cache@v3 + uses: actions/cache@v4.0.0 with: path: "/home/runner/.cache/bazel" key: bazel-macos-13 @@ -102,3 +102,24 @@ jobs: # Test without bzlmod bazelisk build --noenable_bzlmod -- //... bazelisk test --noenable_bzlmod -- //... + + build_and_test_macos_M1: + name: macOS 14 Bazel build + runs-on: macos-14 + + steps: + - uses: actions/checkout@v4.1.1 + + - name: Mount Bazel cache + uses: actions/cache@v4.0.0 + with: + path: "/home/runner/.cache/bazel" + key: bazel-macos-14 + + - name: Build + run: | + bazelisk build //... + bazelisk test //... + # Test without bzlmod + bazelisk build --noenable_bzlmod -- //... + bazelisk test --noenable_bzlmod -- //... diff --git a/.github/workflows/ci_workflow.yml b/.github/workflows/ci_workflow.yml index 3aac7583e8..33670f472c 100644 --- a/.github/workflows/ci_workflow.yml +++ b/.github/workflows/ci_workflow.yml @@ -25,6 +25,7 @@ on: - 'website/src/**' - '!bazel/**' - '!src/wrappers/**' + - '!.github/workflows/python-**.yml' pull_request: branches-ignore: - RB-2.* @@ -38,6 +39,7 @@ on: - 'website/src/**' - '!bazel/**' - '!src/wrappers/**' + - '!.github/workflows/python-**.yml' permissions: contents: read @@ -238,8 +240,8 @@ jobs: run: yum install -y help2man - name: Configure run: | - cmake .. \ - -DCMAKE_INSTALL_PREFIX=../_install \ + cmake -B _build -S . \ + -DCMAKE_INSTALL_PREFIX=_install \ -DCMAKE_BUILD_TYPE=${{ matrix.build-type }} \ -DCMAKE_CXX_STANDARD=${{ matrix.cxx-standard }} \ -DCMAKE_CXX_FLAGS=${{ matrix.cxx-flags }} \ @@ -249,13 +251,11 @@ jobs: -DOPENEXR_INSTALL_DOCS='ON' \ -DOPENEXR_RUN_FUZZ_TESTS='OFF' \ -DOPENEXR_ENABLE_THREADING=${{ matrix.threads-enabled }} - working-directory: _build - name: Build run: | - cmake --build . \ + cmake --build _build \ --target install \ --config ${{ matrix.build-type }} - working-directory: _build - name: Validate run: | share/ci/scripts/linux/validate_openexr_libs.sh _install diff --git a/.github/workflows/python-wheels-publish-test.yml b/.github/workflows/python-wheels-publish-test.yml new file mode 100644 index 0000000000..6b46aef164 --- /dev/null +++ b/.github/workflows/python-wheels-publish-test.yml @@ -0,0 +1,102 @@ +# SPDX-License-Identifier: BSD-3-Clause +# Copyright (c) Contributors to the OpenEXR Project. + +name: Publish python distribution 📦 to TestPyPI + +on: + + # Publish python wheels to test.pypi when a release candidate is tagged, + # e.g. v3.4.5-rc, v3.4.5-rc6, etc. + + push: + tags: + - v3.[0-9]+.[0-9]+-rc* + workflow_dispatch: + +permissions: + contents: read + +jobs: + build: + name: Python Wheels - ${{ matrix.os }} + runs-on: ${{ matrix.os }} + strategy: + matrix: + os: [ubuntu-latest, macos-latest, windows-latest] + + environment: + name: testpypi + url: https://test.pypi.org/p/openexr + + permissions: + id-token: write + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Install Python + uses: actions/setup-python@v5 + with: + python-version: '3.x' + + - name: Create sdist + # Only create it once. + if: ${{ matrix.os == 'ubuntu-latest' }} + run: pipx run build --sdist . --outdir wheelhouse + + - name: Build wheel + uses: pypa/cibuildwheel@v2.16 + with: + output-dir: wheelhouse + env: + CIBW_ARCHS_LINUX: x86_64 + CIBW_ARCHS_MACOS: x86_64 arm64 universal2 + # Skip python 3.6 since scikit-build-core requires 3.7+ + # Skip 32-bit wheels builds on Windows + # Also skip the PyPy builds, since they fail the unit tests + CIBW_SKIP: cp36-* *-win32 *_i686 pp* + CIBW_TEST_SKIP: "*-macosx_universal2:arm64" + CIBW_ENVIRONMENT: OPENEXR_RELEASE_CANDIDATE_TAG="${{ github.ref_name }}" + + - name: Upload artifact + uses: actions/upload-artifact@v4.0.0 + with: + name: wheels-${{ matrix.os }} + path: | + ./wheelhouse/*.whl + ./wheelhouse/*.tar.gz + + publish-to-testpypi: + name: Publish Python 🐍 distribution 📦 to TestPyPI + needs: + - build + runs-on: ubuntu-latest + + environment: + name: testpypi + url: https://test.pypi.org/p/openexr + + permissions: + id-token: write + + steps: + - name: Download Linux artifacts + uses: actions/download-artifact@v4.0.0 + with: + name: wheels-ubuntu-latest + path: dist + - name: Download macOS artifacts + uses: actions/download-artifact@v4.0.0 + with: + name: wheels-macos-latest + path: dist + - name: Download Windows artifacts + uses: actions/download-artifact@v4.0.0 + with: + name: wheels-windows-latest + path: dist + - name: Publish distribution 📦 to TestPyPI + uses: pypa/gh-action-pypi-publish@release/v1 + with: + repository-url: https://test.pypi.org/legacy/ \ No newline at end of file diff --git a/.github/workflows/python-wheels-publish.yml b/.github/workflows/python-wheels-publish.yml new file mode 100644 index 0000000000..2330847df3 --- /dev/null +++ b/.github/workflows/python-wheels-publish.yml @@ -0,0 +1,96 @@ +# SPDX-License-Identifier: BSD-3-Clause +# Copyright (c) Contributors to the OpenEXR Project. + +name: Publish python distribution 📦 to PyPI + +on: + # Publish wheels to pypi on release + release: + types: [published] + workflow_dispatch: + +permissions: + contents: read + +jobs: + build: + name: Python Wheels - ${{ matrix.os }} + runs-on: ${{ matrix.os }} + strategy: + matrix: + os: [ubuntu-latest, macos-latest, windows-latest] + + environment: + name: pypi + url: https://pypi.org/p/openexr + + permissions: + id-token: write + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Install Python + uses: actions/setup-python@v5 + with: + python-version: '3.x' + + - name: Create sdist + # Only create it once. + if: ${{ matrix.os == 'ubuntu-latest' }} + run: pipx run build --sdist . --outdir wheelhouse + + - name: Build wheel + uses: pypa/cibuildwheel@v2.16 + with: + output-dir: wheelhouse + env: + CIBW_BUILD: cp312-* + CIBW_ARCHS_LINUX: x86_64 + CIBW_ARCHS_MACOS: x86_64 arm64 universal2 + # Skip python 3.6 since scikit-build-core requires 3.7+ + # Skip 32-bit wheels builds on Windows + # Also skip the PyPy builds, since they fail the unit tests + CIBW_SKIP: cp36-* *-win32 *_i686 pp* + CIBW_TEST_SKIP: "*arm64" + + - name: Upload artifact + uses: actions/upload-artifact@v4.0.0 + with: + name: wheels-${{ matrix.os }} + path: | + ./wheelhouse/*.whl + ./wheelhouse/*.tar.gz + + publish-to-pypi: + name: Publish Python 🐍 distribution 📦 to PyPI + needs: + - build + runs-on: ubuntu-latest + + environment: + name: pypi + url: https://pypi.org/p/openexr + + permissions: + id-token: write + + steps: + - name: Download Linux artifacts + uses: actions/download-artifact@v4.0.0 + with: + name: wheels-ubuntu-latest + path: dist + - name: Download macOS artifacts + uses: actions/download-artifact@v4.0.0 + with: + name: wheels-macos-latest + path: dist + - name: Download Windows artifacts + uses: actions/download-artifact@v4.0.0 + with: + name: wheels-windows-latest + path: dist + - name: Publish distribution 📦 to PyPI + uses: pypa/gh-action-pypi-publish@release/v1 diff --git a/.github/workflows/python-wheels.yml b/.github/workflows/python-wheels.yml index cc926ef5c7..516af01390 100644 --- a/.github/workflows/python-wheels.yml +++ b/.github/workflows/python-wheels.yml @@ -1,91 +1,74 @@ # SPDX-License-Identifier: BSD-3-Clause # Copyright (c) Contributors to the OpenEXR Project. -# -name: Python wheels +name: Python Wheels on: + + # Run on all changes (PR and push) to the python binding + # source/configuration files, except on the release branches, which + # have their own workflow, which also publish to pypi/test.pypi. + # Note that changes to the core libraries will *not* + # trigger building the wheels. However, the main ci workflow does + # build and test the bindings (for a single python version on a + # single arch) + push: branches-ignore: - - RB-2.* - tags-ignore: - - v1.* - - v2.* + - RB-* paths: - - '**' - - '!**.md' - - '!website/**' - - 'website/src/**' - - '!bazel/**' + - 'src/wrappers/python/**' + - 'pyproject.toml' + - '.github/workflows/python-wheels.yml' pull_request: branches-ignore: - - RB-2.* - tags-ignore: - - v1.* - - v2.* + - RB-* paths: - - '**' - - '!**.md' - - '!website/**' - - 'website/src/**' - - '!bazel/**' + - 'src/wrappers/python/**' + - 'pyproject.toml' + - '.github/workflows/python-wheels.yml' permissions: contents: read jobs: build_wheels: - name: Build Python wheels + name: Python Wheels - ${{ matrix.os }} runs-on: ${{ matrix.os }} strategy: matrix: - os: [ubuntu-22.04, windows-latest, macOS-latest] - env: - # On macOS we build both x86 and arm to support Intel and Apple Silicon. - CIBW_ARCHS_MACOS: x86_64 arm64 - # Skip 32-bit wheels builds on Windows. - # Also skip the PyPy builds, since they fail the unittests - CIBW_SKIP: "*-win32 *_i686 pp*" - # The CI platform is Intel based so we are doing cross compilation - # for arm64. It is not currently possible to test arm64 when cross - # compiling. - CIBW_TEST_SKIP: "*_arm64" - CIBW_BEFORE_BUILD: > - echo "Installing OpenEXR..." && - cd openexr.build && - cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=../openexr.install -DCMAKE_VERBOSE_MAKEFILE:BOOL=ON -DCMAKE_PREFIX_PATH=../openexr.install -DCMAKE_INSTALL_LIBDIR=lib -DBUILD_TESTING=OFF -DOPENEXR_INSTALL_EXAMPLES=OFF -DOPENEXR_BUILD_TOOLS=OFF -DBUILD_SHARED_LIBS=OFF -DOPENEXR_FORCE_INTERNAL_DEFLATE=ON -DOPENEXR_FORCE_INTERNAL_IMATH=ON -DCMAKE_POSITION_INDEPENDENT_CODE=ON ../ && - cmake --build ./ --config Release --clean-first && - cmake --install ./ --config Release && - cd .. - CIBW_TEST_REQUIRES: pytest - CIBW_TEST_COMMAND: pytest {project}/src/wrappers/python/tests/ + os: [ubuntu-latest, macos-latest, windows-latest] steps: - - uses: actions/checkout@v3 - - # Used to host cibuildwheel - - uses: actions/setup-python@v4 - with: - python-version: '3.x' - - name: Install cibuildwheel - run: python -m pip install cibuildwheel==2.16.2 + - name: Checkout + uses: actions/checkout@v4 - - name: Create setup.py - run: | - mv ${{github.workspace}}/src/wrappers/python/setup.py ${{github.workspace}}/setup.py - mv ${{github.workspace}}/src/wrappers/python/Imath.py ${{github.workspace}}/Imath.py - mv ${{github.workspace}}/src/wrappers/python/OpenEXR.cpp ${{github.workspace}}/OpenEXR.cpp + - name: Install Python + uses: actions/setup-python@v5 + with: + python-version: '3.x' - - name: Create folders - run: | - mkdir -p ${{github.workspace}}/openexr.build - mkdir -p ${{github.workspace}}/openexr.install + - name: Create sdist + # Only create it once. + if: ${{ matrix.os == 'ubuntu-latest' }} + run: pipx run build --sdist . --outdir wheelhouse - - name: Build wheels - run: python -m cibuildwheel --output-dir wheelhouse + - name: Build wheel + uses: pypa/cibuildwheel@v2.16 + env: + CIBW_ARCHS_MACOS: x86_64 arm64 universal2 + # Skip python 3.6 since scikit-build-core requires 3.7+ + # Skip 32-bit wheels builds on Windows + # Also skip the PyPy builds, since they fail the unit tests + CIBW_SKIP: cp36-* *-win32 *_i686 pp* + CIBW_TEST_SKIP: "*-macosx*arm64" - - uses: actions/upload-artifact@v3 + - name: Upload artifact + uses: actions/upload-artifact@v4 with: - name: "Python wheels" - path: ./wheelhouse/*.whl + name: wheels-${{ matrix.os }} + path: | + ./wheelhouse/*.whl + ./wheelhouse/*.tar.gz + diff --git a/.github/workflows/snyk-scan-pr.yml b/.github/workflows/snyk-scan-pr.yml deleted file mode 100644 index 5800833ee1..0000000000 --- a/.github/workflows/snyk-scan-pr.yml +++ /dev/null @@ -1,45 +0,0 @@ ---- -# SPDX-License-Identifier: BSD-3-Clause -# Copyright (c) Contributors to the OpenEXR Project. - -name: Snyk Scan Code - -on: - # https://docs.github.com/en/actions/learn-github-actions/workflow-syntax-for-github-actions - pull_request: - branches: - - main - paths: - - '**.h' - - '**.c' - - '**.cpp' - -jobs: - snyk-scan-pr: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - - uses: snyk/actions/setup@master - id: snyk - - - name: Snyk version - run: echo "${{ steps.snyk.outputs.version }}" - - - name: Snyk Auth - run: snyk auth ${{ secrets.SNYK_TOKEN }} - - - name: Snyk Scan Code - # Scan the C/C++ code for vulnerabilities using the Snyk CLI with the unmanaged flag - # https://docs.snyk.io/scan-using-snyk/supported-languages-and-frameworks/c-c++ for options - run: snyk test --unmanaged --print-dep-paths --org=${{ secrets.SNYK_ORG }} - env: - SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }} - continue-on-error: true # optional - - - name: Monitor for Vulnerabilities - # To import the test results (issues and dependencies) in the Snyk CLI, run the snyk monitor --unmanaged command: - run: snyk monitor --unmanaged --org=${{ secrets.SNYK_ORG }} - env: - SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }} - continue-on-error: true # optional diff --git a/.github/workflows/website_workflow.yml b/.github/workflows/website_workflow.yml index afd69e38d3..28edd16a6f 100644 --- a/.github/workflows/website_workflow.yml +++ b/.github/workflows/website_workflow.yml @@ -14,6 +14,8 @@ on: branches:-ignore: - RB-2.* - RB-3.* + tags-ignore: + - v3.[0-9]+.[0-9]+-rc* paths: - 'website/**' @@ -21,6 +23,8 @@ on: branches:-ignore: - RB-2.* - RB-3.* + tags-ignore: + - v3.[0-9]+.[0-9]+-rc* paths: - 'website/**' diff --git a/.gitignore b/.gitignore index db46b006ef..15be45fcfe 100644 --- a/.gitignore +++ b/.gitignore @@ -57,3 +57,4 @@ docs/_test_images/ # Ignore Bazel generated files bazel-* +MODULE.bazel.lock diff --git a/CMakeLists.txt b/CMakeLists.txt index 5a97330051..a4a49663db 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -48,8 +48,25 @@ set(OPENEXR_LIB_VERSION "${OPENEXR_LIB_SOVERSION}.${OPENEXR_VERSION}") # e.g. "3 option(OPENEXR_INSTALL "Install OpenEXR libraries" ON) option(OPENEXR_INSTALL_TOOLS "Install OpenEXR tools" ON) -if(OPENEXR_INSTALL_TOOLS AND NOT OPENEXR_INSTALL) - message(SEND_ERROR "OPENEXR_INSTALL_TOOLS requires OPENEXR_INSTALL") + +# uninstall target +if(NOT TARGET uninstall) + configure_file( + "${CMAKE_CURRENT_SOURCE_DIR}/cmake/cmake_uninstall.cmake.in" + "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake" + IMMEDIATE @ONLY) + add_custom_target(uninstall + COMMAND ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake) +endif() + +# uninstall target +if(NOT TARGET uninstall) + configure_file( + "${CMAKE_CURRENT_SOURCE_DIR}/cmake/cmake_uninstall.cmake.in" + "${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake" + IMMEDIATE @ONLY) + add_custom_target(uninstall + COMMAND ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_BINARY_DIR}/cmake_uninstall.cmake) endif() include(cmake/LibraryDefine.cmake) @@ -73,8 +90,13 @@ if(BUILD_TESTING AND NOT OPENEXR_IS_SUBPROJECT) endif() # Include these two modules without enable/disable options -add_subdirectory(src/lib) -add_subdirectory(src/bin) +if (OPENEXR_BUILD_LIBS) + add_subdirectory(src/lib) +endif() + +if(OPENEXR_BUILD_TOOLS AND OPENEXR_BUILD_LIBS) + add_subdirectory(src/bin) +endif() # Tell CMake where to find the OpenEXRConfig.cmake file. Makes it possible to call # find_package(OpenEXR) in downstream projects @@ -83,8 +105,7 @@ set(OpenEXR_DIR "${CMAKE_CURRENT_BINARY_DIR}/cmake" CACHE PATH "" FORCE) # Can be empty since we already defined the targets in add_subdirectory file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/cmake/OpenEXRTargets.cmake" "# Dummy file") -option(OPENEXR_INSTALL_EXAMPLES "Install OpenEXR examples" ON) -if(OPENEXR_INSTALL_EXAMPLES) +if(OPENEXR_BUILD_EXAMPLES AND OPENEXR_BUILD_LIBS) add_subdirectory( src/examples ) endif() @@ -112,7 +133,7 @@ endif() #set(CTEST_DROP_SITE_CDASH TRUE) include(CTest) -if(BUILD_TESTING AND NOT OPENEXR_IS_SUBPROJECT) +if(BUILD_TESTING AND OPENEXR_BUILD_LIBS AND NOT OPENEXR_IS_SUBPROJECT) add_subdirectory(src/test) endif() @@ -132,12 +153,11 @@ if (BUILD_WEBSITE AND NOT OPENEXR_IS_SUBPROJECT) add_subdirectory(website) endif() -if (NOT OPENEXR_IS_SUBPROJECT) +if (OPENEXR_BUILD_LIBS AND NOT OPENEXR_IS_SUBPROJECT) # Even if not building the website, still make sure the website example code compiles. add_subdirectory(website/src) endif() -option(OPENEXR_BUILD_PYTHON "Set ON to build python bindings") -if (OPENEXR_BUILD_PYTHON AND NOT OPENEXR_IS_SUBPROJECT) +if (OPENEXR_BUILD_PYTHON AND OPENEXR_BUILD_LIBS AND NOT OPENEXR_IS_SUBPROJECT) add_subdirectory(src/wrappers/python) endif() diff --git a/MODULE.bazel b/MODULE.bazel index 464b846470..7de42d5e64 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -7,6 +7,6 @@ module( ) bazel_dep(name = "bazel_skylib", version = "1.5.0") -bazel_dep(name = "imath", version = "3.1.9", repo_name = "Imath") +bazel_dep(name = "imath", version = "3.1.10", repo_name = "Imath") bazel_dep(name = "libdeflate", version = "1.19") bazel_dep(name = "platforms", version = "0.0.8") diff --git a/SECURITY.md b/SECURITY.md index e9686c6ab2..ecfda4fa19 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -6,14 +6,15 @@ ## Reporting a Vulnerability If you think you've found a potential vulnerability in OpenEXR, please -report it by emailing security@openexr.com. Only Technical Steering -Committee members and Academy Software Foundation project management -have access to these messages. Include detailed steps to reproduce the -issue, and any other information that could aid an investigation. Our -policy is to respond to vulnerability reports within 14 days. +report it by filing a GitHub [security +advisory](https://github.com/AcademySoftwareFoundation/openexr/security/advisories/new). Alternatively, +email security@openexr.com and provide your contact info for further +private/secure discussion. If your email does not receive a prompt +acknowledgement, your address may be blocked. -Our policy is to address critical security vulnerabilities rapidly and -post patches as quickly as possible. +Our policy is to acknowledge the receipt of vulnerability reports +within 48 hours. Our policy is to address critical security vulnerabilities +rapidly and post patches within 14 days if possible. ## Known Vulnerabilities @@ -57,3 +58,108 @@ These vulnerabilities are present in the given versions: See the [release notes](CHANGES.md) for more information. +## Supported Versions + +This gives guidance about which branches are supported with patches to +security vulnerabilities. + +| Version / branch | Supported | +| --------- | ---------------------------------------------------- | +| main | :white_check_mark: :construction: ALL fixes immediately, but this is a branch under development with a frequently unstable ABI and occasionally unstable API. | +| 3.2.x | :white_check_mark: All fixes that can be backported without breaking ABI compatibility. | +| 3.1.x | :warning: Only the most critical fixes, only if they can be easily backported. | +| 3.0.x | :warning: Only the most critical fixes, only if they can be easily backported. | +| 2.5.x | :warning: Only the most critical fixes, only if they can be easily backported. | +| <= 1.x | :x: No longer receiving patches of any kind. | + +## Security Expectations + +### Software Features + +- The OpenEXR project implements the EXR image file format, used + throughout the motion picture industry and beyond, on Linux, macOS, + and Windows. + +- The project consists of a software run-time library, implemented in + C/C++ and built via cmake, that reads and writes image data + files. The project also distributes python wrappings for the C/C++ + I/O API. + +- The library reads and writes binary image data and text-based + metadata, treated as blind data, none of which is executable code. + +- Other than the website and online technical documentation, the + project implements no web/online services or network communication + protocols. The library never requests any security or + authentication credentials or login information from users. + + The website implements no interactive features and requires no login + credentials. + +- The library reads and writes only to file paths specificly requested + via the C/C++ API. The runtime library uses no system configuration + files or sidecar data files. Access to data files uses only standard + file I/O system calls. + +- The library compresses/decompresses data via standard compression + algorithms but uses no cryptographic or confidentiality protocols. + +### Software Dependencies + +OpenEXR depends on +[Imath](https://github.com/AcademySoftwareFoundation/Imath), a library +of basic math operations also maintained and distributed by the +OpenEXR project. Imath follows the same security conventions +documented here for OpenEXR itself. The core Imath library has no +external dependencies. The Imath python bindings depend on python and +boost. + +The only +external library dependency of OpenEXR is +[libdeflate](https://github.com/ebiggers/libdeflate), which implements +standard deflate/zlib/gzip compression and decompression. + +The project uses +[Snyk](https://github.com/AcademySoftwareFoundation/openexr/blob/main/.github/workflows/snyk-scan-pr.yml) +to scan for dependency vulnerability. + +### Potential Vulnerabilities + +Potential entry points are images being loaded using the +library. Malformed images could caused issues such as heap buffer +overflows, out-of-memory faults, or segmentation faults that could be +exploitable as denial-of-service attacks. + +### Hardening + +#### Testing + +The OpenEXR project implements a comprehensive suite of validation +tests, including fuzz testing to harden against malicious input +data. Note that fuzz testing hardens only against *small* input data +files and is not a comprehensive test against all potential input. + +Note that the +[exrcheck](https://github.com/AcademySoftwareFoundation/openexr/tree/main/src/bin/exrcheck) +utility is intended to be used by testers to demonstrate a particular +proof-of-concept input file exposes a vulnerability, and it is very +helpful to let us know if a vulnerability can be reproduced using that +tool. + +The project also uses the [OSS +Fuzz](https://bugs.chromium.org/p/oss-fuzz) service for continuous +fuzz testing. + +#### Development Cycle and Distribution + +OpenEXR is downloadable and buildable by C/C++ source via GitHub. Only +members of the project's Technical Steering Committee, all veteran +software engineers at major motion picture studios or vendors, have +write permissions on the source code repository. All critical software +changes are reviewed by multiple TSC members. + +The library is distributed in binary form via many common package +managers across all platforms. + + + diff --git a/bazel/third_party/openexr_deps.bzl b/bazel/third_party/openexr_deps.bzl index 5bbd57adf4..4b8d99e4e3 100644 --- a/bazel/third_party/openexr_deps.bzl +++ b/bazel/third_party/openexr_deps.bzl @@ -22,9 +22,9 @@ def openexr_deps(): http_archive, name = "Imath", build_file = "@com_openexr//:bazel/third_party/Imath.BUILD", - strip_prefix = "Imath-3.1.9", - sha256 = "f1d8aacd46afed958babfced3190d2d3c8209b66da451f556abd6da94c165cf3", - urls = ["https://github.com/AcademySoftwareFoundation/Imath/archive/refs/tags/v3.1.9.tar.gz"], + strip_prefix = "Imath-3.1.10", + sha256 = "f2943e86bfb694e216c60b9a169e5356f8a90f18fbd34d7b6e3450be14f60b10", + urls = ["https://github.com/AcademySoftwareFoundation/Imath/archive/refs/tags/v3.1.10.tar.gz"], ) maybe( diff --git a/cmake/CMakeLists.txt b/cmake/CMakeLists.txt index 3a4dbf991e..9348702611 100644 --- a/cmake/CMakeLists.txt +++ b/cmake/CMakeLists.txt @@ -74,15 +74,6 @@ configure_file(OpenEXRConfigInternal.h.in ${CMAKE_CURRENT_BINARY_DIR}/OpenEXRCon # make a temp copy in the binary dir for OpenEXRConfig.h to include configure_file(../src/lib/OpenEXRCore/openexr_version.h ${CMAKE_CURRENT_BINARY_DIR}/OpenEXRCore/openexr_version.h COPYONLY) -if(OPENEXR_INSTALL) -install( - FILES - ${CMAKE_CURRENT_BINARY_DIR}/OpenEXRConfig.h - DESTINATION - ${CMAKE_INSTALL_INCLUDEDIR}/${OPENEXR_OUTPUT_SUBDIR} -) -endif() - ################################################### ####### IexConfig.h and IexConfigInternal.h diff --git a/cmake/LibraryDefine.cmake b/cmake/LibraryDefine.cmake index a242ec8519..39fa082ede 100644 --- a/cmake/LibraryDefine.cmake +++ b/cmake/LibraryDefine.cmake @@ -93,7 +93,8 @@ function(OPENEXR_DEFINE_LIBRARY libname) string(TOUPPER "${CMAKE_BUILD_TYPE}" uppercase_CMAKE_BUILD_TYPE) set(verlibname ${CMAKE_SHARED_LIBRARY_PREFIX}${libname}${OPENEXR_LIB_SUFFIX}${CMAKE_${uppercase_CMAKE_BUILD_TYPE}_POSTFIX}${CMAKE_SHARED_LIBRARY_SUFFIX}) set(baselibname ${CMAKE_SHARED_LIBRARY_PREFIX}${libname}${CMAKE_${uppercase_CMAKE_BUILD_TYPE}_POSTFIX}${CMAKE_SHARED_LIBRARY_SUFFIX}) - install(CODE "execute_process(COMMAND ${CMAKE_COMMAND} -E chdir \"\$ENV\{DESTDIR\}${CMAKE_INSTALL_FULL_LIBDIR}\" ${CMAKE_COMMAND} -E create_symlink ${verlibname} ${baselibname})") + file(CREATE_LINK ${verlibname} ${CMAKE_CURRENT_BINARY_DIR}/${baselibname} SYMBOLIC) + install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${baselibname} DESTINATION ${CMAKE_INSTALL_FULL_LIBDIR}) install(CODE "message(STATUS \"Creating symlink ${CMAKE_INSTALL_FULL_LIBDIR}/${baselibname} -> ${verlibname}\")") set(verlibname) set(baselibname) diff --git a/cmake/OpenEXRSetup.cmake b/cmake/OpenEXRSetup.cmake index a70f86d2f4..f3e84bf984 100644 --- a/cmake/OpenEXRSetup.cmake +++ b/cmake/OpenEXRSetup.cmake @@ -51,8 +51,23 @@ option(OPENEXR_ENABLE_LARGE_STACK "Enables code to take advantage of large stack ######################## ## Build related options -# Whether to build & install the various command line utility programs +option(OPENEXR_INSTALL "Install OpenEXR libraries/binaries/bindings" ON) + +# Whether to build & install the main libraries +option(OPENEXR_BUILD_LIBS "Enables building of main libraries" ON) + +# Whether to build the various command line utility programs option(OPENEXR_BUILD_TOOLS "Enables building of utility programs" ON) +option(OPENEXR_INSTALL_TOOLS "Install OpenEXR tools" ON) + +option(OPENEXR_BUILD_EXAMPLES "Build OpenEXR examples" ON) +option(OPENEXR_INSTALL_EXAMPLES "Install OpenEXR examples" ON) + +option(OPENEXR_BUILD_PYTHON "Build python bindings" OFF) + +option(OPENEXR_TEST_LIBRARIES "Run library tests" ON) +option(OPENEXR_TEST_TOOLS "Run tool tests" ON) +option(OPENEXR_TEST_PYTHON "Run python binding tests" ON) # This is a variable here for use in controlling where include files are # installed. Care must be taken when changing this, as many things @@ -138,6 +153,10 @@ if(OPENEXR_USE_CLANG_TIDY) ) endif() +if (NOT OPENEXR_BUILD_LIBS) + return() +endif() + ############################### # Dependent libraries @@ -246,10 +265,8 @@ endif() option(OPENEXR_FORCE_INTERNAL_IMATH "Force using an internal imath" OFF) # Check to see if Imath is installed outside of the current build directory. -set(OPENEXR_IMATH_REPO "https://github.com/AcademySoftwareFoundation/Imath.git" CACHE STRING - "Repo for auto-build of Imath") -set(OPENEXR_IMATH_TAG "main" CACHE STRING - "Tag for auto-build of Imath (branch, tag, or SHA)") +set(OPENEXR_IMATH_REPO "https://github.com/AcademySoftwareFoundation/Imath.git" CACHE STRING "Repo for auto-build of Imath") +set(OPENEXR_IMATH_TAG "main" CACHE STRING "Tag for auto-build of Imath (branch, tag, or SHA)") if(NOT OPENEXR_FORCE_INTERNAL_IMATH) #TODO: ^^ Release should not clone from main, this is a place holder set(CMAKE_IGNORE_PATH "${CMAKE_CURRENT_BINARY_DIR}/_deps/imath-src/config;${CMAKE_CURRENT_BINARY_DIR}/_deps/imath-build/config") @@ -274,10 +291,13 @@ if(NOT TARGET Imath::Imath AND NOT Imath_FOUND) if(NOT Imath_POPULATED) FetchContent_Populate(Imath) + # Propagate OpenEXR's install setting to Imath + set(IMATH_INSTALL ${OPENEXR_INSTALL}) + # Propagate OpenEXR's setting for pkg-config generation to Imath: # If OpenEXR is generating it, the internal Imath should, too. set(IMATH_INSTALL_PKG_CONFIG ${OPENEXR_INSTALL_PKG_CONFIG}) - + # hrm, cmake makes Imath lowercase for the properties (to imath) add_subdirectory(${imath_SOURCE_DIR} ${imath_BINARY_DIR}) endif() diff --git a/cmake/cmake_uninstall.cmake.in b/cmake/cmake_uninstall.cmake.in new file mode 100644 index 0000000000..1e5d2bb876 --- /dev/null +++ b/cmake/cmake_uninstall.cmake.in @@ -0,0 +1,23 @@ +# Source: https://gitlab.kitware.com/cmake/community/-/wikis/FAQ#can-i-do-make-uninstall-with-cmake + +if(NOT EXISTS "@CMAKE_BINARY_DIR@/install_manifest.txt") + message(FATAL_ERROR "Cannot find install manifest: @CMAKE_BINARY_DIR@/install_manifest.txt") +endif() + +file(READ "@CMAKE_BINARY_DIR@/install_manifest.txt" files) +string(REGEX REPLACE "\n" ";" files "${files}") +foreach(file ${files}) + message(STATUS "Uninstalling $ENV{DESTDIR}${file}") + if(IS_SYMLINK "$ENV{DESTDIR}${file}" OR EXISTS "$ENV{DESTDIR}${file}") + exec_program( + "@CMAKE_COMMAND@" ARGS + "-E remove \"$ENV{DESTDIR}${file}\"" + OUTPUT_VARIABLE rm_out + RETURN_VALUE rm_retval) + if(NOT "${rm_retval}" STREQUAL 0) + message(FATAL_ERROR "Problem when removing $ENV{DESTDIR}${file}") + endif() + else(IS_SYMLINK "$ENV{DESTDIR}${file}" OR EXISTS "$ENV{DESTDIR}${file}") + message(STATUS "File $ENV{DESTDIR}${file} does not exist.") + endif() +endforeach() diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000000..fdc7b0370a --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,60 @@ +# SPDX-License-Identifier: BSD-3-Clause +# Copyright (c) Contributors to the OpenEXR Project. + +[build-system] +requires = ["scikit-build-core==0.8.0"] +build-backend = "scikit_build_core.build" + +[project] +name = "OpenEXR" +dynamic = ["version"] + +description="Python bindings for the OpenEXR image file format" +readme = "src/wrappers/python/README.md" +authors = [ + { name="Contributors to the OpenEXR project", email="info@openexr.com" }, +] +requires-python = ">=3.7" + +[project.urls] +"Homepage" = "https://openexr.com" +"Source" = "https://github.com/AcademySoftwareFoundation/OpenEXR" +"Bug Tracker" = "https://github.com/AcademySoftwareFoundation/OpenEXR/issues" + +[project.optional-dependencies] +test = ["pytest"] + +[tool.scikit-build] +wheel.expand-macos-universal-tags = true +sdist.exclude = [".github", "src/test", "src/examples", "website", "ASWF", "bazel", "share"] +cmake.targets = ["PyOpenEXR"] +# Enable experimental features if any are available +# In this case we need custom local plugin to get +# the project version from cmake. +experimental = true +metadata.version.provider = "openexr_skbuild_plugin" +metadata.version.provider-path = "./src/wrappers/python" + +[tool.scikit-build.cmake.define] +OPENEXR_INSTALL = 'OFF' +OPENEXR_BUILD_PYTHON = 'ON' +OPENEXR_BUILD_EXAMPLES = 'OFF' +OPENEXR_INSTALL_EXAMPLES = 'OFF' +OPENEXR_BUILD_TOOLS = 'OFF' +OPENEXR_INSTALL_TOOLS = 'OFF' +OPENEXR_INSTALL_PKG_CONFIG = 'OFF' +OPENEXR_FORCE_INTERNAL_DEFLATE = 'ON' +OPENEXR_FORCE_INTERNAL_IMATH = 'ON' +OPENEXR_TEST_LIBRARIES = 'OFF' +BUILD_SHARED_LIBS = 'OFF' +CMAKE_POSITION_INDEPENDENT_CODE = 'ON' + +[tool.cibuildwheel] +test-command = "ctest -R PyOpenEXR" +test-extras = ["test"] +test-skip = ["*universal2:arm64"] +build-verbosity = 1 + +manylinux-x86_64-image = "manylinux2014" +manylinux-i686-image = "manylinux2014" +manylinux-aarch64-image = "manylinux2014" diff --git a/src/bin/CMakeLists.txt b/src/bin/CMakeLists.txt index 893fd58d11..99785681b1 100644 --- a/src/bin/CMakeLists.txt +++ b/src/bin/CMakeLists.txt @@ -1,19 +1,20 @@ # SPDX-License-Identifier: BSD-3-Clause # Copyright (c) Contributors to the OpenEXR Project. -########################## -# Binaries / Utilities -########################## -if(OPENEXR_BUILD_TOOLS) - add_subdirectory( exr2aces ) - add_subdirectory( exrheader ) - add_subdirectory( exrinfo ) - add_subdirectory( exrmaketiled ) - add_subdirectory( exrstdattr ) - add_subdirectory( exrmakepreview ) - add_subdirectory( exrenvmap ) - add_subdirectory( exrmultiview ) - add_subdirectory( exrmultipart ) - add_subdirectory( exrcheck ) - add_subdirectory( exrmanifest ) -endif() +################## +# Binaries / Tools +################## + +message(STATUS "Building OpenEXR tools") + +add_subdirectory( exr2aces ) +add_subdirectory( exrheader ) +add_subdirectory( exrinfo ) +add_subdirectory( exrmaketiled ) +add_subdirectory( exrstdattr ) +add_subdirectory( exrmakepreview ) +add_subdirectory( exrenvmap ) +add_subdirectory( exrmultiview ) +add_subdirectory( exrmultipart ) +add_subdirectory( exrcheck ) +add_subdirectory( exrmanifest ) diff --git a/src/examples/CMakeLists.txt b/src/examples/CMakeLists.txt index ea34cb42c9..d6d092aee0 100644 --- a/src/examples/CMakeLists.txt +++ b/src/examples/CMakeLists.txt @@ -6,6 +6,8 @@ # standalone program linking against an already-installed OpenEXR # library. +message(STATUS "Building OpenEXR examples") + if("${CMAKE_PROJECT_NAME}" STREQUAL "") cmake_minimum_required(VERSION 3.12) project(OpenEXRExamples) diff --git a/src/lib/CMakeLists.txt b/src/lib/CMakeLists.txt index a315e444c5..8b4b5f0fa8 100644 --- a/src/lib/CMakeLists.txt +++ b/src/lib/CMakeLists.txt @@ -1,6 +1,8 @@ # SPDX-License-Identifier: BSD-3-Clause # Copyright (c) Contributors to the OpenEXR Project. +message(STATUS "Building OpenEXR libraries") + add_subdirectory( Iex ) add_subdirectory( IlmThread ) diff --git a/src/lib/OpenEXRCore/chunk.c b/src/lib/OpenEXRCore/chunk.c index 304a402ccc..89f582dc92 100644 --- a/src/lib/OpenEXRCore/chunk.c +++ b/src/lib/OpenEXRCore/chunk.c @@ -590,13 +590,15 @@ extract_chunk_table ( // something similar, except when in strict mode, we // will fail with a corrupt chunk immediately. rv = reconstruct_chunk_table (ctxt, part, ctable); - if (rv != EXR_ERR_SUCCESS && ctxt->strict_header) + if (rv != EXR_ERR_SUCCESS) { ctxt->free_fn (ctable); - return ctxt->report_error ( - ctxt, - EXR_ERR_BAD_CHUNK_LEADER, - "Incomplete / corrupt chunk table, unable to reconstruct"); + ctable = (uint64_t*) UINTPTR_MAX; + if (ctxt->strict_header) + return ctxt->report_error ( + ctxt, + EXR_ERR_BAD_CHUNK_LEADER, + "Incomplete / corrupt chunk table, unable to reconstruct"); } } } @@ -609,7 +611,8 @@ extract_chunk_table ( &eptr, nptr)) { - ctxt->free_fn (ctable); + if (nptr != UINTPTR_MAX) + ctxt->free_fn (ctable); ctable = (uint64_t*) eptr; if (ctable == NULL) return ctxt->standard_error (ctxt, EXR_ERR_OUT_OF_MEMORY); @@ -617,7 +620,7 @@ extract_chunk_table ( } *chunktable = ctable; - return EXR_ERR_SUCCESS; + return ((uintptr_t)ctable) == UINTPTR_MAX ? EXR_ERR_BAD_CHUNK_LEADER : EXR_ERR_SUCCESS; } /**************************************/ diff --git a/src/lib/OpenEXRCore/decoding.c b/src/lib/OpenEXRCore/decoding.c index 322cbd8965..66c59dc47e 100644 --- a/src/lib/OpenEXRCore/decoding.c +++ b/src/lib/OpenEXRCore/decoding.c @@ -288,6 +288,7 @@ default_decompress_chunk (exr_decode_pipeline_t* decode) uint64_t sampsize = (((uint64_t) decode->chunk.width) * ((uint64_t) decode->chunk.height)); + sampsize *= sizeof (int32_t); rv = decompress_data ( @@ -340,7 +341,7 @@ unpack_sample_table ( exr_result_t rv = EXR_ERR_SUCCESS; int32_t w = decode->chunk.width; int32_t h = decode->chunk.height; - int32_t totsamp = 0; + uint64_t totsamp = 0; int32_t* samptable = decode->sample_count_table; size_t combSampSize = 0; @@ -351,38 +352,44 @@ unpack_sample_table ( { for (int32_t y = 0; y < h; ++y) { + int32_t *cursampline = samptable + y * w; int32_t prevsamp = 0; for (int32_t x = 0; x < w; ++x) { int32_t nsamps = - (int32_t) one_to_native32 ((uint32_t) samptable[y * w + x]); - if (nsamps < 0) return EXR_ERR_INVALID_SAMPLE_DATA; - samptable[y * w + x] = nsamps - prevsamp; - prevsamp = nsamps; + (int32_t) one_to_native32 ((uint32_t) cursampline[x]); + if (nsamps < prevsamp) return EXR_ERR_INVALID_SAMPLE_DATA; + + cursampline[x] = nsamps - prevsamp; + prevsamp = nsamps; } - totsamp += prevsamp; + totsamp += (uint64_t)prevsamp; } - samptable[w * h] = totsamp; + if (totsamp >= (uint64_t)INT32_MAX) + return EXR_ERR_INVALID_SAMPLE_DATA; + samptable[w * h] = (int32_t)totsamp; } else { for (int32_t y = 0; y < h; ++y) { + int32_t *cursampline = samptable + y * w; int32_t prevsamp = 0; for (int32_t x = 0; x < w; ++x) { int32_t nsamps = - (int32_t) one_to_native32 ((uint32_t) samptable[y * w + x]); - if (nsamps < 0) return EXR_ERR_INVALID_SAMPLE_DATA; - samptable[y * w + x] = nsamps; - prevsamp = nsamps; + (int32_t) one_to_native32 ((uint32_t) cursampline[x]); + if (nsamps < prevsamp) return EXR_ERR_INVALID_SAMPLE_DATA; + + cursampline[x] = nsamps; + prevsamp = nsamps; } - totsamp += prevsamp; + + totsamp += (uint64_t)prevsamp; } } - if (totsamp < 0 || - (((uint64_t) totsamp) * combSampSize) > decode->chunk.unpacked_size) + if ((totsamp * combSampSize) > decode->chunk.unpacked_size) { rv = pctxt->report_error ( pctxt, EXR_ERR_INVALID_SAMPLE_DATA, "Corrupt sample count table"); diff --git a/src/lib/OpenEXRCore/internal_structs.c b/src/lib/OpenEXRCore/internal_structs.c index ef39ca46d6..477e2872e6 100644 --- a/src/lib/OpenEXRCore/internal_structs.c +++ b/src/lib/OpenEXRCore/internal_structs.c @@ -174,7 +174,7 @@ internal_exr_destroy_part ( ctable = (uint64_t*) atomic_load (&(cur->chunk_table)); atomic_store (&(cur->chunk_table), (uintptr_t) (0)); #endif - if (ctable) dofree (ctable); + if (ctable && ((uintptr_t)ctable) != UINTPTR_MAX) dofree (ctable); } /**************************************/ diff --git a/src/lib/OpenEXRCore/unpack.c b/src/lib/OpenEXRCore/unpack.c index b88c98974c..1324508c5b 100644 --- a/src/lib/OpenEXRCore/unpack.c +++ b/src/lib/OpenEXRCore/unpack.c @@ -1196,9 +1196,10 @@ generic_unpack_deep_pointers (exr_decode_pipeline_t* decode) if (outpix) { uint8_t* cdata = outpix; + UNPACK_SAMPLES (samps) } - srcbuffer += bpc * samps; + srcbuffer += ((size_t) bpc) * ((size_t) samps); } } sampbuffer += w; @@ -1242,12 +1243,14 @@ generic_unpack_deep (exr_decode_pipeline_t* decode) } else prevsamps = sampbuffer[w - 1]; + srcbuffer += ((size_t) bpc) * ((size_t) prevsamps); if (incr_tot) totsamps += (size_t) prevsamps; continue; } + cdata += totsamps * ((size_t) ubpc); for (int x = 0; x < w; ++x) @@ -1263,7 +1266,7 @@ generic_unpack_deep (exr_decode_pipeline_t* decode) UNPACK_SAMPLES (samps) - srcbuffer += bpc * samps; + srcbuffer += ((size_t) bpc) * ((size_t) samps); if (incr_tot) totsamps += (size_t) samps; } } @@ -1301,7 +1304,7 @@ internal_exr_match_decode ( if (isdeep) { - if ((decode->decode_flags & EXR_DECODE_SAMPLE_COUNTS_AS_INDIVIDUAL)) + if ((decode->decode_flags & EXR_DECODE_NON_IMAGE_DATA_AS_POINTERS)) return &generic_unpack_deep_pointers; return &generic_unpack_deep; } diff --git a/src/lib/OpenEXRUtil/ImfCheckFile.cpp b/src/lib/OpenEXRUtil/ImfCheckFile.cpp index 82f318a92f..ca28cd6d0d 100644 --- a/src/lib/OpenEXRUtil/ImfCheckFile.cpp +++ b/src/lib/OpenEXRUtil/ImfCheckFile.cpp @@ -1200,13 +1200,92 @@ runChecks (T& source, bool reduceMemory, bool reduceTime) return threw; } +// This is not entirely needed in that the chunk info has the +// total unpacked_size field which can be used for allocation +// but this adds an additional point to use when debugging issues. +static exr_result_t +realloc_deepdata(exr_decode_pipeline_t* decode) +{ + int32_t w = decode->chunk.width; + int32_t h = decode->chunk.height; + uint64_t totsamps = 0, bytes = 0; + const int32_t *sampbuffer = decode->sample_count_table; + std::vector* ud = static_cast*>( + decode->decoding_user_data); + + if ( ! ud ) + { + for (int c = 0; c < decode->channel_count; c++) + { + exr_coding_channel_info_t& outc = decode->channels[c]; + outc.decode_to_ptr = NULL; + outc.user_pixel_stride = outc.user_bytes_per_element; + outc.user_line_stride = 0; + } + return EXR_ERR_SUCCESS; + } + + if ((decode->decode_flags & + EXR_DECODE_SAMPLE_COUNTS_AS_INDIVIDUAL)) + { + for (int32_t y = 0; y < h; ++y) + { + for (int x = 0; x < w; ++x) + totsamps += sampbuffer[x]; + sampbuffer += w; + } + } + else + { + for (int32_t y = 0; y < h; ++y) + totsamps += sampbuffer[y*w + w - 1]; + } + + for (int c = 0; c < decode->channel_count; c++) + { + exr_coding_channel_info_t& outc = decode->channels[c]; + bytes += totsamps * outc.user_bytes_per_element; + } + + if (bytes >= gMaxBytesPerDeepScanline) + { + for (int c = 0; c < decode->channel_count; c++) + { + exr_coding_channel_info_t& outc = decode->channels[c]; + outc.decode_to_ptr = NULL; + outc.user_pixel_stride = outc.user_bytes_per_element; + outc.user_line_stride = 0; + } + return EXR_ERR_SUCCESS; + } + + if (ud->size () < bytes) + { + ud->resize (bytes); + if (ud->capacity() < bytes) + return EXR_ERR_OUT_OF_MEMORY; + } + + uint8_t* dptr = &((*ud)[0]); + for (int c = 0; c < decode->channel_count; c++) + { + exr_coding_channel_info_t& outc = decode->channels[c]; + outc.decode_to_ptr = dptr; + outc.user_pixel_stride = outc.user_bytes_per_element; + outc.user_line_stride = 0; + + dptr += totsamps * (uint64_t) outc.user_bytes_per_element; + } + return EXR_ERR_SUCCESS; +} + //////////////////////////////////////// bool readCoreScanlinePart ( exr_context_t f, int part, bool reduceMemory, bool reduceTime) { - exr_result_t rv; + exr_result_t rv, frv; exr_attr_box2i_t datawin; rv = exr_get_data_window (f, part, &datawin); if (rv != EXR_ERR_SUCCESS) return true; @@ -1224,6 +1303,8 @@ readCoreScanlinePart ( rv = exr_get_scanlines_per_chunk (f, part, &lines_per_chunk); if (rv != EXR_ERR_SUCCESS) return true; + frv = rv; + for (uint64_t chunk = 0; chunk < height; chunk += lines_per_chunk) { exr_chunk_info_t cinfo = {0}; @@ -1232,6 +1313,7 @@ readCoreScanlinePart ( rv = exr_read_scanline_chunk_info (f, part, y, &cinfo); if (rv != EXR_ERR_SUCCESS) { + frv = rv; if (reduceTime) break; continue; } @@ -1253,19 +1335,32 @@ readCoreScanlinePart ( (uint64_t) lines_per_chunk; } - // TODO: check we are supposed to multiple by lines per chunk above doread = true; - if (reduceMemory && bytes >= gMaxBytesPerScanline) doread = false; + if (reduceMemory && bytes >= gMaxBytesPerScanline) + doread = false; - if (doread) imgdata.resize (bytes); + if (cinfo.type == EXR_STORAGE_DEEP_SCANLINE) + { + decoder.decoding_user_data = &imgdata; + decoder.realloc_nonimage_data_fn = &realloc_deepdata; + } + else + { + if (doread) imgdata.resize (bytes); + } rv = exr_decoding_choose_default_routines (f, part, &decoder); - if (rv != EXR_ERR_SUCCESS) break; + if (rv != EXR_ERR_SUCCESS) + { + frv = rv; + break; + } } else { rv = exr_decoding_update (f, part, &cinfo, &decoder); if (rv != EXR_ERR_SUCCESS) { + frv = rv; if (reduceTime) break; continue; } @@ -1273,20 +1368,25 @@ readCoreScanlinePart ( if (doread) { - uint8_t* dptr = &(imgdata[0]); - for (int c = 0; c < decoder.channel_count; c++) + if (cinfo.type != EXR_STORAGE_DEEP_SCANLINE) { - exr_coding_channel_info_t& outc = decoder.channels[c]; - outc.decode_to_ptr = dptr; - outc.user_pixel_stride = outc.user_bytes_per_element; - outc.user_line_stride = outc.user_pixel_stride * width; - dptr += width * (uint64_t) outc.user_bytes_per_element * + uint8_t* dptr = &(imgdata[0]); + for (int c = 0; c < decoder.channel_count; c++) + { + exr_coding_channel_info_t& outc = decoder.channels[c]; + outc.decode_to_ptr = dptr; + outc.user_pixel_stride = outc.user_bytes_per_element; + outc.user_line_stride = outc.user_pixel_stride * width; + + dptr += width * (uint64_t) outc.user_bytes_per_element * (uint64_t) lines_per_chunk; + } } rv = exr_decoding_run (f, part, &decoder); if (rv != EXR_ERR_SUCCESS) { + frv = rv; if (reduceTime) break; } } @@ -1294,7 +1394,7 @@ readCoreScanlinePart ( exr_decoding_destroy (f, &decoder); - return (rv != EXR_ERR_SUCCESS); + return (frv != EXR_ERR_SUCCESS); } //////////////////////////////////////// @@ -1303,7 +1403,7 @@ bool readCoreTiledPart ( exr_context_t f, int part, bool reduceMemory, bool reduceTime) { - exr_result_t rv; + exr_result_t rv, frv; exr_attr_box2i_t datawin; rv = exr_get_data_window (f, part, &datawin); @@ -1321,6 +1421,7 @@ readCoreTiledPart ( rv = exr_get_tile_levels (f, part, &levelsx, &levelsy); if (rv != EXR_ERR_SUCCESS) return true; + frv = rv; bool keepgoing = true; for (int32_t ylevel = 0; keepgoing && ylevel < levelsy; ++ylevel) { @@ -1330,6 +1431,7 @@ readCoreTiledPart ( rv = exr_get_level_sizes (f, part, xlevel, ylevel, &levw, &levh); if (rv != EXR_ERR_SUCCESS) { + frv = rv; if (reduceTime) { keepgoing = false; @@ -1342,6 +1444,7 @@ readCoreTiledPart ( rv = exr_get_tile_sizes (f, part, xlevel, ylevel, &curtw, &curth); if (rv != EXR_ERR_SUCCESS) { + frv = rv; if (reduceTime) { keepgoing = false; @@ -1371,6 +1474,7 @@ readCoreTiledPart ( f, part, tx, ty, xlevel, ylevel, &cinfo); if (rv != EXR_ERR_SUCCESS) { + frv = rv; if (reduceTime) { keepgoing = false; @@ -1385,6 +1489,7 @@ readCoreTiledPart ( exr_decoding_initialize (f, part, &cinfo, &decoder); if (rv != EXR_ERR_SUCCESS) { + frv = rv; keepgoing = false; break; } @@ -1409,11 +1514,20 @@ readCoreTiledPart ( if (reduceMemory && bytes >= gMaxTileBytes) doread = false; - if (doread) tiledata.resize (bytes); + if (cinfo.type == EXR_STORAGE_DEEP_TILED) + { + decoder.decoding_user_data = &tiledata; + decoder.realloc_nonimage_data_fn = &realloc_deepdata; + } + else + { + if (doread) tiledata.resize (bytes); + } rv = exr_decoding_choose_default_routines ( f, part, &decoder); if (rv != EXR_ERR_SUCCESS) { + frv = rv; keepgoing = false; break; } @@ -1423,6 +1537,7 @@ readCoreTiledPart ( rv = exr_decoding_update (f, part, &cinfo, &decoder); if (rv != EXR_ERR_SUCCESS) { + frv = rv; if (reduceTime) { keepgoing = false; @@ -1434,24 +1549,28 @@ readCoreTiledPart ( if (doread) { - uint8_t* dptr = &(tiledata[0]); - for (int c = 0; c < decoder.channel_count; c++) + if (cinfo.type != EXR_STORAGE_DEEP_TILED) { - exr_coding_channel_info_t& outc = - decoder.channels[c]; - outc.decode_to_ptr = dptr; - outc.user_pixel_stride = - outc.user_bytes_per_element; - outc.user_line_stride = - outc.user_pixel_stride * curtw; - dptr += (uint64_t) curtw * + uint8_t* dptr = &(tiledata[0]); + for (int c = 0; c < decoder.channel_count; c++) + { + exr_coding_channel_info_t& outc = + decoder.channels[c]; + outc.decode_to_ptr = dptr; + outc.user_pixel_stride = + outc.user_bytes_per_element; + outc.user_line_stride = + outc.user_pixel_stride * curtw; + dptr += (uint64_t) curtw * (uint64_t) outc.user_bytes_per_element * (uint64_t) curth; + } } rv = exr_decoding_run (f, part, &decoder); if (rv != EXR_ERR_SUCCESS) { + frv = rv; if (reduceTime) { keepgoing = false; @@ -1486,17 +1605,14 @@ checkCoreFile (exr_context_t f, bool reduceMemory, bool reduceTime) rv = exr_get_storage (f, p, &store); if (rv != EXR_ERR_SUCCESS) return true; - // TODO: Need to fill this in - if (store == EXR_STORAGE_DEEP_SCANLINE || - store == EXR_STORAGE_DEEP_TILED) - continue; - - if (store == EXR_STORAGE_SCANLINE) + if (store == EXR_STORAGE_SCANLINE || + store == EXR_STORAGE_DEEP_SCANLINE) { if (readCoreScanlinePart (f, p, reduceMemory, reduceTime)) return true; } - else if (store == EXR_STORAGE_TILED) + else if (store == EXR_STORAGE_TILED || + store == EXR_STORAGE_DEEP_TILED) { if (readCoreTiledPart (f, p, reduceMemory, reduceTime)) return true; } @@ -1535,6 +1651,20 @@ runCoreChecks (const char* filename, bool reduceMemory, bool reduceTime) cinit.error_handler_fn = &core_error_handler_cb; + if (reduceMemory || reduceTime) + { + /* could use set_default functions for this, but those just + * initialize the context, doing it in the initializer is mt + * safe... + * exr_set_default_maximum_image_size (2048, 2048); + * exr_set_default_maximum_tile_size (512, 512); + */ + cinit.max_image_width = 2048; + cinit.max_image_height = 2048; + cinit.max_tile_width = 512; + cinit.max_tile_height = 512; + } + rv = exr_start_read (&f, filename, &cinit); if (rv != EXR_ERR_SUCCESS) return true; @@ -1604,6 +1734,19 @@ runCoreChecks ( cinit.read_fn = &memstream_read; cinit.size_fn = &memstream_size; cinit.error_handler_fn = &core_error_handler_cb; + if (reduceMemory || reduceTime) + { + /* could use set_default functions for this, but those just + * initialize the context, doing it in the initializer is mt + * safe... + * exr_set_default_maximum_image_size (2048, 2048); + * exr_set_default_maximum_tile_size (512, 512); + */ + cinit.max_image_width = 2048; + cinit.max_image_height = 2048; + cinit.max_tile_width = 512; + cinit.max_tile_height = 512; + } rv = exr_start_read (&f, "", &cinit); if (rv != EXR_ERR_SUCCESS) return true; diff --git a/src/test/CMakeLists.txt b/src/test/CMakeLists.txt index 44d9185d8b..25f429daa2 100644 --- a/src/test/CMakeLists.txt +++ b/src/test/CMakeLists.txt @@ -4,10 +4,15 @@ # We require this to get object library link library support and # combined python 2 + 3 support -add_subdirectory(IexTest) -add_subdirectory(OpenEXRCoreTest) -add_subdirectory(OpenEXRTest) -add_subdirectory(OpenEXRUtilTest) -add_subdirectory(OpenEXRFuzzTest) -add_subdirectory(bin) +if (OPENEXR_TEST_LIBRARIES) + add_subdirectory(IexTest) + add_subdirectory(OpenEXRCoreTest) + add_subdirectory(OpenEXRTest) + add_subdirectory(OpenEXRUtilTest) + add_subdirectory(OpenEXRFuzzTest) +endif() + +if (OPENEXR_BUILD_TOOLS AND OPENEXR_TEST_TOOLS) + add_subdirectory(bin) +endif() diff --git a/src/wrappers/python/CMakeLists.txt b/src/wrappers/python/CMakeLists.txt index a136ad5531..471619b683 100644 --- a/src/wrappers/python/CMakeLists.txt +++ b/src/wrappers/python/CMakeLists.txt @@ -1,23 +1,42 @@ # SPDX-License-Identifier: BSD-3-Clause # Copyright Contributors to the OpenEXR Project. +message(STATUS "Building OpenEXR python bindings") + if(NOT "${CMAKE_PROJECT_NAME}" STREQUAL "OpenEXR") cmake_minimum_required(VERSION 3.12) project(PyOpenEXR) find_package(OpenEXR) endif() -add_library (PyOpenEXR SHARED OpenEXR.cpp) +find_package(Python COMPONENTS Interpreter Development.Module REQUIRED) + +python_add_library (PyOpenEXR MODULE OpenEXR.cpp) -set (Python_ADDITIONAL_VERSIONS 3) -find_package (PythonLibs REQUIRED) -find_package (PythonInterp REQUIRED) +target_link_libraries (PyOpenEXR PRIVATE "${Python_LIBRARIES}" OpenEXR::OpenEXR) -include_directories ("${PYTHON_INCLUDE_DIRS}") +# The python module should be called "OpenEXR.so", not "PyOpenEXR.so", +# but "OpenEXR" is taken as a library name by the main lib, so specify +# the name explicitly here. -set_target_properties (PyOpenEXR PROPERTIES PREFIX "") set_target_properties (PyOpenEXR PROPERTIES OUTPUT_NAME "OpenEXR") -set_target_properties (PyOpenEXR PROPERTIES SUFFIX ".so") -target_link_libraries (PyOpenEXR "${PYTHON_LIBRARIES}" OpenEXR::OpenEXR) +configure_file(Imath.py ${CMAKE_CURRENT_BINARY_DIR}/Imath.py COPYONLY) + +set(PYTHON_INSTALL_DIR "python/OpenEXR") +if(SKBUILD) + set(PYTHON_INSTALL_DIR ${SKBUILD_PLATLIB_DIR}) +endif() + +install(TARGETS PyOpenEXR DESTINATION ${PYTHON_INSTALL_DIR}) +install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/Imath.py DESTINATION ${PYTHON_INSTALL_DIR}) +if(BUILD_TESTING AND OPENEXR_TEST_PYTHON) + + add_test(OpenEXR.PyOpenEXR pytest ${CMAKE_CURRENT_SOURCE_DIR}/tests) + + set_tests_properties(OpenEXR.PyOpenEXR PROPERTIES + ENVIRONMENT "PYTHONPATH=${CMAKE_CURRENT_BINARY_DIR}" + ) + +endif() diff --git a/src/wrappers/python/README.md b/src/wrappers/python/README.md new file mode 100644 index 0000000000..d7c15c989a --- /dev/null +++ b/src/wrappers/python/README.md @@ -0,0 +1,142 @@ + + + +[![License](https://img.shields.io/github/license/AcademySoftwareFoundation/openexr)](https://github.com/AcademySoftwareFoundation/openexr/blob/main/LICENSE.md) +[![CII Best Practices](https://bestpractices.coreinfrastructure.org/projects/2799/badge)](https://bestpractices.coreinfrastructure.org/projects/2799) +[![OpenSSF Scorecard](https://api.securityscorecards.dev/projects/github.com/AcademySoftwareFoundation/openexr/badge)](https://securityscorecards.dev/viewer/?uri=github.com/AcademySoftwareFoundation/openexr) +[![Build Status](https://github.com/AcademySoftwareFoundation/openexr/workflows/CI/badge.svg)](https://github.com/AcademySoftwareFoundation/openexr/actions?query=workflow%3ACI) +[![Analysis Status](https://github.com/AcademySoftwareFoundation/openexr/workflows/Analysis/badge.svg)](https://github.com/AcademySoftwareFoundation/openexr/actions?query=workflow%3AAnalysis) +[![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=AcademySoftwareFoundation_openexr&metric=alert_status)](https://sonarcloud.io/dashboard?id=AcademySoftwareFoundation_openexr) + +# OpenEXR + +OpenEXR provides the specification and reference implementation of the +EXR file format, the professional-grade image storage format of the +motion picture industry. + +The purpose of EXR format is to accurately and efficiently represent +high-dynamic-range scene-linear image data and associated metadata, +with strong support for multi-part, multi-channel use cases. + +OpenEXR is widely used in host application software where accuracy is +critical, such as photorealistic rendering, texture access, image +compositing, deep compositing, and DI. + +## OpenEXR Project Mission + +The goal of the OpenEXR project is to keep the EXR format reliable and +modern and to maintain its place as the preferred image format for +entertainment content creation. + +Major revisions are infrequent, and new features will be carefully +weighed against increased complexity. The principal priorities of the +project are: + +* Robustness, reliability, security +* Backwards compatibility, data longevity +* Performance - read/write/compression/decompression time +* Simplicity, ease of use, maintainability +* Wide adoption, multi-platform support - Linux, Windows, macOS, and others + +OpenEXR is intended solely for 2D data. It is not appropriate for +storage of volumetric data, cached or lit 3D scenes, or more complex +3D data such as light fields. + +The goals of the Imath project are simplicity, ease of use, +correctness and verifiability, and breadth of adoption. Imath is not +intended to be a comprehensive linear algebra or numerical analysis +package. + +## Python Module + +The OpenEXR python module provides rudimentary support for reading and +writing basic scanline image data. Many features of the file format +are not yet supported, including: + +- Writing of tiled images +- Multiresoltion images +- Deep image data +- Some attribute types +- Nonunity channel sampling frequencies +- No support for interleaved channel data + +## Project Governance + +OpenEXR is a project of the [Academy Software +Foundation](https://www.aswf.io). See the project's [governance +policies](https://github.com/AcademySoftwareFoundation/openexr/blob/main/GOVERNANCE.md), [contribution guidelines](https://github.com/AcademySoftwareFoundation/openexr/blob/main/CONTRIBUTING.md), and [code of conduct](https://github.com/AcademySoftwareFoundation/openexr/blob/main/CODE_OF_CONDUCT.md) +for more information. + +# Quick Start + +The "hello, world" image writer: + + import OpenEXR + + width = 10 + height = 10 + size = width * height + + h = OpenEXR.Header(width,height) + h['channels'] = {'R' : Imath.Channel(FLOAT), + 'G' : Imath.Channel(FLOAT), + 'B' : Imath.Channel(FLOAT), + 'A' : Imath.Channel(FLOAT)} + o = OpenEXR.OutputFile("hello.exr", h) + r = array('f', [n for n in range(size*0,size*1)]).tobytes() + g = array('f', [n for n in range(size*1,size*2)]).tobytes() + b = array('f', [n for n in range(size*2,size*3)]).tobytes() + a = array('f', [n for n in range(size*3,size*4)]).tobytes() + channels = {'R' : r, 'G' : g, 'B' : b, 'A' : a} + o.writePixels(channels) + o.close() + +# Community + +* **Ask a question:** + + - Email: openexr-dev@lists.aswf.io + + - Slack: [academysoftwarefdn#openexr](https://academysoftwarefdn.slack.com/archives/CMLRW4N73) + +* **Attend a meeting:** + + - Technical Steering Committee meetings are open to the + public, fortnightly on Thursdays, 1:30pm Pacific Time. + + - Calendar: https://lists.aswf.io/g/openexr-dev/calendar + + - Meeting notes: https://wiki.aswf.io/display/OEXR/TSC+Meetings + +* **Report a bug:** + + - Submit an Issue: https://github.com/AcademySoftwareFoundation/openexr/issues + +* **Report a security vulnerability:** + + - Email to security@openexr.com + +* **Contribute a Fix, Feature, or Improvement:** + + - Read the [Contribution Guidelines](https://github.com/AcademySoftwareFoundation/openexr/blob/main/CONTRIBUTING.md) and [Code of Conduct](https://github.com/AcademySoftwareFoundation/openexr/blob/main/CODE_OF_CONDUCT.md) + + - Sign the [Contributor License + Agreement](https://contributor.easycla.lfx.linuxfoundation.org/#/cla/project/2e8710cb-e379-4116-a9ba-964f83618cc5/user/564e571e-12d7-4857-abd4-898939accdd7) + + - Submit a Pull Request: https://github.com/AcademySoftwareFoundation/openexr/pulls + +# Resources + +- Website: http://www.openexr.com +- Technical documentation: https://openexr.readthedocs.io +- Porting help: [OpenEXR/Imath Version 2.x to 3.x Porting Guide](https://openexr.readthedocs.io/en/latest/PortingGuide.html) +- Reference images: https://github.com/AcademySoftwareFoundation/openexr-images +- Security policy: [SECURITY.md](https://github.com/AcademySoftwareFoundation/openexr/blob/main/SECURITY.md) +- Release notes: [CHANGES.md](https://github.com/AcademySoftwareFoundation/openexr/blob/main/CHANGES.md) +- Contributors: [CONTRIBUTORS.md](https://github.com/AcademySoftwareFoundation/openexr/blob/main/CONTRIBUTORS.md) + +# License + +OpenEXR is licensed under the [BSD-3-Clause license](https://github.com/AcademySoftwareFoundation/openexr/blob/main/LICENSE.md). + + diff --git a/src/wrappers/python/libdeflate.patch b/src/wrappers/python/libdeflate.patch deleted file mode 100644 index a1e2e5b0cc..0000000000 --- a/src/wrappers/python/libdeflate.patch +++ /dev/null @@ -1,12 +0,0 @@ ---- CMakeLists.txt 2023-07-14 08:51:51.375958419 +0200 -+++ CMakeLists.txt 2023-07-14 08:47:47.854104430 +0200 -@@ -183,7 +183,8 @@ - endif() - set_target_properties(libdeflate_static PROPERTIES - OUTPUT_NAME ${STATIC_LIB_NAME} -- PUBLIC_HEADER libdeflate.h) -+ PUBLIC_HEADER libdeflate.h -+ POSITION_INDEPENDENT_CODE ON) - target_include_directories(libdeflate_static PUBLIC ${LIB_INCLUDE_DIRS}) - target_compile_definitions(libdeflate_static PRIVATE ${LIB_COMPILE_DEFINITIONS}) - target_compile_options(libdeflate_static PRIVATE ${LIB_COMPILE_OPTIONS}) diff --git a/src/wrappers/python/openexr_skbuild_plugin.py b/src/wrappers/python/openexr_skbuild_plugin.py new file mode 100644 index 0000000000..ea09221b46 --- /dev/null +++ b/src/wrappers/python/openexr_skbuild_plugin.py @@ -0,0 +1,106 @@ +# Copyright Contributors to the MaterialX Project +# SPDX-License-Identifier: Apache-2.0 +# copied from: https://github.com/AcademySoftwareFoundation/MaterialX/blob/main/python/mtx_skbuild_plugin.py +# Modifications Copyright (c) Contributors to the OpenEXR Project. + +""" +This is a custom scikit-build-core plugin that will +fetch the OpenEXR version from the CMake project. +""" +import os +import tempfile +import subprocess +from pathlib import Path +from typing import FrozenSet, Dict, Optional, Union, List + +from scikit_build_core.file_api.query import stateless_query +from scikit_build_core.file_api.reply import load_reply_dir + + +def dynamic_metadata( + fields: FrozenSet[str], + settings: Optional[Dict[str, object]] = None, +) -> Dict[str, Union[str, Dict[str, Optional[str]]]]: + print("openexr_skbuild_plugin: Computing OpenEXR version from CMake...") + + if fields != "version": + msg = f"Only the 'version' field is supported: fields={fields}" + raise ValueError(msg) + + if settings: + msg = "No inline configuration is supported" + raise ValueError(msg) + + if "OPENEXR_RELEASE_CANDIDATE_TAG" in os.environ: + + # e.g. "v3.1.2-rc4" + # + # If OPENEXR_RELEASE_CANDIDATE_TAG is set, + # the build is for a publish to test.pypi.org. Multiple test + # publishes may happen in the course of preparing for a + # release, but published packages require unique + # names/versions, so use the release candidate tag as the + # version (minus the leading 'v'), + + rct = os.environ["OPENEXR_RELEASE_CANDIDATE_TAG"] + version = rct[1:] + + else: + + current_dir = os.path.dirname(__file__) + + with tempfile.TemporaryDirectory() as tmpdir: + # We will use CMake's file API to get the version + # instead of parsing the CMakeLists files. + + # First generate the query folder so that CMake can generate replies. + reply_dir = stateless_query(Path(tmpdir)) + + # Run cmake (configure). CMake will generate a reply automatically. + try: + subprocess.run( + [ + "cmake", + "-S", + current_dir + "../../../..", + "-B", + tmpdir, + "-DOPENEXR_BUILD_LIBS=OFF", + ], + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + check=True, + text=True, + ) + except subprocess.CalledProcessError as exc: + print(exc.stdout) + raise RuntimeError( + "Failed to configure project to get the version" + ) from exc + + # Get the generated replies. + index = load_reply_dir(reply_dir) + + # Get the version from the CMAKE_PROJECT_VERSION variable. + entries = [ + entry + for entry in index.reply.cache_v2.entries + if entry.name == "CMAKE_PROJECT_VERSION" + ] + + if not entries: + raise ValueError("Could not find OpenEXR version from CMake project") + + if len(entries) > 1: + raise ValueError("More than one entry for CMAKE_PROJECT_VERSION found...") + + version = entries[0].value + + print("openexr_skbuild_plugin: Computed version: {0}".format(version)) + + return version + +def get_requires_for_dynamic_metadata( + _settings: Optional[Dict[str, object]] = None, +) -> List[str]: + return ["cmake"] diff --git a/src/wrappers/python/setup.py b/src/wrappers/python/setup.py deleted file mode 100644 index 5cf4cda608..0000000000 --- a/src/wrappers/python/setup.py +++ /dev/null @@ -1,83 +0,0 @@ -from setuptools import setup, Extension -import os -import platform -import re - - -DESC = """Python bindings for the OpenEXR image file format. - -This is a script to autobuild the wheels using github actions. Please, do not -use it manually - -If you detect any problem, please feel free to report the issue on the GitHub -page: - -https://github.com/AcademySoftwareFoundation/openexr/issues -""" - -# Get the version and library suffix for both OpenEXR and Imath from -# the .pc pkg-config file. - -def pkg_config(var, pkg): - with open(f'./openexr.install/lib/pkgconfig/{pkg}.pc', 'r') as f: - return re.search(f'{var}([^ \n]+)', f.read()).group(1) - -imath_libsuffix = pkg_config("libsuffix=", "Imath") -openexr_libsuffix = pkg_config("libsuffix=", "OpenEXR") -openexr_version = pkg_config("Version: ", "OpenEXR") -openexr_version_major, openexr_version_minor, openexr_version_patch = openexr_version.split('.') - -libs=[] -libs_static=[f'OpenEXR{openexr_libsuffix}', - f'IlmThread{openexr_libsuffix}', - f'Iex{openexr_libsuffix}', - f'Imath{imath_libsuffix}', - f'OpenEXRCore{openexr_libsuffix}', - ] -definitions = [('PYOPENEXR_VERSION_MAJOR', f'{openexr_version_major}'), - ('PYOPENEXR_VERSION_MINOR', f'{openexr_version_minor}'), - ('PYOPENEXR_VERSION_PATCH', f'{openexr_version_patch}'),] -if platform.system() == "Windows": - definitions = [('PYOPENEXR_VERSION', f'\\"{openexr_version}\\"')] -extra_compile_args = [] -if platform.system() == 'Darwin': - extra_compile_args += ['-std=c++11', - '-Wc++11-extensions', - '-Wc++11-long-long'] - -libs_dir = "./openexr.install/lib/" -if not os.path.isdir(libs_dir): - libs_dir = "./openexr.install/lib64/" -if platform.system() == "Windows": - extra_link_args = [libs_dir + lib + ".lib" - for lib in libs_static] - extra_link_args = extra_link_args + [ - "ws2_32.lib", "dbghelp.lib", "psapi.lib", "kernel32.lib", "user32.lib", - "gdi32.lib", "winspool.lib", "shell32.lib", "ole32.lib", - "oleaut32.lib", "uuid.lib", "comdlg32.lib", "advapi32.lib"] -else: - extra_link_args = [libs_dir + "lib" + lib + ".a" - for lib in libs_static] - - -setup(name='OpenEXR', - author = 'Contributors to the OpenEXR Project', - author_email = 'info@openexr.com', - url = 'https://github.com/AcademySoftwareFoundation/openexr', - description = "Python bindings for the OpenEXR image file format", - long_description = DESC, - version=openexr_version, - ext_modules=[ - Extension('OpenEXR', - ['OpenEXR.cpp'], - language='c++', - define_macros=definitions, - include_dirs=['./openexr.install/include/OpenEXR', - './openexr.install/include/Imath',], - libraries=libs, - extra_compile_args=extra_compile_args, - extra_link_args=extra_link_args, - ) - ], - py_modules=['Imath'], -) diff --git a/src/wrappers/python/tests/test_minimal.py b/src/wrappers/python/tests/test_minimal.py index 564bab0b74..43560a8560 100644 --- a/src/wrappers/python/tests/test_minimal.py +++ b/src/wrappers/python/tests/test_minimal.py @@ -1,5 +1,9 @@ -import pytest +#!/usr/bin/env python3 + +# SPDX-License-Identifier: BSD-3-Clause +# Copyright (c) Contributors to the OpenEXR Project. +import pytest def test_import(): import OpenEXR diff --git a/src/wrappers/python/tests/test_unittest.py b/src/wrappers/python/tests/test_unittest.py index 5061ac2b91..e08a44b0b9 100644 --- a/src/wrappers/python/tests/test_unittest.py +++ b/src/wrappers/python/tests/test_unittest.py @@ -11,10 +11,8 @@ import random from array import array -import Imath import OpenEXR - -test_dir = os.path.dirname(os.path.abspath(__file__)) +import Imath FLOAT = Imath.PixelType(Imath.PixelType.FLOAT) UINT = Imath.PixelType(Imath.PixelType.UINT) diff --git a/website/install.rst b/website/install.rst index aa64ee0507..81273d9ef5 100644 --- a/website/install.rst +++ b/website/install.rst @@ -277,6 +277,29 @@ You can customize these options three ways: 2. Use the UI ``cmake-gui`` or ``ccmake``. 3. Specify them as command-line arguments when you invoke cmake. +Uninstall +~~~~~~~~~ + +If you did a binary instal of OpenEXR via a package manager +(`apt-get`, `yum`, `port`, `brew`, etc), use the package manager to +uninstall. + +If you have installed from source, *and you still have the build +tree from which you installed*, you can uninstall via: + +.. code-block:: + + % cmake --build $builddir --target uninstall + +or if using ``make``: + +.. code-block:: + + % make uninstall + +The `uninstall` relies on CMake's `install_manifest.txt` for the record +of what was installed. + Library Naming Options ~~~~~~~~~~~~~~~~~~~~~~