diff --git a/.github/workflows/ci_workflow.yml b/.github/workflows/ci_workflow.yml index bc51851d7e..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 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/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/CMakeLists.txt b/CMakeLists.txt index a365910645..cd28f765f7 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() # uninstall target @@ -83,8 +100,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 @@ -93,8 +115,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() @@ -122,7 +143,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() @@ -142,12 +163,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/cmake/OpenEXRSetup.cmake b/cmake/OpenEXRSetup.cmake index 0a7dabcc6f..9f1a5ea5ee 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 "v3.1.9" 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 "v3.1.10" 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/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/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)