diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0ba4aca3ff..529ee42937 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -169,7 +169,7 @@ jobs: openexr_ver: v3.1.0 pybind11_ver: v2.7.0 python_ver: 3.7 - setenvs: export CMAKE_VERSION=3.15.5 + setenvs: export CMAKE_VERSION=3.18.2 PTEX_VERSION=v2.3.2 WEBP_VERSION=v1.1.0 depcmds: sudo rm -rf /usr/local/include/OpenEXR @@ -189,7 +189,7 @@ jobs: python_ver: 3.7 simd: 0 setenvs: export EMBEDPLUGINS=0 - CMAKE_VERSION=3.15.5 + CMAKE_VERSION=3.18.2 PTEX_VERSION=v2.3.2 WEBP_VERSION=v1.1.0 USE_JPEGTURBO=0 diff --git a/.github/workflows/wheel.yml b/.github/workflows/wheel.yml new file mode 100644 index 0000000000..b1a3a75584 --- /dev/null +++ b/.github/workflows/wheel.yml @@ -0,0 +1,423 @@ +# SPDX-License-Identifier: Apache-2.0 +# Copyright Contributors to the OpenImageIO Project. +# + +name: Wheel + +permissions: + contents: read + id-token: write + +on: + push: + # Workflow run on tags for v2.6 and v3 only. + tags: + - v2.6.* + - v3.* + pull_request: + # Workflow run on pull_request only when related files change. + branches-ignore: + - dev-* + - lg-* + - rtd + - sonar-1 + paths: + - .github/workflows/wheel.yml + - pyproject.toml + - tasks.py + - src/python/OpenImageIO/*.py + - src/cmake/pythonutils.cmake + schedule: + # Nightly build + - cron: "0 8 * * *" + workflow_dispatch: + # This allows manual triggering of the workflow from the web + +jobs: + # Linux jobs run in Docker containers (manylinux), so the latest OS version + # is OK. macOS and Windows jobs need to be locked to specific virtual + # environment versions to mitigate issues from OS updates, and will require + # maintenance as OS versions are retired. + + # --------------------------------------------------------------------------- + # Source Distribution + # --------------------------------------------------------------------------- + + sdist: + name: Build SDist + runs-on: ubuntu-latest + # Don't run on OIIO forks + if: | + github.event_name != 'schedule' || + github.repository == 'AcademySoftwareFoundation/OpenImageIO' + + steps: + + - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + + - name: Build SDist + run: pipx run build --sdist + + - name: Check metadata + run: pipx run twine check dist/* + + - uses: actions/upload-artifact@50769540e7f4bd5e21e526ee35c689e35e0d6874 # v4.4.0 + with: + name: cibw-sdist + path: dist/*.tar.gz + + # --------------------------------------------------------------------------- + # Linux Wheels + # --------------------------------------------------------------------------- + + linux: + name: Build wheels on Linux + runs-on: ubuntu-latest + # Don't run on OIIO forks + if: | + github.event_name != 'schedule' || + github.repository == 'AcademySoftwareFoundation/OpenImageIO' + strategy: + matrix: + include: + # ------------------------------------------------------------------- + # CPython 64 bits manylinux_2_28 + # ------------------------------------------------------------------- + - build: CPython 3.8 64 bits manylinux_2_28 + manylinux: manylinux_2_28 + python: cp38-manylinux_x86_64 + arch: x86_64 + - build: CPython 3.9 64 bits manylinux_2_28 + manylinux: manylinux_2_28 + python: cp39-manylinux_x86_64 + arch: x86_64 + - build: CPython 3.10 64 bits manylinux_2_28 + manylinux: manylinux_2_28 + python: cp310-manylinux_x86_64 + arch: x86_64 + - build: CPython 3.11 64 bits manylinux_2_28 + manylinux: manylinux_2_28 + python: cp311-manylinux_x86_64 + arch: x86_64 + - build: CPython 3.12 64 bits manylinux_2_28 + manylinux: manylinux_2_28 + python: cp312-manylinux_x86_64 + arch: x86_64 + - build: CPython 3.13 64 bits manylinux_2_28 + manylinux: manylinux_2_28 + python: cp313-manylinux_x86_64 + arch: x86_64 + # ------------------------------------------------------------------- + # CPython 64 bits manylinux2014 + # ------------------------------------------------------------------- + - build: CPython 3.8 64 bits manylinux2014 + manylinux: manylinux2014 + python: cp38-manylinux_x86_64 + arch: x86_64 + - build: CPython 3.9 64 bits manylinux2014 + manylinux: manylinux2014 + python: cp39-manylinux_x86_64 + arch: x86_64 + - build: CPython 3.10 64 bits manylinux2014 + manylinux: manylinux2014 + python: cp310-manylinux_x86_64 + arch: x86_64 + - build: CPython 3.11 64 bits manylinux2014 + manylinux: manylinux2014 + python: cp311-manylinux_x86_64 + arch: x86_64 + - build: CPython 3.12 64 bits manylinux2014 + manylinux: manylinux2014 + python: cp312-manylinux_x86_64 + arch: x86_64 + - build: CPython 3.13 64 bits manylinux2014 + manylinux: manylinux2014 + python: cp313-manylinux_x86_64 + arch: x86_64 + + steps: + - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + + - uses: actions/setup-python@f677139bbe7f9c59b41e40162b753c062f5d49a3 # v5.2.0 + name: Install Python + with: + python-version: '3.9' + + - name: Build wheels + uses: pypa/cibuildwheel@d4a2945fcc8d13f20a1b99d461b8e844d5fc6e23 # v2.21.1 + env: + CIBW_BUILD: ${{ matrix.python }} + CIBW_ARCHS: ${{ matrix.arch }} + CIBW_MANYLINUX_X86_64_IMAGE: ${{ matrix.manylinux }} + + - uses: actions/upload-artifact@50769540e7f4bd5e21e526ee35c689e35e0d6874 # v4.4.0 + with: + name: cibw-wheels-${{ matrix.python }}-${{ matrix.manylinux }} + path: ./wheelhouse/*.whl + + # --------------------------------------------------------------------------- + # Linux ARM Wheels + # --------------------------------------------------------------------------- + + linux-arm: + name: Build wheels on Linux ARM + runs-on: ubuntu-latest + # Don't run on OIIO forks + if: | + github.event_name != 'schedule' || + github.repository == 'AcademySoftwareFoundation/OpenImageIO' + strategy: + matrix: + include: + # ------------------------------------------------------------------- + # CPython ARM 64 bits manylinux2014 + # ------------------------------------------------------------------- + - build: CPython 3.8 ARM 64 bits manylinux2014 + manylinux: manylinux2014 + python: cp38-manylinux_aarch64 + arch: aarch64 + - build: CPython 3.9 ARM 64 bits manylinux2014 + manylinux: manylinux2014 + python: cp39-manylinux_aarch64 + arch: aarch64 + - build: CPython 3.10 ARM 64 bits manylinux2014 + manylinux: manylinux2014 + python: cp310-manylinux_aarch64 + arch: aarch64 + - build: CPython 3.11 ARM 64 bits manylinux2014 + manylinux: manylinux2014 + python: cp311-manylinux_aarch64 + arch: aarch64 + - build: CPython 3.12 ARM 64 bits manylinux2014 + manylinux: manylinux2014 + python: cp312-manylinux_aarch64 + arch: aarch64 + - build: CPython 3.13 ARM 64 bits manylinux2014 + manylinux: manylinux2014 + python: cp313-manylinux_aarch64 + arch: aarch64 + + steps: + - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + + - uses: actions/setup-python@f677139bbe7f9c59b41e40162b753c062f5d49a3 # v5.2.0 + name: Install Python + with: + python-version: '3.9' + + - name: Set up QEMU + uses: docker/setup-qemu-action@49b3bc8e6bdd4a60e6116a5414239cba5943d3cf # v3.2.0 + with: + platforms: all + + - name: Build wheels + uses: pypa/cibuildwheel@d4a2945fcc8d13f20a1b99d461b8e844d5fc6e23 # v2.21.1 + env: + CIBW_BUILD: ${{ matrix.python }} + CIBW_ARCHS: ${{ matrix.arch }} + CIBW_MANYLINUX_AARCH64_IMAGE: ${{ matrix.manylinux }} + # Apparently, the x86_64 runners aren't able to execute tests + # tests for ARM wheels. + # TODO: Re-enable tests when linux ARM runners are available + CIBW_TEST_SKIP: '*' + + - uses: actions/upload-artifact@50769540e7f4bd5e21e526ee35c689e35e0d6874 # v4.4.0 + with: + name: cibw-wheels-${{ matrix.python }}-${{ matrix.manylinux }} + path: ./wheelhouse/*.whl + + # --------------------------------------------------------------------------- + # macOS Wheels + # --------------------------------------------------------------------------- + + macos: + name: Build wheels on macOS + runs-on: macos-12 + # Don't run on OIIO forks + if: | + github.event_name != 'schedule' || + github.repository == 'AcademySoftwareFoundation/OpenImageIO' + strategy: + matrix: + include: + # ------------------------------------------------------------------- + # CPython 64 bits + # ------------------------------------------------------------------- + - build: CPython 3.8 64 bits + python: cp38-macosx_x86_64 + arch: x86_64 + - build: CPython 3.9 64 bits + python: cp39-macosx_x86_64 + arch: x86_64 + - build: CPython 3.10 64 bits + python: cp310-macosx_x86_64 + arch: x86_64 + - build: CPython 3.11 64 bits + python: cp311-macosx_x86_64 + arch: x86_64 + - build: CPython 3.12 64 bits + python: cp312-macosx_x86_64 + arch: x86_64 + - build: CPython 3.13 64 bits + python: cp313-macosx_x86_64 + arch: x86_64 + + steps: + - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + + - uses: actions/setup-python@f677139bbe7f9c59b41e40162b753c062f5d49a3 # v5.2.0 + name: Install Python + with: + python-version: '3.9' + + - name: Build wheels + uses: pypa/cibuildwheel@d4a2945fcc8d13f20a1b99d461b8e844d5fc6e23 # v2.21.1 + env: + CIBW_BUILD: ${{ matrix.python }} + CIBW_ARCHS: ${{ matrix.arch }} + # TODO: Re-enable HEIF when we provide a build recipe that does + # not include GPL-licensed dynamic libraries. + USE_Libheif: 'OFF' + # Make sure the OIIO build's version of freetype is linked + CIBW_BEFORE_ALL: | + brew uninstall --ignore-dependencies harfbuzz fontconfig cairo freetype + + - uses: actions/upload-artifact@50769540e7f4bd5e21e526ee35c689e35e0d6874 # v4.4.0 + with: + name: cibw-wheels-${{ matrix.python }} + path: ./wheelhouse/*.whl + + # --------------------------------------------------------------------------- + # macOS ARM Wheels + # --------------------------------------------------------------------------- + + macos-arm: + name: Build wheels on macOS ARM + runs-on: macos-14 + # Don't run on OIIO forks + if: | + github.event_name != 'schedule' || + github.repository == 'AcademySoftwareFoundation/OpenImageIO' + strategy: + matrix: + include: + # ------------------------------------------------------------------- + # CPython ARM 64 bits + # ------------------------------------------------------------------- + - build: CPython 3.8 ARM 64 bits + python: cp38-macosx_arm64 + arch: arm64 + - build: CPython 3.9 ARM 64 bits + python: cp39-macosx_arm64 + arch: arm64 + - build: CPython 3.10 ARM 64 bits + python: cp310-macosx_arm64 + arch: arm64 + - build: CPython 3.11 ARM 64 bits + python: cp311-macosx_arm64 + arch: arm64 + - build: CPython 3.12 ARM 64 bits + python: cp312-macosx_arm64 + arch: arm64 + - build: CPython 3.13 ARM 64 bits + python: cp313-macosx_arm64 + arch: arm64 + + steps: + - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + + - uses: actions/setup-python@f677139bbe7f9c59b41e40162b753c062f5d49a3 # v5.2.0 + name: Install Python + # https://cibuildwheel.pypa.io/en/stable/faq/#macos-building-cpython-38-wheels-on-arm64 + with: + python-version: '3.8' + + + - name: Build wheels + uses: pypa/cibuildwheel@d4a2945fcc8d13f20a1b99d461b8e844d5fc6e23 # v2.21.1 + env: + CIBW_BUILD: ${{ matrix.python }} + CIBW_ARCHS: ${{ matrix.arch }} + # Make sure the OIIO build's version of freetype is linked + CIBW_BEFORE_ALL: | + brew uninstall --ignore-dependencies harfbuzz fontconfig cairo freetype + #TODO: Re-enable the PNG plugin until we can figure out why the system PNG is causing problems + ENABLE_PNG: 'OFF' + + - uses: actions/upload-artifact@50769540e7f4bd5e21e526ee35c689e35e0d6874 # v4.4.0 + with: + name: cibw-wheels-${{ matrix.python }} + path: ./wheelhouse/*.whl + + # --------------------------------------------------------------------------- + # Windows Wheels + # --------------------------------------------------------------------------- + + windows: + name: Build wheels on Windows + runs-on: windows-2022 + # Don't run on OIIO forks + if: | + github.event_name != 'schedule' || + github.repository == 'AcademySoftwareFoundation/OpenImageIO' + strategy: + matrix: + include: + # ------------------------------------------------------------------- + # CPython 64 bits + # ------------------------------------------------------------------- + - build: CPython 3.8 64 bits + python: cp38-win_amd64 + arch: AMD64 + - build: CPython 3.9 64 bits + python: cp39-win_amd64 + arch: AMD64 + - build: CPython 3.10 64 bits + python: cp310-win_amd64 + arch: AMD64 + - build: CPython 3.11 64 bits + python: cp311-win_amd64 + arch: AMD64 + - build: CPython 3.12 64 bits + python: cp312-win_amd64 + arch: AMD64 + - build: CPython 3.13 64 bits + python: cp313-win_amd64 + arch: AMD64 + + steps: + - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + + - uses: actions/setup-python@f677139bbe7f9c59b41e40162b753c062f5d49a3 # v5.2.0 + name: Install Python + with: + python-version: '3.9' + + - name: Build wheels + uses: pypa/cibuildwheel@d4a2945fcc8d13f20a1b99d461b8e844d5fc6e23 # v2.21.1 + env: + CIBW_BUILD: ${{ matrix.python }} + CIBW_ARCHS: ${{ matrix.arch }} + + - uses: actions/upload-artifact@50769540e7f4bd5e21e526ee35c689e35e0d6874 # v4.4.0 + with: + name: cibw-wheels-${{ matrix.python }} + path: ./wheelhouse/*.whl + + + upload_pypi: + needs: [sdist, linux, linux-arm, macos, macos-arm, windows] + runs-on: ubuntu-latest + permissions: + id-token: write + if: github.event_name == 'push' && startsWith(github.event.ref, 'refs/tags/v3') + steps: + - uses: actions/setup-python@f677139bbe7f9c59b41e40162b753c062f5d49a3 # v5.2.0 + + - uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8 + with: + pattern: cibw-* + path: dist + merge-multiple: true + + - uses: pypa/gh-action-pypi-publish@897895f1e160c830e369f9779632ebc134688e1b # release/v1 \ No newline at end of file diff --git a/.gitignore b/.gitignore index c8c897e967..a9dca689b3 100644 --- a/.gitignore +++ b/.gitignore @@ -4,13 +4,17 @@ branches/ build/ dist/ ext/ +wheelhouse/ +_coverage/ +.python-version .idea .vscode .cproject .project .DS_Store *.pyc -_coverage/ +/*.lock +gastest.o # Exclude test files I tend to leave around at the top level. The leading # slash ensures that files with these extensions within subdirectories are not @@ -21,3 +25,5 @@ _coverage/ /*.jxl /*.tx /*.log + + diff --git a/CMakeLists.txt b/CMakeLists.txt index b2d2d02279..f16ec522b5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,8 +1,8 @@ # Copyright Contributors to the OpenImageIO project. -# SPDX-License-Identifier: BSD-3-Clause and Apache-2.0 +# SPDX-License-Identifier: Apache-2.0 # https://github.com/AcademySoftwareFoundation/OpenImageIO -cmake_minimum_required (VERSION 3.15) +cmake_minimum_required (VERSION 3.18.2) set (OpenImageIO_VERSION "2.6.7.0") set (OpenImageIO_VERSION_OVERRIDE "" CACHE STRING @@ -248,10 +248,16 @@ if (NOT EMBEDPLUGINS AND NOT BUILD_OIIOUTIL_ONLY) endforeach () endif () -if (USE_PYTHON AND Python3_Development_FOUND AND NOT BUILD_OIIOUTIL_ONLY) + +if (WIN32) + set (_py_dev_found Python3_Development_FOUND) +else () + set (_py_dev_found Python3_Development.Module_FOUND) +endif () +if (USE_PYTHON AND ${_py_dev_found} AND NOT BUILD_OIIOUTIL_ONLY) add_subdirectory (src/python) else () - message (STATUS "Not building Python bindings: USE_PYTHON=${USE_PYTHON}, Python3_Development_FOUND=${Python3_Development_FOUND}") + message (STATUS "Not building Python bindings: USE_PYTHON=${USE_PYTHON}, Python3_Development.Module_FOUND=${Python3_Development.Module_FOUND}") endif () add_subdirectory (src/include) diff --git a/INSTALL.md b/INSTALL.md index 16413ce3ec..fa1168bd4d 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -19,7 +19,7 @@ NEW or CHANGED MINIMUM dependencies since the last major release are **bold**. CMake configuration flag: `-DCMAKE_CXX_STANDARD=20`, etc. * Compilers: **gcc 9.3** - 14.1, **clang 5** - 18, MSVS 2017 - 2019 (**v19.14 and up**), **Intel icc 19+**, Intel OneAPI C++ compiler 2022+. - * **CMake >= 3.15** (tested through 3.30) + * **CMake >= 3.18.2** (tested through 3.30) * **Imath >= 3.1** (tested through 3.1.x and main) * **OpenEXR >= 3.1** (tested through 3.3 and main) * **libTIFF >= 4.0** (tested through 4.7) @@ -357,6 +357,28 @@ produce a dynamic-linked version. 3. Execute the PowerShell command from where vcpkg is located in directory. ``vcpkg install openimageio`` + +Python-based Builds and Installs +-------------------------------- + +**Installing from prebuilt binary distributions** + +If you're only interested in the Python module and the CLI tools, you can install with `pip` or `uv`: + +> ```pip install OpenImageIO``` + +**Building and installing from source** + +If you have a C++ compiler installed, you can also use the Python build-backend to compile and install +from source by navigating to the root of the repository and running: ```pip install .``` + +This will download and install CMake and Ninja if necessary, and invoke the CMake build system; which, +in turn, will build missing dependencies, compile OIIO, and install the Python module, the libraries, +the headers, and the CLI tools to a platform-specific, Python-specific location. + +See the [scikit-build-core docs](https://scikit-build-core.readthedocs.io/en/latest/configuration.html#configuring-cmake-arguments-and-defines) +for more information on customizing and overriding build-tool options and CMake arguments. + Test Images ----------- diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000000..00a14fd19e --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,125 @@ +[project] +name = "OpenImageIO" +# The build backend ascertains the version from the CMakeLists.txt file. +dynamic = ["version"] +description = "Reading, writing, and processing images in a wide variety of file formats, using a format-agnostic API, aimed at VFX applications." +authors = [ + {name = "Larry Gritz", email = "lg@larrygritz.com"}, + {name = "OpenImageIO Contributors", email = "oiio-dev@lists.aswf.io"} +] +maintainers = [ + {name = "OpenImageIO Contributors", email="oiio-dev@lists.aswf.io"}, +] +readme = "README.md" +license = {text = "Apache-2.0"} +classifiers = [ + "Natural Language :: English", + "Operating System :: OS Independent", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: 3.13", + "License :: OSI Approved :: Apache Software License", + "Topic :: Multimedia :: Graphics", + "Topic :: Multimedia :: Video", + "Topic :: Multimedia :: Video :: Display", + "Topic :: Software Development :: Libraries :: Python Modules", +] +requires-python = ">= 3.8" + +[project.urls] +Homepage = "https://openimageio.org/" +Source = "https://github.com/AcademySoftwareFoundation/OpenImageIO" +Documentation = "https://openimageio.readthedocs.io" +Issues = "https://github.com/AcademySoftwareFoundation/OpenImageIO/issues" + + +[project.scripts] +# Use the convention below to expose CLI tools as Python scripts. +iconvert = "OpenImageIO:_command_line" +idiff = "OpenImageIO:_command_line" +igrep = "OpenImageIO:_command_line" +iinfo = "OpenImageIO:_command_line" +maketx = "OpenImageIO:_command_line" +oiiotool = "OpenImageIO:_command_line" +testtex = "OpenImageIO:_command_line" + +[build-system] +build-backend = "scikit_build_core.build" +requires = [ + "scikit-build-core>=0.10.6,<1", + "pybind11", +] + +[tool.scikit-build] +build.verbose = true +# Exclude unnecessary directories from the source distribution. +sdist.exclude = [".github", "testsuite", "ASWF", "docs"] +# Pin minimum scikit-build-core to that specified in build-system.requires. +minimum-version = "build-system.requires" +# Pin minimum CMake version to that specified in CMakeLists.txt. +cmake.version = "CMakeLists.txt" +wheel.license-files = ["LICENSE.md", "THIRD-PARTY.md"] +# Make sure the package is structured as expected. +wheel.install-dir = "OpenImageIO" + +[tool.scikit-build.cmake.define] +# Build missing dependencies. See src/cmake for details. +OpenImageIO_BUILD_MISSING_DEPS = "all" +# Don't build tests. Dramatically improves build time. +OIIO_BUILD_TESTS = "0" +# Prefer linking static dependencies when possible. +LINKSTATIC = "1" + +# Dynamically set the package version metadata by pasrsing CMakeLists.txt. +[tool.scikit-build.metadata.version] +provider = "scikit_build_core.metadata.regex" +input = "CMakeLists.txt" +regex = 'set \(OpenImageIO_VERSION "(?P[0-9a-z.]+)"\)' + + +[tool.cibuildwheel] +build-verbosity = 1 +skip = [ + # Skip 32-bit builds + "*-win32", + "*-manylinux_i686", + # Building with musl seems to work, but the repair-wheel step seems to fail... + # This may be a bug in repairwheel (or auditwheel)? + "*musllinux*", +] +test-command = "oiiotool --buildinfo" + +[tool.cibuildwheel.macos.environment] +# Specify a persistent build directory so the repairwheel command can (re)find +# dependency libraries built by OpenImageIO. +SKBUILD_BUILD_DIR = "/tmp/build_oiio_wheels" +# Always (re)build the TIFF library; see #4439 for more details. +SKBUILD_CMAKE_ARGS = "-DOpenImageIO_BUILD_LOCAL_DEPS=TIFF; -DOpenImageIO_BUILD_MISSING_DEPS=all" +# C++17 - std::filesystem is only available in macOS 10.15 and later. +MACOSX_DEPLOYMENT_TARGET = "10.15" +# Optimize for size (not speed). +SKBUILD_CMAKE_BUILD_TYPE = "MinSizeRel" + +[tool.cibuildwheel.linux.environment] +SKBUILD_BUILD_DIR = "/tmp/build_oiio_wheels" +SKBUILD_CMAKE_ARGS = "-DOpenImageIO_BUILD_LOCAL_DEPS=all; -DOpenImageIO_BUILD_MISSING_DEPS=all" +SKBUILD_CMAKE_BUILD_TYPE = "MinSizeRel" +# Suppress warnings that cause linux cibuildwheel build to fail +CXXFLAGS = "-Wno-error=stringop-overflow -Wno-pragmas" + +[tool.cibuildwheel.windows.environment] +SKBUILD_CMAKE_BUILD_TYPE = "MinSizeRel" + +# On macOS and Linux, run a custom repair-wheel-command after the build. +# See tasks.py for more details. +[tool.cibuildwheel.macos] +before-build = "pip install repairwheel invoke" +repair-wheel-command = "invoke wheel-repair --build-dir ${SKBUILD_BUILD_DIR} --wheel-path {wheel} --output-dir {dest_dir}" + +[tool.cibuildwheel.linux] +before-build = "pip install repairwheel invoke" +repair-wheel-command = "invoke wheel-repair --build-dir ${SKBUILD_BUILD_DIR} --wheel-path {wheel} --output-dir {dest_dir}" diff --git a/src/build-scripts/build_libtiff.bash b/src/build-scripts/build_libtiff.bash index 763fb4bc75..e069175cba 100755 --- a/src/build-scripts/build_libtiff.bash +++ b/src/build-scripts/build_libtiff.bash @@ -45,6 +45,7 @@ if [[ -z $DEP_DOWNLOAD_ONLY ]]; then -Dtiff-tests=${LIBTIFF_BUILD_TESTS:-OFF} \ -Dtiff-docs=${LIBTIFF_BUILD_TESTS:-OFF} \ -Dlibdeflate=ON \ + -Dwebp=OFF \ ${LIBTIFF_BUILDOPTS} time cmake --build build --target install fi diff --git a/src/cmake/build_libdeflate.cmake b/src/cmake/build_libdeflate.cmake index 50543699ce..a7d46b7741 100644 --- a/src/cmake/build_libdeflate.cmake +++ b/src/cmake/build_libdeflate.cmake @@ -9,7 +9,7 @@ set_cache (libdeflate_BUILD_VERSION 1.20 "libdeflate version for local builds") set (libdeflate_GIT_REPOSITORY "https://github.com/ebiggers/libdeflate") set (libdeflate_GIT_TAG "v${libdeflate_BUILD_VERSION}") -set_cache (libdeflate_BUILD_SHARED_LIBS ${LOCAL_BUILD_SHARED_LIBS_DEFAULT} +set_cache (libdeflate_BUILD_SHARED_LIBS OFF # ${LOCAL_BUILD_SHARED_LIBS_DEFAULT} DOC "Should a local libdeflate build, if necessary, build shared libraries" ADVANCED) string (MAKE_C_IDENTIFIER ${libdeflate_BUILD_VERSION} libdeflate_VERSION_IDENT) @@ -20,6 +20,7 @@ build_dependency_with_cmake(libdeflate GIT_TAG ${libdeflate_GIT_TAG} CMAKE_ARGS -D BUILD_SHARED_LIBS=${libdeflate_BUILD_SHARED_LIBS} + -D LIBDEFLATE_BUILD_SHARED_LIB=${libdeflate_BUILD_SHARED_LIBS} -D CMAKE_POSITION_INDEPENDENT_CODE=ON -D CMAKE_INSTALL_LIBDIR=lib -D LIBDEFLATE_BUILD_GZIP=OFF @@ -32,6 +33,7 @@ set (libdeflate_ROOT ${libdeflate_LOCAL_INSTALL_DIR}) # Signal to caller that we need to find again at the installed location set (libdeflate_REFIND TRUE) set (libdeflate_REFIND_ARGS CONFIG) +set (libdeflate_REFIND_VERSION ${libdeflate_BUILD_VERSION}) if (libdeflate_BUILD_SHARED_LIBS) install_local_dependency_libs (libdeflate libdeflate) diff --git a/src/cmake/cuda_macros.cmake b/src/cmake/cuda_macros.cmake index 5e625ff22e..29f6c300ce 100644 --- a/src/cmake/cuda_macros.cmake +++ b/src/cmake/cuda_macros.cmake @@ -8,9 +8,6 @@ set_cache (CUDA_TARGET_ARCH "sm_60" "CUDA GPU architecture (e.g. sm_60)") set_cache (CUDAToolkit_ROOT "" "Path to CUDA toolkit") if (OIIO_USE_CUDA) - if (OIIO_USE_CUDA AND CMAKE_VERSION VERSION_LESS 3.18) - message (WARNING "CMake >= 3.18 is required to correctly find the CUDA dependency") - endif () set (CUDA_PROPAGATE_HOST_FLAGS ON) set (CUDA_VERBOSE_BUILD ${VERBOSE}) checked_find_package(CUDAToolkit diff --git a/src/cmake/fancy_add_executable.cmake b/src/cmake/fancy_add_executable.cmake index d517370ea9..17f2e6e865 100644 --- a/src/cmake/fancy_add_executable.cmake +++ b/src/cmake/fancy_add_executable.cmake @@ -53,6 +53,17 @@ macro (fancy_add_executable) target_link_libraries (${_target_NAME} PRIVATE ${_target_LINK_LIBRARIES}) target_link_libraries (${_target_NAME} PRIVATE ${PROFILER_LIBRARIES}) set_target_properties (${_target_NAME} PROPERTIES FOLDER ${_target_FOLDER}) + if (SKBUILD) + # When building cli tools with scikit-build, set RPATH + # to a relative path to the distribution's lib directory. + if (APPLE) + set_target_properties (${_target_NAME} PROPERTIES + INSTALL_RPATH "@loader_path/../${CMAKE_INSTALL_LIBDIR}") + elseif (LINUX) + set_target_properties (${_target_NAME} PROPERTIES + INSTALL_RPATH "$ORIGIN/../${CMAKE_INSTALL_LIBDIR}") + endif () + endif () check_is_enabled (INSTALL_${_target_NAME} _target_NAME_INSTALL_enabled) if (CMAKE_UNITY_BUILD AND UNITY_BUILD_MODE STREQUAL GROUP) set_source_files_properties(${_target_SRC} PROPERTIES diff --git a/src/cmake/pythonutils.cmake b/src/cmake/pythonutils.cmake index c5f4720400..8acc967938 100644 --- a/src/cmake/pythonutils.cmake +++ b/src/cmake/pythonutils.cmake @@ -32,13 +32,24 @@ macro (find_python) list (APPEND _req EXACT) endif () endif () + + # Support building on manylinux docker images, which do not contain + # the Development.Embedded component. + # https://pybind11.readthedocs.io/en/stable/compiling.html#findpython-mode + if (WIN32) + set (_py_components Interpreter Development) + else () + set (_py_components Interpreter Development.Module) + endif () + checked_find_package (Python3 ${PYTHON_VERSION} ${_req} VERSION_MIN 3.7 - COMPONENTS Interpreter Development + COMPONENTS ${_py_components} PRINT Python3_VERSION Python3_EXECUTABLE Python3_LIBRARIES Python3_Development_FOUND + Python3_Development.Module_FOUND Python3_Interpreter_FOUND ) # The version that was found may not be the default or user @@ -129,6 +140,19 @@ macro (setup_python_module) # SUFFIX ".pyd") # endif() + if (SKBUILD) + set (PYTHON_SITE_DIR .) + # When building python wheels, set RPATH to a relative path to + # the distribution's lib directory. + if (APPLE) + set_target_properties (${target_name} PROPERTIES + INSTALL_RPATH "@loader_path/${CMAKE_INSTALL_LIBDIR}") + elseif (LINUX) + set_target_properties (${target_name} PROPERTIES + INSTALL_RPATH "$ORIGIN/${CMAKE_INSTALL_LIBDIR}") + endif () + endif () + # In the build area, put it in lib/python so it doesn't clash with the # non-python libraries of the same name (which aren't prefixed by "lib" # on Windows). diff --git a/src/iv/CMakeLists.txt b/src/iv/CMakeLists.txt index 698f4d686e..30b2fa6bd7 100644 --- a/src/iv/CMakeLists.txt +++ b/src/iv/CMakeLists.txt @@ -5,6 +5,7 @@ set (OIIO_IV_EXTRA_IV_LIBRARIES "" CACHE STRING "Paths to extra libraries to force iv to link against") set (CMAKE_AUTOMOC ON) +set (CMAKE_AUTOMOC_PATH_PREFIX OFF) if (Qt5_POSITION_INDEPENDENT_CODE OR Qt6_POSITION_INDEPENDENT_CODE) set (CMAKE_POSITION_INDEPENDENT_CODE ON) endif() diff --git a/src/python/__init__.py b/src/python/__init__.py index f3c345ec13..de6c530a5b 100644 --- a/src/python/__init__.py +++ b/src/python/__init__.py @@ -2,16 +2,65 @@ # SPDX-License-Identifier: Apache-2.0 # https://github.com/AcademySoftwareFoundation/OpenImageIO -import os, sys, platform +import os +import sys +import logging +import platform +import subprocess -# This works around the python 3.8 change to stop loading DLLs from PATH on Windows. -# We reproduce the old behaviour by manually tokenizing PATH, checking that the directories exist and are not ".", -# then add them to the DLL load path. -# This behviour can be disabled by setting the environment variable "OIIO_LOAD_DLLS_FROM_PATH" to "0" -if sys.version_info >= (3, 8) and platform.system() == "Windows" and os.getenv("OIIO_LOAD_DLLS_FROM_PATH", "1") == "1": - for path in os.getenv("PATH", "").split(os.pathsep): - if os.path.exists(path) and path != ".": - os.add_dll_directory(path) +logger = logging.getLogger(__name__) -from .OpenImageIO import * +if platform.system() == "Windows": + # Python wheel module is dynamically linked to the OIIO DLL present in the bin folder. + _here = os.path.abspath(os.path.dirname(__file__)) + _bin_dir = os.path.join(_here, "bin") + if os.path.exists(_bin_dir): + if sys.version_info >= (3, 8): + os.add_dll_directory(_bin_dir) + else: + os.environ['PATH'] = '{0};{1}'.format(_bin_dir, os.getenv('PATH', '')) + + #TODO: Remove. Python 3.8 stopped loading DLLs from PATH on Windows for security reasons. + if sys.version_info >= (3, 8): + # This works around the python 3.8 change to stop loading DLLs from PATH on Windows. + # We reproduce the old behavior by manually tokenizing PATH, checking that the + # directories exist and are not ".", then add them to the DLL load path. + # Set the environment variable "OIIO_LOAD_DLLS_FROM_PATH" to "0" to disable this behavior. + if os.getenv("OIIO_LOAD_DLLS_FROM_PATH", "1") == "1": + logger.warning( + "Adding DLL directories to PATH. This behavior will be removed in " + "a future version. Set the environment variable OIIO_LOAD_DLLS_FROM_PATH " + "to 0 to disable this behavior." + ) + for path in os.getenv("PATH", "").split(os.pathsep): + if os.path.exists(path) and path != ".": + os.add_dll_directory(path) +from .OpenImageIO import * # type: ignore # noqa: F401, F403, E402 + +__version__ = VERSION_STRING # type: ignore # noqa: F405 + +__status__ = "production" + +__author__ = "OpenImageIO Contributors" + +__email__ = "oiio-dev@lists.aswf.io" + +__license__ = "SPDX-License-Identifier: Apache-2.0" + +__copyright__ = "Copyright Contributors to the OpenImageIO Project" + +__doc__ = """OpenImageIO is a toolset for reading, writing, and manipulating image files of +any image file format relevant to VFX / animation via a format-agnostic API +with a feature set, scalability, and robustness needed for feature film +production. +""" + + +def _call_program(name, args): + bin_dir = os.path.join(os.path.dirname(__file__), 'bin') + return subprocess.call([os.path.join(bin_dir, name)] + args) + +def _command_line(): + name = os.path.basename(sys.argv[0]) + raise SystemExit(_call_program(name, sys.argv[1:])) diff --git a/tasks.py b/tasks.py new file mode 100644 index 0000000000..f2e83280e8 --- /dev/null +++ b/tasks.py @@ -0,0 +1,35 @@ +# Copyright Contributors to the OpenImageIO project. +# SPDX-License-Identifier: Apache-2.0 +# https://github.com/AcademySoftwareFoundation/OpenImageIO + +from pathlib import Path +import shutil +import invoke +from repairwheel._vendor.auditwheel.wheeltools import InWheelCtx + + +DIRS_TO_REMOVE = ['lib', 'lib64', 'share', 'include'] + +def _wheel_edit(wheel_path, dirs_to_remove=DIRS_TO_REMOVE): + """ + Edit the contents of the repaired wheel to remove the 'lib', 'share', and 'include' directories. + """ + + with InWheelCtx(wheel_path, wheel_path) as ctx: + root = Path(ctx.path) + for dir_name in dirs_to_remove: + this_dir = root / "OpenImageIO" / dir_name + if this_dir.exists(): + shutil.rmtree(this_dir) + + return Path(wheel_path) + + +@invoke.task +def wheel_repair(c, build_dir, wheel_path, output_dir): + """ + Slim down and repair the wheel file at `wheel_path` with libraries from `build_dir` and save the result to `output_dir`. + """ + edited_wheel_path = _wheel_edit(wheel_path) + c.run(f"repairwheel -l {build_dir}/deps/dist/lib -l {build_dir}/lib -o {output_dir} {edited_wheel_path}") + print(f"Repaired + slimmed wheel created at {output_dir}")