diff --git a/.appveyor.yml b/.appveyor.yml index 36f5bd0ad68..60132a9a35a 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -20,6 +20,7 @@ environment: install: - '%PYTHON%\%EXECUTABLE% --version' +- '%PYTHON%\%EXECUTABLE% -m pip install --upgrade pip' - curl -fsSL -o pillow-depends.zip https://github.com/python-pillow/pillow-depends/archive/main.zip - curl -fsSL -o pillow-test-images.zip https://github.com/python-pillow/test-images/archive/main.zip - 7z x pillow-depends.zip -oc:\ @@ -37,10 +38,9 @@ install: - path C:\pillow\winbuild\build\bin;%PATH% build_script: -- ps: | - c:\pillow\winbuild\build\build_pillow.cmd install - $host.SetShouldExit(0) - cd c:\pillow +- winbuild\build\build_env.cmd +- '%PYTHON%\%EXECUTABLE% -m pip install -v -C raqm=vendor -C fribidi=vendor .' - '%PYTHON%\%EXECUTABLE% selftest.py --installed' test_script: @@ -62,18 +62,15 @@ cache: - '%LOCALAPPDATA%\pip\Cache' artifacts: -- path: pillow\dist\*.egg +- path: pillow\*.egg name: egg -- path: pillow\dist\*.wheel +- path: pillow\*.whl name: wheel before_deploy: - cd c:\pillow - - '%PYTHON%\%EXECUTABLE% -m pip install wheel' - - cd c:\pillow\winbuild\ - - c:\pillow\winbuild\build\build_pillow.cmd bdist_wheel - - cd c:\pillow - - ps: Get-ChildItem .\dist\*.* | % { Push-AppveyorArtifact $_.FullName -FileName $_.Name } + - '%PYTHON%\%EXECUTABLE% -m pip wheel -v -C raqm=vendor -C fribidi=vendor .' + - ps: Get-ChildItem .\*.whl | % { Push-AppveyorArtifact $_.FullName -FileName $_.Name } deploy: provider: S3 diff --git a/.github/workflows/test-windows.yml b/.github/workflows/test-windows.yml index 3a24fd36a3e..70afbab24ee 100644 --- a/.github/workflows/test-windows.yml +++ b/.github/workflows/test-windows.yml @@ -57,8 +57,8 @@ jobs: - name: Print build system information run: python3 .github/workflows/system-info.py - - name: python3 -m pip install setuptools wheel pytest pytest-cov pytest-timeout defusedxml - run: python3 -m pip install setuptools wheel pytest pytest-cov pytest-timeout defusedxml + - name: python3 -m pip install pytest pytest-cov pytest-timeout defusedxml + run: python3 -m pip install pytest pytest-cov pytest-timeout defusedxml - name: Install dependencies id: install @@ -89,7 +89,7 @@ jobs: - name: Prepare build if: steps.build-cache.outputs.cache-hit != 'true' run: | - & python.exe winbuild\build_prepare.py -v --python $env:pythonLocation + & python.exe winbuild\build_prepare.py -v shell: pwsh - name: Build dependencies / libjpeg-turbo @@ -157,9 +157,9 @@ jobs: - name: Build Pillow run: | - $FLAGS="" - if ('${{ github.event_name }}' -ne 'pull_request') { $FLAGS="--disable-imagequant" } - & winbuild\build\build_pillow.cmd $FLAGS install + $FLAGS="-C raqm=vendor -C fribidi=vendor" + if ('${{ github.event_name }}' -ne 'pull_request') { $FLAGS+=" -C imagequant=disable" } + cmd /c "winbuild\build\build_env.cmd && $env:pythonLocation\python.exe -m pip install -v $FLAGS ." & $env:pythonLocation\python.exe selftest.py --installed shell: pwsh @@ -223,7 +223,8 @@ jobs: ) ) for /f "tokens=3 delims=/" %%a in ("${{ github.ref }}") do echo dist=dist-%%a >> %GITHUB_OUTPUT% - winbuild\\build\\build_pillow.cmd --disable-imagequant bdist_wheel + call winbuild\\build\\build_env.cmd + %pythonLocation%\python.exe -m pip wheel -v -C raqm=vendor -C fribidi=vendor -C imagequant=disable . shell: cmd - name: Upload wheel @@ -231,7 +232,7 @@ jobs: if: "github.event_name != 'pull_request'" with: name: ${{ steps.wheel.outputs.dist }} - path: dist\*.whl + path: "*.whl" - name: Upload fribidi.dll if: "github.event_name != 'pull_request' && matrix.python-version == 3.11" diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 0ddc6beb415..872c73843c6 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -46,6 +46,7 @@ repos: hooks: - id: check-merge-conflict - id: check-json + - id: check-toml - id: check-yaml - repo: https://github.com/sphinx-contrib/sphinx-lint @@ -53,6 +54,16 @@ repos: hooks: - id: sphinx-lint + - repo: https://github.com/tox-dev/pyproject-fmt + rev: 0.12.1 + hooks: + - id: pyproject-fmt + + - repo: https://github.com/abravalheri/validate-pyproject + rev: v0.13 + hooks: + - id: validate-pyproject + - repo: https://github.com/tox-dev/tox-ini-fmt rev: 1.3.0 hooks: diff --git a/pyproject.toml b/pyproject.toml index cf31b6407ee..93a43360891 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,4 +1,9 @@ [build-system] -requires = ["setuptools >= 40.8.0", "wheel"] build-backend = "backend" -backend-path = ["_custom_build"] +requires = [ + "setuptools>=67.8", + "wheel", +] +backend-path = [ + "_custom_build", +] diff --git a/setup.py b/setup.py index 7c1ad6dc5b2..024634ad8f9 100755 --- a/setup.py +++ b/setup.py @@ -137,7 +137,6 @@ class RequiredDependencyException(Exception): PLATFORM_MINGW = os.name == "nt" and "GCC" in sys.version -PLATFORM_PYPY = hasattr(sys, "pypy_version_info") def _dbg(s, tp=None): @@ -848,14 +847,7 @@ def build_extensions(self): if struct.unpack("h", b"\0\1")[0] == 1: defs.append(("WORDS_BIGENDIAN", None)) - if ( - sys.platform == "win32" - and sys.version_info < (3, 9) - and not (PLATFORM_PYPY or PLATFORM_MINGW) - ): - defs.append(("PILLOW_VERSION", f'"\\"{PILLOW_VERSION}\\""')) - else: - defs.append(("PILLOW_VERSION", f'"{PILLOW_VERSION}"')) + defs.append(("PILLOW_VERSION", f'"{PILLOW_VERSION}"')) self._update_extension("PIL._imaging", libs, defs) diff --git a/winbuild/README.md b/winbuild/README.md index 2975acf283f..7e81abcb0e5 100644 --- a/winbuild/README.md +++ b/winbuild/README.md @@ -18,12 +18,12 @@ The following is a simplified version of the script used on AppVeyor: ``` set PYTHON=C:\Python38\bin cd /D C:\Pillow\winbuild -C:\Python39\bin\python.exe build_prepare.py -v --depends=C:\pillow-depends +%PYTHON%\python.exe build_prepare.py -v --depends=C:\pillow-depends build\build_dep_all.cmd -build\build_pillow.cmd install cd .. +%PYTHON%\python.exe -m pip install -v -C raqm=vendor -C fribidi=vendor . path C:\Pillow\winbuild\build\bin;%PATH% %PYTHON%\python.exe selftest.py %PYTHON%\python.exe -m pytest -vx --cov PIL --cov Tests --cov-report term --cov-report xml Tests -build\build_pillow.cmd bdist_wheel +%PYTHON%\python.exe -m pip wheel -v -C raqm=vendor -C fribidi=vendor . ``` diff --git a/winbuild/build.rst b/winbuild/build.rst index 99dfad3015f..a8e4ebaa6cc 100644 --- a/winbuild/build.rst +++ b/winbuild/build.rst @@ -42,11 +42,10 @@ Run ``build_prepare.py`` to configure the build:: usage: winbuild\build_prepare.py [-h] [-v] [-d PILLOW_BUILD] [--depends PILLOW_DEPS] - [--architecture {x86,x64,ARM64}] - [--python PYTHON] [--executable EXECUTABLE] - [--nmake] [--no-imagequant] [--no-fribidi] + [--architecture {x86,x64,ARM64}] [--nmake] + [--no-imagequant] [--no-fribidi] - Download dependencies and generate build scripts for Pillow. + Download and generate build scripts for Pillow dependencies. options: -h, --help show this help message and exit @@ -58,17 +57,13 @@ Run ``build_prepare.py`` to configure the build:: 'winbuild\depends') --architecture {x86,x64,ARM64} build architecture (default: same as host Python) - --python PYTHON Python install directory (default: use host Python) - --executable EXECUTABLE - Python executable (default: use host Python) --nmake build dependencies using NMake instead of Ninja --no-imagequant skip GPL-licensed optional dependency libimagequant --no-fribidi, --no-raqm skip LGPL-licensed optional dependency FriBiDi Arguments can also be supplied using the environment variables PILLOW_BUILD, - PILLOW_DEPS, ARCHITECTURE, PYTHON, EXECUTABLE. See winbuild\build.rst for more - information. + PILLOW_DEPS, ARCHITECTURE. See winbuild\build.rst for more information. **Warning:** The build directory is wiped when ``build_prepare.py`` is run. @@ -86,14 +81,16 @@ or run the individual scripts in order to build each dependency separately. Building Pillow --------------- -Once the dependencies are built, run -``winbuild\build\build_pillow.cmd install`` to build and install -Pillow for the selected version of Python. -``winbuild\build\build_pillow.cmd bdist_wheel`` will build wheels -instead of installing Pillow. +Once the dependencies are built, make sure the required environment variables +are set by running ``winbuild\build\build_env.cmd`` and install Pillow with pip:: -You can also use ``winbuild\build\build_pillow.cmd --inplace develop`` to build -and install Pillow in develop mode (instead of ``python3 -m pip install --editable``). + winbuild\build\build_env.cmd + python.exe -m pip install -v -C raqm=vendor -C fribidi=vendor . + +To build a wheel instead, run:: + + winbuild\build\build_env.cmd + python.exe -m pip wheel -v -C raqm=vendor -C fribidi=vendor . Testing Pillow -------------- @@ -112,11 +109,12 @@ The following is a simplified version of the script used on AppVeyor:: set PYTHON=C:\Python38\bin cd /D C:\Pillow\winbuild - C:\Python39\bin\python.exe build_prepare.py -v --depends C:\pillow-depends + %PYTHON%\python.exe build_prepare.py -v --depends C:\pillow-depends build\build_dep_all.cmd - build\build_pillow.cmd install + build\build_env.cmd cd .. + %PYTHON%\python.exe -m pip install -v -C raqm=vendor -C fribidi=vendor . path C:\Pillow\winbuild\build\bin;%PATH% %PYTHON%\python.exe selftest.py %PYTHON%\python.exe -m pytest -vx --cov PIL --cov Tests --cov-report term --cov-report xml Tests - build\build_pillow.cmd bdist_wheel + %PYTHON%\python.exe -m pip wheel -v -C raqm=vendor -C fribidi=vendor . diff --git a/winbuild/build_prepare.py b/winbuild/build_prepare.py index b9de071a0a5..0787a66ddce 100644 --- a/winbuild/build_prepare.py +++ b/winbuild/build_prepare.py @@ -5,7 +5,6 @@ import shutil import struct import subprocess -import sys def cmd_cd(path): @@ -103,13 +102,6 @@ def cmd_msbuild( "ARM64": {"vcvars_arch": "x86_arm64", "msbuild_arch": "ARM64"}, } -header = [ - cmd_set("INCLUDE", "{inc_dir}"), - cmd_set("INCLIB", "{lib_dir}"), - cmd_set("LIB", "{lib_dir}"), - cmd_append("PATH", "{bin_dir}"), -] - # dependencies, listed in order of compilation deps = { "libjpeg": { @@ -401,23 +393,12 @@ def find_msvs(): print("Visual Studio seems to be missing C compiler") return None - vs = { - "header": [], - # nmake selected by vcvarsall - "nmake": "nmake.exe", - "vs_dir": vspath, - } - # vs2017 msbuild = os.path.join(vspath, "MSBuild", "15.0", "Bin", "MSBuild.exe") - if os.path.isfile(msbuild): - vs["msbuild"] = f'"{msbuild}"' - else: + if not os.path.isfile(msbuild): # vs2019 msbuild = os.path.join(vspath, "MSBuild", "Current", "Bin", "MSBuild.exe") - if os.path.isfile(msbuild): - vs["msbuild"] = f'"{msbuild}"' - else: + if not os.path.isfile(msbuild): print("Visual Studio MSBuild not found") return None @@ -425,9 +406,13 @@ def find_msvs(): if not os.path.isfile(vcvarsall): print("Visual Studio vcvarsall not found") return None - vs["header"].append(f'call "{vcvarsall}" {{vcvars_arch}}') - return vs + return { + "vs_dir": vspath, + "msbuild": f'"{msbuild}"', + "vcvarsall": f'"{vcvarsall}"', + "nmake": "nmake.exe", # nmake selected by vcvarsall + } def extract_dep(url, filename): @@ -497,6 +482,22 @@ def get_footer(dep): return lines +def build_env(): + lines = [ + "if defined DISTUTILS_USE_SDK goto end", + cmd_set("INCLUDE", "{inc_dir}"), + cmd_set("INCLIB", "{lib_dir}"), + cmd_set("LIB", "{lib_dir}"), + cmd_append("PATH", "{bin_dir}"), + "call {vcvarsall} {vcvars_arch}", + cmd_set("DISTUTILS_USE_SDK", "1"), # use same compiler to build Pillow + cmd_set("py_vcruntime_redist", "true"), # always use /MD, never /MT + ":end", + "@echo on", + ] + write_script("build_env.cmd", lines) + + def build_dep(name): dep = deps[name] dir = dep["dir"] @@ -534,11 +535,11 @@ def build_dep(name): banner = f"Building {name} ({dir})" lines = [ + r'call "{build_dir}\build_env.cmd"', "@echo " + ("=" * 70), f"@echo ==== {banner:<60} ====", "@echo " + ("=" * 70), - "cd /D %s" % os.path.join(sources_dir, dir), - *prefs["header"], + cmd_cd(os.path.join(sources_dir, dir)), *dep.get("build", []), *get_footer(dep), ] @@ -548,7 +549,7 @@ def build_dep(name): def build_dep_all(): - lines = ["@echo on"] + lines = [r'call "{build_dir}\build_env.cmd"'] for dep_name in deps: print() if dep_name in disabled: @@ -562,29 +563,16 @@ def build_dep_all(): write_script("build_dep_all.cmd", lines) -def build_pillow(): - lines = [ - "@echo ---- Building Pillow (build_ext %*) ----", - cmd_cd("{pillow_dir}"), - *prefs["header"], - cmd_set("DISTUTILS_USE_SDK", "1"), # use same compiler to build Pillow - cmd_set("py_vcruntime_redist", "true"), # always use /MD, never /MT - r'"{python_dir}\{python_exe}" setup.py build_ext --vendor-raqm --vendor-fribidi %*', # noqa: E501 - ] - - write_script("build_pillow.cmd", lines) - - if __name__ == "__main__": winbuild_dir = os.path.dirname(os.path.realpath(__file__)) pillow_dir = os.path.realpath(os.path.join(winbuild_dir, "..")) parser = argparse.ArgumentParser( prog="winbuild\\build_prepare.py", - description="Download dependencies and generate build scripts for Pillow.", + description="Download and generate build scripts for Pillow dependencies.", epilog="""Arguments can also be supplied using the environment variables - PILLOW_BUILD, PILLOW_DEPS, ARCHITECTURE, PYTHON, EXECUTABLE. - See winbuild\\build.rst for more information.""", + PILLOW_BUILD, PILLOW_DEPS, ARCHITECTURE. See winbuild\\build.rst + for more information.""", ) parser.add_argument( "-v", "--verbose", action="store_true", help="print generated scripts" @@ -619,20 +607,6 @@ def build_pillow(): ), help="build architecture (default: same as host Python)", ) - parser.add_argument( - "--python", - dest="python_dir", - metavar="PYTHON", - default=os.environ.get("PYTHON"), - help="Python install directory (default: use host Python)", - ) - parser.add_argument( - "--executable", - dest="python_exe", - metavar="EXECUTABLE", - default=os.environ.get("EXECUTABLE", "python.exe"), - help="Python executable (default: use host Python)", - ) parser.add_argument( "--nmake", dest="cmake_generator", @@ -657,11 +631,6 @@ def build_pillow(): arch_prefs = architectures[args.architecture] print("Target architecture:", args.architecture) - if args.python_dir is None: - args.python_dir = os.path.dirname(os.path.realpath(sys.executable)) - args.python_exe = os.path.basename(sys.executable) - print("Target Python:", os.path.join(args.python_dir, args.python_exe)) - msvs = find_msvs() if msvs is None: msg = "Visual Studio not found. Please install Visual Studio 2017 or newer." @@ -699,9 +668,6 @@ def build_pillow(): disabled += ["fribidi"] prefs = { - # Python paths / preferences - "python_dir": args.python_dir, - "python_exe": args.python_exe, "architecture": args.architecture, **arch_prefs, # Pillow paths @@ -719,8 +685,6 @@ def build_pillow(): "cmake": "cmake.exe", # TODO find CMAKE automatically "cmake_generator": args.cmake_generator, # TODO find NASM automatically - # script header - "header": sum([header, msvs["header"], ["@echo on"]], []), } for k, v in deps.items(): @@ -729,7 +693,5 @@ def build_pillow(): print() write_script(".gitignore", ["*"]) + build_env() build_dep_all() - if args.verbose: - print() - build_pillow()