diff --git a/.github/workflows/analysis-coverage.yml b/.github/workflows/analysis-coverage.yml index dd8bb4fa..66198673 100644 --- a/.github/workflows/analysis-coverage.yml +++ b/.github/workflows/analysis-coverage.yml @@ -243,13 +243,14 @@ jobs: - name: Prepare system run: | - sudo apt -y purge libheif1 libaom-dev libx265-dev - sudo apt -y install libde265-dev nasm + sudo apt -y purge libheif1 libaom-dev libx265-dev libde265-dev + sudo apt -y install nasm - name: Install from source run: | sudo -H python3 -m pip install pillow==9.3.0 pytest defusedxml packaging numpy coverage - sudo -H PH_LIGHT_ACTION=1 python3 -m pip -v install --no-build-isolation . + sudo -H PH_LIGHT_ACTION=1 python3 libheif/linux_build_libs.py + sudo -H python3 -m pip -v install --no-build-isolation . - name: LibHeif info run: sudo -H python3 -c "import pillow_heif; print(pillow_heif.libheif_info())" @@ -289,13 +290,14 @@ jobs: - name: Prepare system run: | - sudo apt -y purge libheif1 libaom-dev libx265-dev - sudo apt -y install libde265-dev nasm + sudo apt -y purge libheif1 libaom-dev libx265-dev libde265-dev + sudo apt -y install nasm - name: Install from source run: | sudo -H python3 -m pip install pillow==9.4.0 pytest defusedxml packaging numpy - sudo -H PH_LIGHT_ACTION=1 python3 -m pip -v install --no-build-isolation . + sudo -H PH_LIGHT_ACTION=1 python3 libheif/linux_build_libs.py + sudo -H python3 -m pip -v install --no-build-isolation . - name: LibHeif info run: sudo -H python3 -c "import pi_heif; print(pi_heif.libheif_info())" diff --git a/.github/workflows/build-cache-deps.yml b/.github/workflows/build-cache-deps.yml index f2561c8c..e8ec7cd7 100644 --- a/.github/workflows/build-cache-deps.yml +++ b/.github/workflows/build-cache-deps.yml @@ -54,9 +54,9 @@ jobs: env: CIBW_BUILD: ${{ format('cp38-{0}*', matrix.cibw_buildlinux) }} CIBW_ARCHS: ${{ matrix.cibw_arch }} + CIBW_BEFORE_ALL_LINUX: | + python3 {package}/libheif/linux_build_libs.py CIBW_ENVIRONMENT_LINUX: BUILD_DIR=/host${{ env.BUILD_DIR }} PH_FULL_ACTION=1 - CIBW_TEST_COMMAND: "python3 -c 'import pillow_heif; print(pillow_heif.libheif_info())'" - CIBW_TEST_EXTRAS: "" CIBW_BUILD_VERBOSITY: 1 - name: Checking built wheels @@ -125,10 +125,10 @@ jobs: env: CIBW_BUILD: ${{ format('cp38-{0}*', matrix.cibw_buildlinux) }} CIBW_ARCHS: ${{ matrix.cibw_arch }} - CIBW_BEFORE_ALL_LINUX: ${{ env.INSTALL_OS_PACKAGES }} + CIBW_BEFORE_ALL_LINUX: | + ${{ env.INSTALL_OS_PACKAGES }} + python3 {package}/libheif/linux_build_libs.py CIBW_ENVIRONMENT_LINUX: BUILD_DIR=/host${{ env.BUILD_DIR }} PH_LIGHT_ACTION=1 - CIBW_TEST_COMMAND: "python3 -c 'import pi_heif; print(pi_heif.libheif_info())'" - CIBW_TEST_EXTRAS: "" CIBW_BUILD_VERBOSITY: 1 - name: Checking built wheels diff --git a/.github/workflows/test-src-build-linux.yml b/.github/workflows/test-src-build-linux.yml index ab4b74e1..0fd591ce 100644 --- a/.github/workflows/test-src-build-linux.yml +++ b/.github/workflows/test-src-build-linux.yml @@ -35,8 +35,13 @@ jobs: strategy: fail-fast: false matrix: - arch: ["amd64", "arm64", "arm/v7"] - docker_file: ["Alpine_3_17", "Alpine_3_18", "Debian_11", "Debian_12", "Ubuntu_22_04"] + arch: ["amd64", "arm64"] + docker_file: ["Alpine_3_18", "Almalinux_9"] + include: + - arch: "arm/v7" + docker_file: "Alpine_3_18" + - arch: "amd64" + docker_file: "Fedora_38" steps: - uses: actions/checkout@v4 @@ -66,25 +71,3 @@ jobs: push: false cache-from: type=gha,scope=${{ matrix.docker_file }}-${{ matrix.arch }} target: build_test - - ubuntu_22: - name: LibHeif==1.12.0 - runs-on: ubuntu-22.04 - - steps: - - uses: actions/checkout@v4 - - uses: actions/setup-python@v4 - with: - python-version: '3.8' - - - name: Install LibHeif from default repo - run: sudo apt -y install libheif-dev - - - name: Install from source - run: python3 -m pip -v install ".[dev]" - - - name: LibHeif info - run: python3 -c "import pillow_heif; print(pillow_heif.libheif_info())" - - - name: Run Tests - run: python3 -m pytest diff --git a/.github/workflows/test-wheels-pi_heif.yml b/.github/workflows/test-wheels-pi_heif.yml index 7e536d42..6347824a 100644 --- a/.github/workflows/test-wheels-pi_heif.yml +++ b/.github/workflows/test-wheels-pi_heif.yml @@ -24,8 +24,6 @@ jobs: { "os": "debian", "ver": "12", "arch": "amd64" }, { "os": "debian", "ver": "12", "arch": "arm64" }, { "os": "debian", "ver": "12", "arch": "arm/v7" }, - { "os": "alpine", "ver": "3.16", "arch": "amd64" }, - { "os": "alpine", "ver": "3.16", "arch": "arm64" }, { "os": "alpine", "ver": "3.17", "arch": "amd64" }, { "os": "alpine", "ver": "3.17", "arch": "arm64" }, { "os": "alpine", "ver": "3.17", "arch": "arm/v7" }, diff --git a/.github/workflows/test-wheels.yml b/.github/workflows/test-wheels.yml index 390e13f5..39f01281 100644 --- a/.github/workflows/test-wheels.yml +++ b/.github/workflows/test-wheels.yml @@ -22,8 +22,6 @@ jobs: { "os": "debian", "ver": "11", "arch": "arm64" }, { "os": "debian", "ver": "12", "arch": "amd64" }, { "os": "debian", "ver": "12", "arch": "arm64" }, - { "os": "alpine", "ver": "3.16", "arch": "amd64" }, - { "os": "alpine", "ver": "3.16", "arch": "arm64" }, { "os": "alpine", "ver": "3.17", "arch": "amd64" }, { "os": "alpine", "ver": "3.17", "arch": "arm64" }, { "os": "alpine", "ver": "3.18", "arch": "amd64" }, diff --git a/.github/workflows/wheels-pi_heif.yml b/.github/workflows/wheels-pi_heif.yml index 549bad56..c72714d4 100644 --- a/.github/workflows/wheels-pi_heif.yml +++ b/.github/workflows/wheels-pi_heif.yml @@ -17,6 +17,7 @@ jobs: check-name: 'macosx • aarch64 • Pi-Heif • Cirrus' repo-token: ${{ secrets.GITHUB_TOKEN }} wait-interval: 60 + allowed-conclusions: success - name: Download artifacts run: | @@ -173,7 +174,9 @@ jobs: env: CIBW_BUILD: ${{ format('cp3*-{0}_{1}', matrix.cibw_buildlinux, matrix.cibw_arch) }} CIBW_ARCHS: ${{ matrix.cibw_arch }} - CIBW_BEFORE_ALL_LINUX: ${{ env.INSTALL_OS_PACKAGES }} + CIBW_BEFORE_ALL_LINUX: | + ${{ env.INSTALL_OS_PACKAGES }} + python3 {package}/libheif/linux_build_libs.py CIBW_ENVIRONMENT_LINUX: BUILD_DIR=/host${{ env.BUILD_DIR }} PH_LIGHT_ACTION=1 - name: Checking built wheels @@ -231,7 +234,7 @@ jobs: env: CIBW_BUILD: ${{ format('pp3*-{0}_{1}', matrix.cibw_buildlinux, matrix.cibw_arch) }} CIBW_ARCHS: ${{ matrix.cibw_arch }} - CIBW_BEFORE_ALL_LINUX: "yum makecache && yum install -y libjpeg-turbo-devel lcms2-devel" + CIBW_BEFORE_ALL_LINUX: "yum makecache && yum install -y libjpeg-turbo-devel lcms2-devel && python3 {package}/libheif/linux_build_libs.py" CIBW_ENVIRONMENT_LINUX: BUILD_DIR=/host${{ env.BUILD_DIR }} PH_LIGHT_ACTION=1 - name: Checking built wheels diff --git a/.github/workflows/wheels-pillow_heif.yml b/.github/workflows/wheels-pillow_heif.yml index 4680593c..b49a65e4 100644 --- a/.github/workflows/wheels-pillow_heif.yml +++ b/.github/workflows/wheels-pillow_heif.yml @@ -16,6 +16,7 @@ jobs: check-name: 'macosx • aarch64 • Cirrus' repo-token: ${{ secrets.GITHUB_TOKEN }} wait-interval: 60 + allowed-conclusions: success - name: Download artifacts run: | @@ -167,7 +168,9 @@ jobs: env: CIBW_BUILD: ${{ format('cp3*-{0}_{1}', matrix.cibw_buildlinux, matrix.cibw_arch) }} CIBW_ARCHS: ${{ matrix.cibw_arch }} - CIBW_BEFORE_ALL_LINUX: ${{ env.INSTALL_OS_PACKAGES }} + CIBW_BEFORE_ALL_LINUX: | + ${{ env.INSTALL_OS_PACKAGES }} + python3 {package}/libheif/linux_build_libs.py CIBW_ENVIRONMENT_LINUX: BUILD_DIR=/host${{ env.BUILD_DIR }} PH_FULL_ACTION=1 CIBW_ENVIRONMENT_PASS_LINUX: PH_TESTS_NO_HEVC_ENC @@ -229,7 +232,9 @@ jobs: env: CIBW_BUILD: ${{ format('pp3*-{0}_{1}', matrix.cibw_buildlinux, matrix.cibw_arch) }} CIBW_ARCHS: ${{ matrix.cibw_arch }} - CIBW_BEFORE_ALL_LINUX: "yum makecache && yum install -y libjpeg-turbo-devel lcms2-devel" + CIBW_BEFORE_ALL_LINUX: | + yum makecache && yum install -y libjpeg-turbo-devel lcms2-devel + python3 {package}/libheif/linux_build_libs.py CIBW_ENVIRONMENT_LINUX: BUILD_DIR=/host${{ env.BUILD_DIR }} PH_FULL_ACTION=1 CIBW_ENVIRONMENT_PASS_LINUX: PH_TESTS_NO_HEVC_ENC diff --git a/CHANGELOG.md b/CHANGELOG.md index 2b88cc53..773887df 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ All notable changes to this project will be documented in this file. ### Changed - Minimum supported Pillow version raised to `9.2.0`. +- Linux: When building from source, `libheif` and other libraries are no longer try built automatically. #158 ### Fixed diff --git a/docker/from_src/Ubuntu_22_04.Dockerfile b/docker/from_src/Almalinux_9.Dockerfile similarity index 57% rename from docker/from_src/Ubuntu_22_04.Dockerfile rename to docker/from_src/Almalinux_9.Dockerfile index 14f61701..f509b0c1 100644 --- a/docker/from_src/Ubuntu_22_04.Dockerfile +++ b/docker/from_src/Almalinux_9.Dockerfile @@ -1,28 +1,15 @@ -FROM ubuntu:jammy as base +FROM almalinux:9 as base RUN \ - apt-get -qq update && \ - apt-get -y -q install \ - curl \ - python3-pip \ - libfribidi-dev \ - libharfbuzz-dev \ - libjpeg-dev \ - liblcms2-dev \ - libffi-dev \ - libtool \ - git \ - cmake \ - nasm \ - libde265-dev \ - libaom-dev + dnf install --nogpgcheck https://mirrors.rpmfusion.org/free/el/rpmfusion-free-release-$(rpm -E %rhel).noarch.rpm -y && \ + dnf makecache && \ + dnf install -y python3 python3-devel python3-pip libheif-freeworld && \ + dnf install -y libheif-devel && \ + dnf groupinstall -y 'Development Tools' RUN \ python3 -m pip install --upgrade pip -RUN \ - python3 -m pip install Pillow==9.3.0 - FROM base as build_test COPY . /pillow_heif diff --git a/docker/from_src/Debian_11.Dockerfile b/docker/from_src/Debian_11.Dockerfile deleted file mode 100644 index 9c3ff5e2..00000000 --- a/docker/from_src/Debian_11.Dockerfile +++ /dev/null @@ -1,39 +0,0 @@ -FROM debian:bullseye-slim as base - -RUN \ - apt-get -qq update && \ - apt-get -y -q install \ - python3-pip \ - libfribidi-dev \ - libharfbuzz-dev \ - libjpeg-dev \ - liblcms2-dev \ - libffi-dev \ - libtool \ - git \ - cmake \ - nasm \ - wget \ - libde265-dev - -RUN \ - python3 -m pip install --upgrade pip - -RUN \ - python3 -m pip install Pillow==9.2.0 - -FROM base as build_test - -COPY . /pillow_heif - -RUN \ - if [ `uname -m` = "x86_64" ]; then \ - python3 -m pip install -v "pillow_heif/.[tests]"; \ - else \ - python3 -m pip install -v "pillow_heif/.[tests-min]"; \ - export PH_TESTS_NO_HEVC_ENC=1; \ - fi && \ - echo "**** Build Done ****" && \ - python3 -c "import pillow_heif; print(pillow_heif.libheif_info())" && \ - pytest pillow_heif && \ - echo "**** Test Done ****" diff --git a/docker/from_src/Debian_12.Dockerfile b/docker/from_src/Debian_12.Dockerfile deleted file mode 100644 index 76515f2c..00000000 --- a/docker/from_src/Debian_12.Dockerfile +++ /dev/null @@ -1,31 +0,0 @@ -FROM debian:bookworm-slim as base - -RUN \ - apt-get -qq update && \ - apt-get -y -q install \ - python3-pip \ - python3-pillow \ - libffi-dev \ - libtool \ - git \ - cmake \ - nasm \ - wget \ - libde265-dev \ - libx265-dev - -FROM base as build_test - -COPY . /pillow_heif - -RUN \ - if [ `getconf LONG_BIT` = 64 ]; then \ - python3 -m pip install -v --break-system-packages "pillow_heif/.[tests]"; \ - else \ - python3 -m pip install -v --break-system-packages "pillow_heif/.[tests-min]"; \ - export PH_TESTS_NO_HEVC_ENC=1; \ - fi && \ - echo "**** Build Done ****" && \ - python3 -c "import pillow_heif; print(pillow_heif.libheif_info())" && \ - pytest pillow_heif && \ - echo "**** Test Done ****" diff --git a/docker/from_src/Alpine_3_17.Dockerfile b/docker/from_src/Fedora_38.Dockerfile similarity index 58% rename from docker/from_src/Alpine_3_17.Dockerfile rename to docker/from_src/Fedora_38.Dockerfile index 0473f05c..ce82cfaa 100644 --- a/docker/from_src/Alpine_3_17.Dockerfile +++ b/docker/from_src/Fedora_38.Dockerfile @@ -1,17 +1,11 @@ -FROM alpine:3.17 as base +FROM fedora:38 as base RUN \ - apk add --no-cache \ - python3-dev \ - py3-pip \ - perl \ - alpine-sdk \ - libffi-dev \ - cmake \ - nasm \ - aom-dev \ - py3-numpy \ - py3-pillow + dnf install -y https://download1.rpmfusion.org/free/fedora/rpmfusion-free-release-$(rpm -E %fedora).noarch.rpm && \ + dnf makecache && \ + dnf install -y python3 python3-devel python3-pip libheif-freeworld && \ + dnf install -y libheif-devel && \ + dnf groupinstall -y 'Development Tools' RUN \ python3 -m pip install --upgrade pip diff --git a/docker/manylinux_armv7l_wheels.Dockerfile b/docker/manylinux_armv7l_wheels.Dockerfile index 05aacbae..92051386 100644 --- a/docker/manylinux_armv7l_wheels.Dockerfile +++ b/docker/manylinux_armv7l_wheels.Dockerfile @@ -33,6 +33,7 @@ RUN \ python3 -m pip install pytest Pillow && \ echo "**** Start building ****" && \ export BUILD_DIR="/build_cache" && \ + python3 libheif/linux_build_libs.py && \ python3 setup.py bdist_wheel -d dist_manylinux && \ echo "**** Repairing wheel ****" && \ PTAG=$(echo $PY_VERSION | tr -d '.' | tr -d '"') && \ diff --git a/docker/musllinux_armv7l_wheels.Dockerfile b/docker/musllinux_armv7l_wheels.Dockerfile index 036bc364..e2970876 100644 --- a/docker/musllinux_armv7l_wheels.Dockerfile +++ b/docker/musllinux_armv7l_wheels.Dockerfile @@ -31,6 +31,7 @@ RUN \ python3 -m pip install pytest Pillow && \ echo "**** Start building ****" && \ export BUILD_DIR="/build_cache" && \ + python3 libheif/linux_build_libs.py && \ python3 setup.py bdist_wheel -d dist_musllinux && \ echo "**** Repairing wheel ****" && \ PTAG=$(echo $PY_VERSION | tr -d '.' | tr -d '"') && \ diff --git a/docs/installation.rst b/docs/installation.rst index 80f0ca94..6b750703 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -19,8 +19,6 @@ Wheels are present for most popular systems with help of `cibuildwheel `_. - .. role:: bash(code) :language: bash @@ -29,25 +27,27 @@ Linux .. note:: - | For installing external libraries(if they are not present in system), you should run install with **root** privileges. - | See `build_libs.py `_ for - additional info what will happen during installing from source... | Here is a `GH Action `_ - and in ``docker/from_src`` folder there are docker files for ``Debian``, ``Ubuntu`` and ``Alpine`` with examples + and in `docker/from_src `_ folder there are docker files for different Linuxes with examples how to build from source. + | + | **And of course you can build your own libheif library with your preferred encoders and decoders and use what you like.** There is many different ways how to build it from source. Main requirements are: - * ``libheif`` should be version >= ``1.12.0`` version. + * ``libheif`` should be version >= ``1.16.1`` version. * ``x265`` should support 10 - 12 bit encoding(if you want to save in that bitness) * ``aom`` should be >= ``3.3.0`` version * ``libde265`` should be >= ``1.0.8`` version -On `Ubuntu 22.04`: -| :bash:`sudo apt install -y libheif-dev` +On `Ubuntu`: + +| :bash:`sudo add-apt-repository ppa:strukturag/libheif` +| :bash:`sudo apt update` +| :bash:`sudo apt -y install libheif-dev` -On `Alpine`: +On `Alpine 18+`: | :bash:`sudo apk add --no-cache libheif-dev` @@ -59,7 +59,31 @@ or from within the uncompressed source directory:: python3 -m pip install . -If you have questions about build from sources you can ask them in discussions or create an issue. +.. note:: + + Refer to `libheif repo `_ for additional information of how to build it with what features you want. + +*If you have questions about build from sources you can ask them in discussions or create an issue.* + +FreeBSD +^^^^^^^ + +`Action to test build on FreeBSD from source `_ + +Since Python itself does not support binary wheels for BSD systems, you should install libheif and then simply install Pillow-Heif from source. + +Install `libeheif` and `gcc`:: + + pkg install -y libheif gcc + +Install Python and Pillow:: + + pkg install -y py39-pip + pkg install -y py39-pillow + +Install Pillow-Heif:: + + python3 -m pip install . macOS ^^^^^ diff --git a/libheif/linux_build_libs.py b/libheif/linux_build_libs.py index 60569dd8..0c946577 100644 --- a/libheif/linux_build_libs.py +++ b/libheif/linux_build_libs.py @@ -104,17 +104,6 @@ def is_musllinux() -> bool: return False -def is_library_installed(name: str) -> bool: - if name.find("main") != -1 and name.find("reference") != -1: - raise Exception("`name` param can not contain `main` and `reference` substrings.") - _r = run(f"gcc -l{name}".split(), stdout=PIPE, stderr=STDOUT, check=False) - if _r.stdout: - _ = _r.stdout.decode("utf-8") - if _.find("main") != -1 and _.find("reference") != -1: - return True - return False - - def run_print_if_error(args) -> None: _ = run(args, stdout=PIPE, stderr=STDOUT, check=False) if _.returncode != 0: @@ -122,7 +111,7 @@ def run_print_if_error(args) -> None: raise ChildProcessError(f"Failed: {args}") -def build_lib_linux(url: str, name: str, musl: bool = False): +def build_lib_linux(url: str, name: str): _lib_path = path.join(BUILD_DIR, name) if path.isdir(_lib_path): print(f"Cache found for {name}", flush=True) @@ -139,7 +128,7 @@ def build_lib_linux(url: str, name: str, musl: bool = False): makedirs(_build_path) if name == "aom": download_extract_to(url, path.join(_lib_path, "aom"), False) - if musl: + if is_musllinux(): patch_path = path.join(_linux_dir, "aom-musl/fix-stack-size-e53da0b-2.patch") chdir(path.join(_lib_path, "aom")) run(f"patch -p 1 -i {patch_path}".split(), check=True) @@ -193,7 +182,7 @@ def build_lib_linux(url: str, name: str, musl: bool = False): " -DWITH_LIBSHARPYUV=OFF -DENABLE_PLUGIN_LOADING=OFF".split() ) _hide_build_process = False - if musl: + if is_musllinux(): cmake_args += [f"-DCMAKE_INSTALL_LIBDIR={INSTALL_DIR_LIBS}/lib"] run(["cmake", *cmake_args], check=True) print(f"{name} configured. building...", flush=True) @@ -203,40 +192,29 @@ def build_lib_linux(url: str, name: str, musl: bool = False): run("make -j4".split(), check=True) print(f"{name} build success.", flush=True) run("make install".split(), check=True) - if musl: + if is_musllinux(): run(f"ldconfig {INSTALL_DIR_LIBS}/lib".split(), check=True) else: run("ldconfig", check=True) -def build_libs() -> str: - _is_musllinux = is_musllinux() - if is_library_installed("heif") or is_library_installed("libheif"): - print("libheif is already present.") - return INSTALL_DIR_LIBS +def build_libs() -> None: _original_dir = getcwd() try: if not tool_check_version("cmake", "3.13.4"): raise ValueError("Can not find `cmake` with version >=3.13.4") - if not is_library_installed("x265"): - if not PH_LIGHT_VERSION: - if not check_install_nasm("2.15.05"): - raise ValueError("Can not find/install `nasm` with version >=2.15.05") - build_lib_linux(LIBX265_URL, "x265", _is_musllinux) - else: - print("x265 already installed.") - if not is_library_installed("aom"): - if not PH_LIGHT_VERSION: - if not check_install_nasm("2.15.05"): - raise ValueError("Can not find/install `nasm` with version >=2.15.05") - build_lib_linux(LIBAOM_URL, "aom", _is_musllinux) - else: - print("aom already installed.") - if not is_library_installed("libde265") and not is_library_installed("de265"): - build_lib_linux(LIBDE265_URL, "libde265", _is_musllinux) - else: - print("libde265 already installed.") - build_lib_linux(LIBHEIF_URL, "libheif", _is_musllinux) + if not PH_LIGHT_VERSION: + if not check_install_nasm("2.15.05"): + raise ValueError("Can not find/install `nasm` with version >=2.15.05") + build_lib_linux(LIBX265_URL, "x265") + if not check_install_nasm("2.15.05"): + raise ValueError("Can not find/install `nasm` with version >=2.15.05") + build_lib_linux(LIBAOM_URL, "aom") + build_lib_linux(LIBDE265_URL, "libde265") + build_lib_linux(LIBHEIF_URL, "libheif") finally: chdir(_original_dir) - return INSTALL_DIR_LIBS + + +if __name__ == "__main__": + build_libs() diff --git a/pi-heif/setup.cfg b/pi-heif/setup.cfg index fe0e7cae..d1726074 100644 --- a/pi-heif/setup.cfg +++ b/pi-heif/setup.cfg @@ -37,7 +37,7 @@ python_requires = >=3.8 zip_safe = False packages = find: install_requires = - pillow>=9.1.1 + pillow>=9.2.0 [options.extras_require] tests-min = diff --git a/pillow_heif/_pillow_heif.c b/pillow_heif/_pillow_heif.c index adb1f051..a146ea17 100644 --- a/pillow_heif/_pillow_heif.c +++ b/pillow_heif/_pillow_heif.c @@ -1194,9 +1194,7 @@ static PyObject* _load_file(PyObject* self, PyObject* args) { return NULL; } - #if LIBHEIF_HAVE_VERSION(1,13,0) - heif_context_set_max_decoding_threads(heif_ctx, threads_count); - #endif + heif_context_set_max_decoding_threads(heif_ctx, threads_count); heif_item_id primary_image_id; if (check_error(heif_context_get_primary_image_ID(heif_ctx, &primary_image_id))) { @@ -1295,9 +1293,7 @@ static int setup_module(PyObject* m) { if (PyType_Ready(&CtxImage_Type) < 0) return -1; - #if LIBHEIF_HAVE_VERSION(1,14,0) - heif_init(NULL); - #endif + heif_init(NULL); const struct heif_encoder_descriptor* encoder_descriptor; const char* x265_version = ""; diff --git a/pillow_heif/_version.py b/pillow_heif/_version.py index 3c90c0d8..21d4f1e5 100644 --- a/pillow_heif/_version.py +++ b/pillow_heif/_version.py @@ -1,3 +1,3 @@ """Version of pillow_heif/pi_heif.""" -__version__ = "0.13.1" +__version__ = "0.14.0.dev0" diff --git a/pyproject.toml b/pyproject.toml index ed47c04c..47744ba2 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -99,7 +99,7 @@ before-test = [ before-all = [ "brew install libjpeg little-cms2", "brew uninstall --force --ignore-dependencies libheif aom", - "brew install --formula {project}/libheif/macos/libheif.rb", + "HOMEBREW_NO_INSTALLED_DEPENDENTS_CHECK=1 brew install --formula {project}/libheif/macos/libheif.rb", "HOMEBREW_PREFIX=$(brew --prefix)", "REPAIR_LIBRARY_PATH=$HOMEBREW_PREFIX/lib", ] diff --git a/setup.py b/setup.py index 157369c1..e6310f71 100644 --- a/setup.py +++ b/setup.py @@ -13,7 +13,12 @@ from setuptools import Extension, setup from setuptools.command.build_ext import build_ext -from libheif import linux_build_libs +# pylint: disable=too-many-branches disable=too-many-statements disable=too-many-locals +LIBHEIF_ROOT = None + + +class RequiredDependencyException(Exception): + """Raised when no ``libheif`` is found.""" def get_version(): @@ -41,9 +46,6 @@ def _pkg_config(name): command_libs.append("--keep-system-libs") command_cflags.append("--keep-system-cflags") stderr = subprocess.DEVNULL - # if not DEBUG: - # command_libs.append("--silence-errors") - # command_cflags.append("--silence-errors") libs = re.split( r"(^|\s+)-L", subprocess.check_output(command_libs, stderr=stderr).decode("utf8").strip(), @@ -65,7 +67,7 @@ def _pkg_config(name): class PillowHeifBuildExt(build_ext): """Class based on the Pillow setup method.""" - def build_extensions(self): # noqa pylint: disable=too-many-branches disable=too-many-statements + def build_extensions(self): # noqa """Builds all required python binary extensions of the project.""" if os.getenv("PRE_COMMIT"): return @@ -77,32 +79,41 @@ def build_extensions(self): # noqa pylint: disable=too-many-branches disable=to if _cmd_exists(os.environ.get("PKG_CONFIG", "pkg-config")): pkg_config = _pkg_config - root = None - libheif_found = False - if pkg_config: - for lib_name in ("heif", "libheif"): - print(f"Looking for `{lib_name}` using pkg-config.") - root = pkg_config(lib_name) - if root: - print(f"Found `{lib_name}` using pkg-config: {root}") - libheif_found = True - break - - if isinstance(root, tuple): - lib_root, include_root = root - else: - lib_root = include_root = root - - if lib_root is not None: - if not isinstance(lib_root, (tuple, list)): - lib_root = (lib_root,) - for lib_dir in lib_root: - self._add_directory(library_dirs, lib_dir) - if include_root is not None: - if not isinstance(include_root, (tuple, list)): - include_root = (include_root,) - for include_dir in include_root: - self._add_directory(include_dirs, include_dir) + for root_name, lib_name in { + "LIBHEIF_ROOT": ("libheif", "heif"), + }.items(): + root = globals()[root_name] + + if root is None and root_name in os.environ: + prefix = os.environ[root_name] + root = (os.path.join(prefix, "lib"), os.path.join(prefix, "include")) + + if root is None and pkg_config: + if isinstance(lib_name, tuple): + for lib_name2 in lib_name: + print(f"Looking for `{lib_name2}` using pkg-config.") + root = pkg_config(lib_name2) + if root: + break + else: + print(f"Looking for `{lib_name}` using pkg-config.") + root = pkg_config(lib_name) + + if isinstance(root, tuple): + lib_root, include_root = root + else: + lib_root = include_root = root + + if lib_root is not None: + if not isinstance(lib_root, (tuple, list)): + lib_root = (lib_root,) + for lib_dir in lib_root: + self._add_directory(library_dirs, lib_dir) + if include_root is not None: + if not isinstance(include_root, (tuple, list)): + include_root = (include_root,) + for include_dir in include_root: + self._add_directory(include_dirs, include_dir) # respect CFLAGS/CPPFLAGS/LDFLAGS for k in ("CFLAGS", "CPPFLAGS", "LDFLAGS"): @@ -132,7 +143,7 @@ def build_extensions(self): # noqa pylint: disable=too-many-branches disable=to if include_path_prefix is None: include_path_prefix = "C:\\msys64\\mingw64" warn( - f"MSYS2_PREFIX environment variable is not set. Assuming `MSYS2_PREFIX={include_path_prefix}`", + f"MSYS2_PREFIX environment variable is not set. Assuming MSYS2_PREFIX={include_path_prefix}", stacklevel=1, ) @@ -140,7 +151,7 @@ def build_extensions(self): # noqa pylint: disable=too-many-branches disable=to raise ValueError("MSYS2 not found and `MSYS2_PREFIX` is not set or is invalid.") library_dir = os.path.join(include_path_prefix, "lib") - # See comment a few lines below. We can't include MSYS2 directory before compiler directories :( + # See comment a few lines below. We can't include MSYS2 directory before compiler directories. # self._add_directory(include_dirs, path.join(include_path_prefix, "include")) self._add_directory(library_dirs, library_dir) lib_export_file = Path(os.path.join(library_dir, "libheif.dll.a")) @@ -174,6 +185,11 @@ def build_extensions(self): # noqa pylint: disable=too-many-branches disable=to self._add_directory(library_dirs, "/opt/local/lib") self._add_directory(include_dirs, "/opt/local/include") + sdk_path = self._get_macos_sdk_path() + if sdk_path: + self._add_directory(library_dirs, os.path.join(sdk_path, "usr", "lib")) + self._add_directory(include_dirs, os.path.join(sdk_path, "usr", "include")) + self._update_extension("_pillow_heif", ["heif"], extra_compile_args=["-Ofast", "-Werror"]) else: # let's assume it's some kind of linux # this old code waiting for refactoring, when time comes. @@ -184,16 +200,15 @@ def build_extensions(self): # noqa pylint: disable=too-many-branches disable=to self._add_directory(library_dirs, "/usr/lib") self._add_directory(library_dirs, "/lib") - if not libheif_found: - include_path_prefix = linux_build_libs.build_libs() # this needs a rework in the future - self._add_directory(library_dirs, os.path.join(include_path_prefix, "lib")) - self._add_directory(include_dirs, os.path.join(include_path_prefix, "include")) - self._update_extension("_pillow_heif", ["heif"], extra_compile_args=["-Ofast", "-Werror"]) self.compiler.library_dirs = library_dirs + self.compiler.library_dirs self.compiler.include_dirs = include_dirs + self.compiler.include_dirs + heif_include = self._find_include_dir("libheif", "heif.h") + if not heif_include: + raise RequiredDependencyException("libheif") + build_ext.build_extensions(self) def _update_extension(self, name, libraries, extra_compile_args=None, extra_link_args=None): @@ -205,6 +220,21 @@ def _update_extension(self, name, libraries, extra_compile_args=None, extra_link if extra_link_args is not None: extension.extra_link_args += extra_link_args + def _find_include_dir(self, dirname, include): + for directory in self.compiler.include_dirs: + print("Checking for include file %s in %s", (include, directory)) + result_path = os.path.join(directory, include) + if os.path.isfile(result_path): + print("Found %s in %s", (include, directory)) + return result_path + subdir = os.path.join(directory, dirname) + print("Checking for include file %s in %s", (include, subdir)) + result_path = os.path.join(subdir, include) + if os.path.isfile(result_path): + print("Found %s in %s", (include, subdir)) + return result_path + return "" + @staticmethod def _add_directory(paths: List, subdir): if subdir: @@ -212,12 +242,41 @@ def _add_directory(paths: List, subdir): if os.path.isdir(subdir) and subdir not in paths: paths.append(subdir) - -if os.getenv("READTHEDOCS", "False") == "True": - setup(version=get_version()) -else: - setup( - version=get_version(), - cmdclass={"build_ext": PillowHeifBuildExt}, - ext_modules=[Extension("_pillow_heif", ["pillow_heif/_pillow_heif.c"])], - ) + @staticmethod + def _get_macos_sdk_path(): + try: + sdk_path = subprocess.check_output(["xcrun", "--show-sdk-path"]).strip().decode("latin1") + except Exception: # noqa # pylint: disable=broad-exception-caught + sdk_path = None + if ( + not sdk_path + or sdk_path + == "/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk" + ): + commandlinetools_sdk_path = "/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk" + if os.path.exists(commandlinetools_sdk_path): + sdk_path = commandlinetools_sdk_path + return sdk_path + + +try: + if os.getenv("READTHEDOCS", "False") == "True": + setup(version=get_version()) + else: + setup( + version=get_version(), + cmdclass={"build_ext": PillowHeifBuildExt}, + ext_modules=[Extension("_pillow_heif", ["pillow_heif/_pillow_heif.c"])], + ) +except RequiredDependencyException as err: + msg = f""" + +The headers or library files could not be found for {err}, +a required dependency when compiling Pillow-Heif from source. + +Please see the install instructions at: + https://pillow-heif.readthedocs.io/en/latest/installation.html + +""" + sys.stderr.write(msg) + raise RequiredDependencyException(msg) from None