Merge remote-tracking branch 'upstream/master' into gha-msys

# Conflicts:
#	.appveyor.yml
#	.github/workflows/test-windows.yml
This commit is contained in:
nulano 2020-05-25 17:37:26 +02:00
commit 21325b8ce3
25 changed files with 803 additions and 1336 deletions

View File

@ -1,4 +1,5 @@
version: '{build}'
image: Visual Studio 2017
clone_folder: c:\pillow
init:
- ECHO %PYTHON%
@ -6,46 +7,36 @@ init:
# Uncomment previous line to get RDP access during the build.
environment:
X64_EXT: -x64
EXECUTABLE: python.exe
PIP_DIR: Scripts
VENV: NO
TEST_OPTIONS:
DEPLOY: YES
matrix:
- PYTHON: C:/Python38
- PYTHON: C:/Python38-x64
- PYTHON: C:/Python35
ARCHITECTURE: x86
- PYTHON: C:/Python35-x64
- PYTHON: C:/vp/pypy3
EXECUTABLE: bin/pypy.exe
VENV: YES
ARCHITECTURE: x64
install:
- curl -fsSL -o pillow-depends.zip https://github.com/python-pillow/pillow-depends/archive/master.zip
- 7z x pillow-depends.zip -oc:\
- mv c:\pillow-depends-master c:\pillow-depends
- xcopy c:\pillow-depends\*.zip c:\pillow\winbuild\
- xcopy c:\pillow-depends\*.tar.gz c:\pillow\winbuild\
- xcopy /s c:\pillow-depends\test_images\* c:\pillow\tests\images
- cd c:\pillow\winbuild\
- ps: |
if ($env:PYTHON -eq "c:/vp/pypy3")
{
c:\pillow\winbuild\appveyor_install_pypy3.cmd
}
- ps: |
c:\python37\python.exe c:\pillow\winbuild\build_dep.py
c:\pillow\winbuild\build_deps.cmd
$host.SetShouldExit(0)
- 7z x ..\pillow-depends\nasm-2.14.02-win64.zip -oc:\
- curl -fsSL -o gs952.exe https://github.com/ArtifexSoftware/ghostpdl-downloads/releases/download/gs952/gs952w32.exe
- gs952.exe /S
- path %path%;C:\Program Files (x86)\gs\gs9.52\bin
- path c:\nasm-2.14.02;C:\Program Files (x86)\gs\gs9.52\bin;%PATH%
- cd c:\pillow\winbuild\
- ps: |
c:\python37\python.exe c:\pillow\winbuild\build_prepare.py -v --depends=C:\pillow-depends\
c:\pillow\winbuild\build\build_dep_all.cmd
$host.SetShouldExit(0)
- path C:\pillow\winbuild\build\bin;%PATH%
build_script:
- ps: |
& $env:PYTHON/$env:EXECUTABLE c:\pillow\winbuild\build.py
c:\pillow\winbuild\build\build_pillow.cmd install
$host.SetShouldExit(0)
- cd c:\pillow
- '%PYTHON%\%EXECUTABLE% selftest.py --installed'
@ -77,7 +68,7 @@ before_deploy:
- cd c:\pillow
- '%PYTHON%\%PIP_DIR%\pip.exe install wheel'
- cd c:\pillow\winbuild\
- '%PYTHON%\%EXECUTABLE% c:\pillow\winbuild\build.py --wheel'
- c:\pillow\winbuild\build\build_pillow.cmd bdist_wheel
- cd c:\pillow
- ps: Get-ChildItem .\dist\*.* | % { Push-AppveyorArtifact $_.FullName -FileName $_.Name }

View File

@ -13,7 +13,7 @@ jobs:
name: Python ${{ matrix.python-version }}
steps:
- uses: actions/checkout@v1
- uses: actions/checkout@v2
- name: pip cache
uses: actions/cache@v1

View File

@ -30,7 +30,7 @@ jobs:
name: ${{ matrix.docker }}
steps:
- uses: actions/checkout@v1
- uses: actions/checkout@v2
- name: Build system information
run: python .github/workflows/system-info.py

View File

@ -4,7 +4,6 @@ on: [push, pull_request]
jobs:
build:
runs-on: windows-2019
strategy:
fail-fast: false
@ -27,14 +26,16 @@ jobs:
name: Python ${{ matrix.python-version }} ${{ matrix.architecture }}
steps:
- uses: actions/checkout@v1
- name: Checkout Pillow
uses: actions/checkout@v2
- uses: actions/checkout@v1
- name: Checkout cached dependencies
uses: actions/checkout@v2
with:
repository: python-pillow/pillow-depends
ref: master
path: winbuild\depends
- name: Cache
- name: Cache pip
uses: actions/cache@v1
with:
path: ~\AppData\Local\pip\Cache
@ -51,291 +52,68 @@ jobs:
python-version: ${{ matrix.python-version }}
architecture: ${{ matrix.architecture }}
- name: Build system information
- name: Print build system information
run: python .github/workflows/system-info.py
- name: pip install wheel pytest pytest-cov
run: |
"%pythonLocation%\python.exe" -m pip install wheel pytest pytest-cov
shell: cmd
run: python -m pip install wheel pytest pytest-cov
- name: Fetch dependencies
- name: Prepare dependencies
run: |
7z x ..\pillow-depends\nasm-2.14.02-win64.zip "-o$env:RUNNER_WORKSPACE\"
Write-Host "`#`#[add-path]$env:RUNNER_WORKSPACE\nasm-2.14.02"
7z x winbuild\depends\nasm-2.14.02-win64.zip "-o$env:RUNNER_WORKSPACE\"
Write-Host "::add-path::$env:RUNNER_WORKSPACE\nasm-2.14.02"
..\pillow-depends\gs950w32.exe /S
Write-Host "`#`#[add-path]C:\Program Files (x86)\gs\gs9.50\bin"
winbuild\depends\gs950w32.exe /S
Write-Host "::add-path::C:\Program Files (x86)\gs\gs9.50\bin"
$env:PYTHON=$env:pythonLocation
xcopy ..\pillow-depends\*.zip $env:GITHUB_WORKSPACE\winbuild\
xcopy ..\pillow-depends\*.tar.gz $env:GITHUB_WORKSPACE\winbuild\
xcopy /s ..\pillow-depends\test_images\* $env:GITHUB_WORKSPACE\tests\images\
cd $env:GITHUB_WORKSPACE/winbuild/
python.exe $env:GITHUB_WORKSPACE\winbuild\build_dep.py
env:
EXECUTABLE: bin\python.exe
xcopy /s winbuild\depends\test_images\* Tests\images\
& python.exe winbuild\build_prepare.py -v --python=$env:pythonLocation
shell: pwsh
- name: Build dependencies / libjpeg
if: false
run: |
REM FIXME uses /MT not /MD, see makefile.vc and win32.mak for more info
set INCLUDE=C:\Program Files (x86)\Microsoft SDKs\Windows\V7.1A\Include
set INCLIB=%GITHUB_WORKSPACE%\winbuild\depends\msvcr10-x32
set BUILD=%GITHUB_WORKSPACE%\winbuild\build
cd /D %BUILD%\jpeg-9d
call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" ${{ matrix.platform-vcvars }}
echo on
nmake -nologo -f makefile.vc setup-vc6
nmake -nologo -f makefile.vc clean
nmake -nologo -f makefile.vc nodebug=1 libjpeg.lib cjpeg.exe djpeg.exe
copy /Y /B j*.h %INCLIB%
copy /Y /B *.lib %INCLIB%
copy /Y /B *.exe %INCLIB%
shell: cmd
- name: Build dependencies / libjpeg-turbo
run: |
set INCLUDE=C:\Program Files (x86)\Microsoft SDKs\Windows\V7.1A\Include
set INCLIB=%GITHUB_WORKSPACE%\winbuild\depends\msvcr10-x32
set BUILD=%GITHUB_WORKSPACE%\winbuild\build
cd /D %BUILD%\libjpeg-turbo-2.0.3
call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" ${{ matrix.platform-vcvars }}
echo on
set CMAKE=cmake.exe -DCMAKE_VERBOSE_MAKEFILE=ON -DCMAKE_RULE_MESSAGES:BOOL=OFF
set CMAKE=%CMAKE% -DENABLE_SHARED:BOOL=OFF -DWITH_JPEG8:BOOL=TRUE -DWITH_CRT_DLL:BOOL=TRUE -DCMAKE_BUILD_TYPE=Release
%CMAKE% -G "NMake Makefiles" .
nmake -nologo -f Makefile clean
nmake -nologo -f Makefile jpeg-static cjpeg-static djpeg-static
copy /Y /B j*.h %INCLIB%
copy /Y /B jpeg-static.lib %INCLIB%\libjpeg.lib
copy /Y /B cjpeg-static.exe %INCLIB%\cjpeg.exe
copy /Y /B djpeg-static.exe %INCLIB%\djpeg.exe
shell: cmd
run: "& winbuild\\build\\build_dep_libjpeg.cmd"
- name: Build dependencies / zlib
run: |
set INCLUDE=C:\Program Files (x86)\Microsoft SDKs\Windows\V7.1A\Include
set INCLIB=%GITHUB_WORKSPACE%\winbuild\depends\msvcr10-x32
set BUILD=%GITHUB_WORKSPACE%\winbuild\build
cd /D %BUILD%\zlib-1.2.11
call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" ${{ matrix.platform-vcvars }}
echo on
nmake -nologo -f win32\Makefile.msc clean
nmake -nologo -f win32\Makefile.msc zlib.lib
copy /Y /B z*.h %INCLIB%
copy /Y /B *.lib %INCLIB%
copy /Y /B zlib.lib %INCLIB%\z.lib
shell: cmd
- name: Build dependencies / LibTIFF
run: |
set INCLUDE=C:\Program Files (x86)\Microsoft SDKs\Windows\V7.1A\Include
set INCLIB=%GITHUB_WORKSPACE%\winbuild\depends\msvcr10-x32
set BUILD=%GITHUB_WORKSPACE%\winbuild\build
cd /D %BUILD%\tiff-4.1.0
call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" ${{ matrix.platform-vcvars }}
echo on
copy %GITHUB_WORKSPACE%\winbuild\tiff.opt nmake.opt
nmake -nologo -f makefile.vc clean
nmake -nologo -f makefile.vc lib
copy /Y /B libtiff\tiff*.h %INCLIB%
copy /Y /B libtiff\*.dll %INCLIB%
copy /Y /B libtiff\*.lib %INCLIB%
shell: cmd
run: "& winbuild\\build\\build_dep_zlib.cmd"
- name: Build dependencies / LibTiff
run: "& winbuild\\build\\build_dep_libtiff.cmd"
- name: Build dependencies / WebP
run: |
set INCLUDE=C:\Program Files (x86)\Microsoft SDKs\Windows\V7.1A\Include
set INCLIB=%GITHUB_WORKSPACE%\winbuild\depends\msvcr10-x32
set BUILD=%GITHUB_WORKSPACE%\winbuild\build
cd /D %BUILD%\libwebp-1.1.0
call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" ${{ matrix.platform-vcvars }}
echo on
rmdir /S /Q output\release-static
nmake -nologo -f Makefile.vc CFG=release-static OBJDIR=output ARCH=${{ matrix.architecture }} all
mkdir %INCLIB%\webp
copy /Y /B src\webp\*.h %INCLIB%\webp
copy /Y /B output\release-static\${{ matrix.architecture }}\lib\* %INCLIB%
shell: cmd
run: "& winbuild\\build\\build_dep_libwebp.cmd"
- name: Build dependencies / FreeType
run: |
REM Toolkit v100 not available; missing VCTargetsPath; Clean fails
set INCLUDE=C:\Program Files (x86)\Microsoft SDKs\Windows\V7.1A\Include
set INCLIB=%GITHUB_WORKSPACE%\winbuild\depends\msvcr10-x32
set BUILD=%GITHUB_WORKSPACE%\winbuild\build
cd /D %BUILD%\freetype-2.10.2
call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" ${{ matrix.platform-vcvars }}
echo on
rmdir /S /Q objs
set DefaultPlatformToolset=v142
set VCTargetsPath=C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\MSBuild\Microsoft\VC\v160\
set MSBUILD="C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\MSBuild\Current\Bin\MSBuild.exe"
powershell -Command "(gc builds\windows\vc2010\freetype.vcxproj) -replace 'MultiThreaded<', 'MultiThreadedDLL<' | Out-File -encoding ASCII builds\windows\vc2010\freetype.vcxproj"
%MSBUILD% builds\windows\vc2010\freetype.sln /t:Build /p:Configuration="Release Static" /p:Platform=${{ matrix.platform-msbuild }} /m
xcopy /Y /E /Q include %INCLIB%
copy /Y /B "objs\${{ matrix.platform-msbuild }}\Release Static\freetype.lib" %INCLIB%
shell: cmd
run: "& winbuild\\build\\build_dep_freetype.cmd"
- name: Build dependencies / LCMS2
run: |
set INCLUDE=C:\Program Files (x86)\Microsoft SDKs\Windows\V7.1A\Include
set INCLIB=%GITHUB_WORKSPACE%\winbuild\depends\msvcr10-x32
set BUILD=%GITHUB_WORKSPACE%\winbuild\build
cd /D %BUILD%\lcms2-2.8
call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" ${{ matrix.platform-vcvars }}
echo on
rmdir /S /Q Lib
rmdir /S /Q Projects\VC2015\Release
set VCTargetsPath=C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\MSBuild\Microsoft\VC\v160\
set MSBUILD="C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\MSBuild\Current\Bin\MSBuild.exe"
powershell %GITHUB_WORKSPACE%\winbuild\lcms2_patch.ps1
%MSBUILD% Projects\VC2015\lcms2.sln /t:Clean;lcms2_static /p:Configuration="Release" /p:Platform=${{ matrix.platform-msbuild }} /m
xcopy /Y /E /Q include %INCLIB%
copy /Y /B Lib\MS\*.lib %INCLIB%
shell: cmd
run: "& winbuild\\build\\build_dep_lcms2.cmd"
- name: Build dependencies / OpenJPEG
run: |
set INCLUDE=C:\Program Files (x86)\Microsoft SDKs\Windows\V7.1A\Include
set INCLIB=%GITHUB_WORKSPACE%\winbuild\depends\msvcr10-x32
set BUILD=%GITHUB_WORKSPACE%\winbuild\build
cd /D %BUILD%\openjpeg-2.3.1msvcr10-x32
call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" ${{ matrix.platform-vcvars }}
echo on
set CMAKE=cmake.exe -DCMAKE_VERBOSE_MAKEFILE=ON -DCMAKE_RULE_MESSAGES:BOOL=OFF
set CMAKE=%CMAKE% -DBUILD_THIRDPARTY:BOOL=OFF -DBUILD_SHARED_LIBS:BOOL=OFF
set CMAKE=%CMAKE% -DCMAKE_BUILD_TYPE=Release
%CMAKE% -G "NMake Makefiles" .
nmake -nologo -f Makefile clean
nmake -nologo -f Makefile
mkdir %INCLIB%\openjpeg-2.3.1
copy /Y /B src\lib\openjp2\*.h %INCLIB%\openjpeg-2.3.1
copy /Y /B bin\*.lib %INCLIB%
shell: cmd
run: "& winbuild\\build\\build_dep_openjpeg.cmd"
# GPL licensed; skip if building wheels
- name: Build dependencies / libimagequant
if: "github.event_name != 'push' || contains(matrix.python-version, 'pypy')"
run: |
set INCLUDE=C:\Program Files (x86)\Microsoft SDKs\Windows\V7.1A\Include
set INCLIB=%GITHUB_WORKSPACE%\winbuild\depends\msvcr10-x32
set BUILD=%GITHUB_WORKSPACE%\winbuild\build
rem e5d454b: Merge tag '2.12.6' into msvc
cd /D %BUILD%\libimagequant-e5d454bc7f5eb63ee50c84a83a7fa5ac94f68ec4
call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" ${{ matrix.platform-vcvars }}
echo on
echo (gc CMakeLists.txt) -replace 'add_library', "add_compile_options(-openmp-)`r`nadd_library" ^| Out-File -encoding ASCII CMakeLists.txt > patch.ps1
echo (gc CMakeLists.txt) -replace ' SHARED', ' STATIC' ^| Out-File -encoding ASCII CMakeLists.txt >> patch.ps1
powershell .\patch.ps1
set CMAKE=cmake.exe -DCMAKE_VERBOSE_MAKEFILE=ON -DCMAKE_RULE_MESSAGES:BOOL=OFF
set CMAKE=%CMAKE% -DCMAKE_BUILD_TYPE=Release
%CMAKE% -G "NMake Makefiles" .
nmake -nologo -f Makefile clean
nmake -nologo -f Makefile
copy /Y /B *.h %INCLIB%
copy /Y /B *.lib %INCLIB%
shell: cmd
if: "github.event_name != 'push'"
run: "& winbuild\\build\\build_dep_libimagequant.cmd"
# for Raqm
# Raqm dependencies
- name: Build dependencies / HarfBuzz
run: |
set INCLUDE=C:\Program Files (x86)\Microsoft SDKs\Windows\V7.1A\Include
set INCLIB=%GITHUB_WORKSPACE%\winbuild\depends\msvcr10-x32
set BUILD=%GITHUB_WORKSPACE%\winbuild\build
set INCLUDE=%INCLUDE%;%INCLIB%
set LIB=%LIB%;%INCLIB%
cd /D %BUILD%\harfbuzz-2.6.4
call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" ${{ matrix.platform-vcvars }}
echo on
set CMAKE=cmake.exe -DCMAKE_VERBOSE_MAKEFILE=ON -DCMAKE_RULE_MESSAGES:BOOL=OFF
set CMAKE=%CMAKE% -DHB_HAVE_FREETYPE:BOOL=ON -DCMAKE_BUILD_TYPE=Release
%CMAKE% -G "NMake Makefiles" .
nmake -nologo -f Makefile clean
nmake -nologo -f Makefile harfbuzz
copy /Y /B src\*.h %INCLIB%
copy /Y /B *.lib %INCLIB%
shell: cmd
# for Raqm
run: "& winbuild\\build\\build_dep_harfbuzz.cmd"
- name: Build dependencies / FriBidi
run: |
set INCLUDE=C:\Program Files (x86)\Microsoft SDKs\Windows\V7.1A\Include
set INCLIB=%GITHUB_WORKSPACE%\winbuild\depends\msvcr10-x32
set BUILD=%GITHUB_WORKSPACE%\winbuild\build
cd /D %BUILD%\fribidi-1.0.9
call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" ${{ matrix.platform-vcvars }}
echo on
copy /Y /B %GITHUB_WORKSPACE%\winbuild\fribidi.cmake CMakeLists.txt
set CMAKE=cmake.exe -DCMAKE_VERBOSE_MAKEFILE=ON -DCMAKE_RULE_MESSAGES:BOOL=OFF
set CMAKE=%CMAKE% -DCMAKE_BUILD_TYPE=Release
%CMAKE% -G "NMake Makefiles" .
nmake -nologo -f Makefile clean
nmake -nologo -f Makefile fribidi
copy /Y /B lib\*.h %INCLIB%
copy /Y /B *.lib %INCLIB%
shell: cmd
run: "& winbuild\\build\\build_dep_fribidi.cmd"
- name: Build dependencies / Raqm
run: |
set INCLUDE=C:\Program Files (x86)\Microsoft SDKs\Windows\V7.1A\Include
set INCLIB=%GITHUB_WORKSPACE%\winbuild\depends\msvcr10-x32
set BUILD=%GITHUB_WORKSPACE%\winbuild\build
set INCLUDE=%INCLUDE%;%INCLIB%
set LIB=%LIB%;%INCLIB%
cd /D %BUILD%\libraqm-0.7.0
call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" ${{ matrix.platform-vcvars }}
echo on
copy /Y /B %GITHUB_WORKSPACE%\winbuild\raqm.cmake CMakeLists.txt
set CMAKE=cmake.exe -DCMAKE_VERBOSE_MAKEFILE=ON -DCMAKE_RULE_MESSAGES:BOOL=OFF
set CMAKE=%CMAKE% -DCMAKE_BUILD_TYPE=Release
%CMAKE% -G "NMake Makefiles" .
nmake -nologo -f Makefile clean
nmake -nologo -f Makefile libraqm
copy /Y /B src\*.h %INCLIB%
copy /Y /B libraqm.dll %INCLIB%
shell: cmd
run: "& winbuild\\build\\build_dep_libraqm.cmd"
- name: Build Pillow
run: |
set PYTHON=%pythonLocation%
set INCLUDE=C:\Program Files (x86)\Microsoft SDKs\Windows\V7.1A\Include
set MPLSRC=%GITHUB_WORKSPACE%
set INCLIB=%GITHUB_WORKSPACE%\winbuild\depends\msvcr10-x32
cd /D %GITHUB_WORKSPACE%
set LIB=%INCLIB%;%PYTHON%\tcl
set INCLUDE=%INCLIB%;%GITHUB_WORKSPACE%\depends\tcl86\include;%INCLUDE%
call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" ${{ matrix.platform-vcvars }}
set MSSdk=1
set DISTUTILS_USE_SDK=1
set py_vcruntime_redist=true
%PYTHON%\python.exe setup.py build_ext install
rem Add libraqm.dll (copied to INCLIB) to PATH.
path %INCLIB%;%PATH%
%PYTHON%\python.exe selftest.py --installed
shell: cmd
& winbuild\build\build_pillow.cmd install
& $env:pythonLocation\python.exe selftest.py --installed
shell: pwsh
# failing with PyPy3
- name: Enable heap verification
if: "!contains(matrix.python-version, 'pypy')"
run: |
c:\"Program Files (x86)"\"Windows Kits"\10\Debuggers\x86\gflags.exe /p /enable %PYTHON%\python.exe
shell: cmd
run: "& 'C:\\Program Files (x86)\\Windows Kits\\10\\Debuggers\\x86\\gflags.exe' /p /enable $env:pythonLocation\\python.exe"
- name: Test Pillow
run: |
set PYTHON=%pythonLocation%
set INCLIB=%GITHUB_WORKSPACE%\winbuild\depends\msvcr10-x32
rem Add libraqm.dll (copied to INCLIB) to PATH.
path %INCLIB%;%PATH%
cd /D %GITHUB_WORKSPACE%
%PYTHON%\python.exe -m pytest -vx -W always --cov PIL --cov Tests --cov-report term --cov-report xml Tests
path %GITHUB_WORKSPACE%\\winbuild\\build\\bin;%PATH%
python.exe -m pytest -vx -W always --cov PIL --cov Tests --cov-report term --cov-report xml Tests
shell: cmd
- name: Prepare to upload errors
@ -361,30 +139,21 @@ jobs:
with:
file: ./coverage.xml
flags: GHA_Windows
name: ${{ runner.os }} Python ${{ matrix.python-version }}
name: ${{ runner.os }} Python ${{ matrix.python-version }} ${{ matrix.architecture }}
- name: Build wheel
id: wheel
if: "github.event_name == 'push' && !contains(matrix.python-version, 'pypy')"
if: "github.event_name == 'push'"
run: |
for /f "tokens=3 delims=/" %%a in ("${{ github.ref }}") do echo ##[set-output name=dist;]dist-%%a
for /f "tokens=3 delims=/" %%a in ("${{ github.ref }}") do echo ::set-output name=dist::dist-%%a
set PYTHON=%pythonLocation%
set INCLUDE=C:\Program Files (x86)\Microsoft SDKs\Windows\V7.1A\Include
set MPLSRC=%GITHUB_WORKSPACE%
set INCLIB=%GITHUB_WORKSPACE%\winbuild\depends\msvcr10-x32
cd /D %GITHUB_WORKSPACE%
set LIB=%INCLIB%;%PYTHON%\tcl
set INCLUDE=%INCLIB%;%GITHUB_WORKSPACE%\depends\tcl86\include;%INCLUDE%
call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" ${{ matrix.platform-vcvars }}
%PYTHON%\python.exe setup.py bdist_wheel
winbuild\\build\\build_pillow.cmd bdist_wheel"
shell: cmd
- uses: actions/upload-artifact@v1
if: "github.event_name == 'push' && !contains(matrix.python-version, 'pypy')"
- uses: actions/upload-artifact@v2
if: "github.event_name == 'push'"
with:
name: ${{ steps.wheel.outputs.dist }}
path: dist
path: dist\*.whl
msys:
runs-on: windows-2019

View File

@ -63,6 +63,12 @@ def test_aspect():
assert im.size == (75, 23) # ratio is 3.260869565217
def test_division_by_zero():
im = Image.new("L", (200, 2))
im.thumbnail((75, 75))
assert im.size == (75, 1)
def test_float():
im = Image.new("L", (128, 128))
im.thumbnail((99.9, 99.9))

View File

@ -1,7 +1,7 @@
import pytest
from PIL import Image, ImageShow
from .helper import hopper, is_win32, on_ci, on_github_actions
from .helper import hopper, is_win32, on_ci
def test_sanity():
@ -38,8 +38,7 @@ def test_viewer_show():
@pytest.mark.skipif(
not on_ci() or (is_win32() and on_github_actions()),
reason="Only run on CIs; hangs on Windows on GitHub Actions",
not on_ci() or is_win32(), reason="Only run on CIs; hangs on Windows CIs",
)
def test_show():
for mode in ("1", "I;16", "LA", "RGB", "RGBA"):

View File

@ -292,9 +292,10 @@ or from within the uncompressed source directory::
Building on Windows
^^^^^^^^^^^^^^^^^^^
We don't recommend trying to build on Windows. It is a maze of twisty
passages, mostly dead ends. There are build scripts and notes for the
Windows build in the ``winbuild`` directory.
We recommend you use prebuilt wheels from PyPI.
If you wish to compile Pillow manually, you can use the build scripts
in the ``winbuild`` directory used for CI testing and development.
These scripts require Visual Studio 2017 or newer and NASM.
Building on FreeBSD
^^^^^^^^^^^^^^^^^^^
@ -408,9 +409,11 @@ These platforms are built and tested for every change.
+----------------------------------+--------------------------+-----------------------+
| Ubuntu Linux 20.04 LTS | 3.8 |x86-64 |
+----------------------------------+--------------------------+-----------------------+
| Windows Server 2012 R2 | 3.5, 3.8 |x86, x86-64 |
| Windows Server 2016 | 3.8 |x86 |
| +--------------------------+-----------------------+
| | PyPy3, 3.7/MinGW |x86 |
| | 3.5 |x86-64 |
| +--------------------------+-----------------------+
| | 3.7/MinGW |x86 |
+----------------------------------+--------------------------+-----------------------+
| Windows Server 2019 | 3.5, 3.6, 3.7, 3.8 |x86, x86-64 |
| +--------------------------+-----------------------+
@ -478,11 +481,13 @@ These platforms have been reported to work at the versions mentioned.
+----------------------------------+------------------------------+--------------------------------+-----------------------+
| FreeBSD 10.2 | 2.7, 3.4 | 3.1.0 |x86-64 |
+----------------------------------+------------------------------+--------------------------------+-----------------------+
| Windows 10 | 3.7 | 7.1.0 |x86-64 |
+----------------------------------+------------------------------+--------------------------------+-----------------------+
| Windows 8.1 Pro | 2.6, 2.7, 3.2, 3.3, 3.4 | 2.4.0 |x86,x86-64 |
+----------------------------------+------------------------------+--------------------------------+-----------------------+
| Windows 8 Pro | 2.6, 2.7, 3.2, 3.3, 3.4a3 | 2.2.0 |x86,x86-64 |
+----------------------------------+------------------------------+--------------------------------+-----------------------+
| Windows 7 Pro | 2.7, 3.2, 3.3 | 3.4.1 |x86-64 |
| Windows 7 Professional | 3.7 | 7.0.0 |x86,x86-64 |
+----------------------------------+------------------------------+--------------------------------+-----------------------+
| Windows Server 2008 R2 Enterprise| 3.3 | |x86-64 |
+----------------------------------+------------------------------+--------------------------------+-----------------------+

View File

@ -2277,7 +2277,9 @@ class Image:
if x / y >= aspect:
x = round_aspect(y * aspect, key=lambda n: abs(aspect - n / y))
else:
y = round_aspect(x / aspect, key=lambda n: abs(aspect - x / n))
y = round_aspect(
x / aspect, key=lambda n: 0 if n == 0 else abs(aspect - x / n)
)
size = (x, y)
box = None

View File

@ -40,6 +40,7 @@
#
import io
import itertools
import logging
import os
import struct
import warnings
@ -51,7 +52,7 @@ from . import Image, ImageFile, ImagePalette, TiffTags
from ._binary import i8, o8
from .TiffTags import TYPES
DEBUG = False # Needs to be merged with the new logging approach.
logger = logging.getLogger(__name__)
# Set these to true to force use of libtiff for reading or writing.
READ_LIBTIFF = False
@ -734,29 +735,21 @@ class ImageFileDirectory_v2(MutableMapping):
try:
for i in range(self._unpack("H", self._ensure_read(fp, 2))[0]):
tag, typ, count, data = self._unpack("HHL4s", self._ensure_read(fp, 12))
if DEBUG:
tagname = TiffTags.lookup(tag).name
typname = TYPES.get(typ, "unknown")
print(
"tag: %s (%d) - type: %s (%d)" % (tagname, tag, typname, typ),
end=" ",
)
msg = "tag: %s (%d) - type: %s (%d)" % (tagname, tag, typname, typ)
try:
unit_size, handler = self._load_dispatch[typ]
except KeyError:
if DEBUG:
print("- unsupported type", typ)
logger.debug(msg + " - unsupported type {}".format(typ))
continue # ignore unsupported type
size = count * unit_size
if size > 4:
here = fp.tell()
(offset,) = self._unpack("L", data)
if DEBUG:
print(
"Tag Location: {} - Data Location: {}".format(here, offset),
end=" ",
)
msg += " Tag Location: {} - Data Location: {}".format(here, offset)
fp.seek(offset)
data = ImageFile._safe_read(fp, size)
fp.seek(here)
@ -769,19 +762,20 @@ class ImageFileDirectory_v2(MutableMapping):
"Expecting to read %d bytes but only got %d."
" Skipping tag %s" % (size, len(data), tag)
)
logger.debug(msg)
continue
if not data:
logger.debug(msg)
continue
self._tagdata[tag] = data
self.tagtype[tag] = typ
if DEBUG:
if size > 32:
print("- value: <table: %d bytes>" % size)
else:
print("- value:", self[tag])
msg += " - value: " + (
"<table: %d bytes>" % size if size > 32 else str(data)
)
logger.debug(msg)
(self.next,) = self._unpack("L", self._ensure_read(fp, 4))
except OSError as msg:
@ -802,21 +796,17 @@ class ImageFileDirectory_v2(MutableMapping):
if tag == STRIPOFFSETS:
stripoffsets = len(entries)
typ = self.tagtype.get(tag)
if DEBUG:
print("Tag {}, Type: {}, Value: {}".format(tag, typ, value))
logger.debug("Tag {}, Type: {}, Value: {}".format(tag, typ, value))
values = value if isinstance(value, tuple) else (value,)
data = self._write_dispatch[typ](self, *values)
if DEBUG:
tagname = TiffTags.lookup(tag).name
typname = TYPES.get(typ, "unknown")
print(
"save: %s (%d) - type: %s (%d)" % (tagname, tag, typname, typ),
end=" ",
msg = "save: %s (%d) - type: %s (%d)" % (tagname, tag, typname, typ)
msg += " - value: " + (
"<table: %d bytes>" % len(data) if len(data) >= 16 else str(values)
)
if len(data) >= 16:
print("- value: <table: %d bytes>" % len(data))
else:
print("- value:", values)
logger.debug(msg)
# count is sum of lengths for string and arbitrary data
if typ in [TiffTags.BYTE, TiffTags.ASCII, TiffTags.UNDEFINED]:
@ -840,8 +830,9 @@ class ImageFileDirectory_v2(MutableMapping):
# pass 2: write entries to file
for tag, typ, count, value, data in entries:
if DEBUG:
print(tag, typ, count, repr(value), repr(data))
logger.debug(
"{} {} {} {} {}".format(tag, typ, count, repr(value), repr(data))
)
result += self._pack("HHL4s", tag, typ, count, value)
# -- overwrite here for multi-page --
@ -997,10 +988,9 @@ class TiffImageFile(ImageFile.ImageFile):
self._frame_pos = []
self._n_frames = None
if DEBUG:
print("*** TiffImageFile._open ***")
print("- __first:", self.__first)
print("- ifh: ", ifh)
logger.debug("*** TiffImageFile._open ***")
logger.debug("- __first: {}".format(self.__first))
logger.debug("- ifh: {}".format(ifh))
# and load the first frame
self._seek(0)
@ -1031,8 +1021,7 @@ class TiffImageFile(ImageFile.ImageFile):
while len(self._frame_pos) <= frame:
if not self.__next:
raise EOFError("no more images in TIFF file")
if DEBUG:
print(
logger.debug(
"Seeking to frame %s, on frame %s, __next %s, location: %s"
% (frame, self.__frame, self.__next, self.fp.tell())
)
@ -1041,8 +1030,7 @@ class TiffImageFile(ImageFile.ImageFile):
self.fp.tell()
self.fp.seek(self.__next)
self._frame_pos.append(self.__next)
if DEBUG:
print("Loading tags, location: %s" % self.fp.tell())
logger.debug("Loading tags, location: %s" % self.fp.tell())
self.tag_v2.load(self.fp)
self.__next = self.tag_v2.next
if self.__next == 0:
@ -1140,21 +1128,18 @@ class TiffImageFile(ImageFile.ImageFile):
# Rearranging for supporting byteio items, since they have a fileno
# that returns an OSError if there's no underlying fp. Easier to
# deal with here by reordering.
if DEBUG:
print("have getvalue. just sending in a string from getvalue")
logger.debug("have getvalue. just sending in a string from getvalue")
n, err = decoder.decode(self.fp.getvalue())
elif fp:
# we've got a actual file on disk, pass in the fp.
if DEBUG:
print("have fileno, calling fileno version of the decoder.")
logger.debug("have fileno, calling fileno version of the decoder.")
if not close_self_fp:
self.fp.seek(0)
# 4 bytes, otherwise the trace might error out
n, err = decoder.decode(b"fpfp")
else:
# we have something else.
if DEBUG:
print("don't have fileno or getvalue. just reading")
logger.debug("don't have fileno or getvalue. just reading")
self.fp.seek(0)
# UNDONE -- so much for that buffer size thing.
n, err = decoder.decode(self.fp.read())
@ -1194,21 +1179,19 @@ class TiffImageFile(ImageFile.ImageFile):
fillorder = self.tag_v2.get(FILLORDER, 1)
if DEBUG:
print("*** Summary ***")
print("- compression:", self._compression)
print("- photometric_interpretation:", photo)
print("- planar_configuration:", self._planar_configuration)
print("- fill_order:", fillorder)
print("- YCbCr subsampling:", self.tag.get(530))
logger.debug("*** Summary ***")
logger.debug("- compression: {}".format(self._compression))
logger.debug("- photometric_interpretation: {}".format(photo))
logger.debug("- planar_configuration: {}".format(self._planar_configuration))
logger.debug("- fill_order: {}".format(fillorder))
logger.debug("- YCbCr subsampling: {}".format(self.tag.get(530)))
# size
xsize = int(self.tag_v2.get(IMAGEWIDTH))
ysize = int(self.tag_v2.get(IMAGELENGTH))
self._size = xsize, ysize
if DEBUG:
print("- size:", self.size)
logger.debug("- size: {}".format(self.size))
sampleFormat = self.tag_v2.get(SAMPLEFORMAT, (1,))
if len(sampleFormat) > 1 and max(sampleFormat) == min(sampleFormat) == 1:
@ -1242,18 +1225,15 @@ class TiffImageFile(ImageFile.ImageFile):
bps_tuple,
extra_tuple,
)
if DEBUG:
print("format key:", key)
logger.debug("format key: {}".format(key))
try:
self.mode, rawmode = OPEN_INFO[key]
except KeyError:
if DEBUG:
print("- unsupported format")
logger.debug("- unsupported format")
raise SyntaxError("unknown pixel mode")
if DEBUG:
print("- raw mode:", rawmode)
print("- pil mode:", self.mode)
logger.debug("- raw mode: {}".format(rawmode))
logger.debug("- pil mode: {}".format(self.mode))
self.info["compression"] = self._compression
@ -1294,8 +1274,7 @@ class TiffImageFile(ImageFile.ImageFile):
if fillorder == 2:
# Replace fillorder with fillorder=1
key = key[:3] + (1,) + key[4:]
if DEBUG:
print("format key:", key)
logger.debug("format key: {}".format(key))
# this should always work, since all the
# fillorder==2 modes have a corresponding
# fillorder=1 mode
@ -1357,8 +1336,7 @@ class TiffImageFile(ImageFile.ImageFile):
x = y = 0
layer += 1
else:
if DEBUG:
print("- unsupported data organization")
logger.debug("- unsupported data organization")
raise SyntaxError("unknown data organization")
# Fix up info.
@ -1438,8 +1416,7 @@ def _save(im, fp, filename):
# write any arbitrary tags passed in as an ImageFileDirectory
info = im.encoderinfo.get("tiffinfo", {})
if DEBUG:
print("Tiffinfo Keys: %s" % list(info))
logger.debug("Tiffinfo Keys: %s" % list(info))
if isinstance(info, ImageFileDirectory_v1):
info = info.to_v2()
for key in info:
@ -1524,9 +1501,8 @@ def _save(im, fp, filename):
)
ifd[JPEGQUALITY] = quality
if DEBUG:
print("Saving using libtiff encoder")
print("Items: %s" % sorted(ifd.items()))
logger.debug("Saving using libtiff encoder")
logger.debug("Items: %s" % sorted(ifd.items()))
_fp = 0
if hasattr(fp, "fileno"):
try:
@ -1588,8 +1564,7 @@ def _save(im, fp, filename):
else:
atts[tag] = value
if DEBUG:
print("Converted items: %s" % sorted(atts.items()))
logger.debug("Converted items: %s" % sorted(atts.items()))
# libtiff always expects the bytes in native order.
# we're storing image byte order. So, if the rawmode

View File

@ -43,14 +43,12 @@ void *
ImagingError_ModeError(void)
{
return ImagingError_ValueError("bad image mode");
return NULL;
}
void *
ImagingError_Mismatch(void)
{
return ImagingError_ValueError("images don't match");
return NULL;
}
void *

View File

@ -3,16 +3,28 @@ Quick README
For more extensive info, see the [Windows build instructions](build.rst).
* See https://github.com/python-pillow/Pillow/issues/553#issuecomment-37877416 and https://github.com/matplotlib/matplotlib/issues/1717#issuecomment-13343859
* See [Current Windows Build/Testing process (Pillow#553)](https://github.com/python-pillow/Pillow/issues/553#issuecomment-37877416),
[Definitive docs for how to compile on Windows (matplotlib#1717)](https://github.com/matplotlib/matplotlib/issues/1717#issuecomment-13343859),
[Test Windows with GitHub Actions (Pillow#4084)](https://github.com/python-pillow/Pillow/pull/4084).
* Works best with Python 3.4, due to virtualenv and pip batteries included. Python3+ required for fetch command.
* Check config.py for virtual env paths, suffix for 64-bit releases. Defaults to `x64`, set `X64_EXT` to change.
* When running in CI with one Python per invocation, set the `PYTHON` env variable to the Python folder. (e.g. `PYTHON`=`c:\Python27\`) This overrides the matrix in config.py and will just build and test for the specific Python.
* `python get_pythons.py` downloads all the Python releases, and their signatures. (Manually) Install in `c:\PythonXX[x64]\`.
* `python build_dep.py` downloads and creates a build script for all the dependencies, in 32 and 64-bit versions, and with both compiler versions.
* (in powershell) `build_deps.cmd` invokes the dependency build.
* `python build.py --clean` makes Pillow for the matrix of Pythons.
* `python test.py` runs the tests on Pillow in all the virtual envs.
* Currently working with zlib, libjpeg, freetype, and libtiff on Python 2.7, and 3.4, both 32 and 64-bit, on a local win7 pro machine and appveyor.com
* WebP is built, not detected.
* LCMS, OpenJPEG and libimagequant are not building.
* Requires Microsoft Visual Studio 2017 or newer with C++ component.
* Requires NASM for libjpeg-turbo, a required dependency when using this script.
* Requires CMake 3.12 or newer (available as Visual Studio component).
* Python 3.6+ is required to generate valid scripts, but builds targeting Python 3.5+ are supported.
* Tested on Windows Server 2016 with Visual Studio 2017 Community (AppVeyor).
* Tested on Windows Server 2019 with Visual Studio 2019 Enterprise (GitHub Actions).
The following is a simplified version of the script used on AppVeyor:
```
set PYTHON=C:\Python35\bin
cd /D C:\Pillow\winbuild
C:\Python37\bin\python.exe build_prepare.py -v --depends=C:\pillow-depends
build\build_dep_all.cmd
build\build_pillow.cmd install
cd ..
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
```

View File

@ -1,3 +0,0 @@
curl -fsSL -o pypy3.zip https://bitbucket.org/pypy/pypy/downloads/pypy3.6-v7.3.1-win32.zip
7z x pypy3.zip -oc:\
c:\Python37\Scripts\virtualenv.exe -p c:\pypy3.6-v7.3.1-win32\pypy3.exe c:\vp\pypy3

View File

@ -1,205 +0,0 @@
#!/usr/bin/env python3
import getopt
import os
import shutil
import subprocess
import sys
from config import (
VIRT_BASE,
X64_EXT,
bit_from_env,
compiler_from_env,
compilers,
pythons,
pyversion_from_env,
)
def setup_vms():
ret = []
for py in pythons:
for arch in ("", X64_EXT):
ret.append(
"virtualenv -p c:/Python%s%s/python.exe --clear %s%s%s"
% (py, arch, VIRT_BASE, py, arch)
)
ret.append(
r"%s%s%s\Scripts\pip.exe install pytest pytest-cov"
% (VIRT_BASE, py, arch)
)
return "\n".join(ret)
def run_script(params):
(version, script) = params
try:
print("Running %s" % version)
filename = "build_pillow_%s.cmd" % version
with open(filename, "w") as f:
f.write(script)
command = ["powershell", "./%s" % filename]
proc = subprocess.Popen(
command,
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
)
(trace, stderr) = proc.communicate()
status = proc.returncode
print("-- stderr --")
print(stderr.decode())
print("-- stdout --")
print(trace.decode())
print("Done with {}: {}".format(version, status))
return (version, status, trace, stderr)
except Exception as msg:
print("Error with {}: {}".format(version, str(msg)))
return (version, -1, "", str(msg))
def header(op):
return r"""
setlocal
set MPLSRC=%%~dp0\..
set INCLIB=%%~dp0\depends
set BLDOPT=%s
cd /D %%MPLSRC%%
""" % (
op
)
def footer():
return """endlocal
exit
"""
def vc_setup(compiler, bit):
script = ""
if compiler["vc_version"] == "2015":
arch = "x86" if bit == 32 else "x86_amd64"
script = (
r"""
call "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat" %s
echo on"""
% arch
)
return script
def build_one(py_ver, compiler, bit):
# UNDONE virtual envs if we're not running on AppVeyor
args = {}
args.update(compiler)
if "PYTHON" in os.environ:
args["python_path"] = "%PYTHON%"
else:
args["python_path"] = "{}{}\\Scripts".format(VIRT_BASE, py_ver)
args["executable"] = "python.exe"
if "EXECUTABLE" in os.environ:
args["executable"] = "%EXECUTABLE%"
args["py_ver"] = py_ver
args["tcl_ver"] = "86"
if compiler["vc_version"] == "2015":
args["imaging_libs"] = " build_ext --add-imaging-libs=msvcrt"
else:
args["imaging_libs"] = ""
args["vc_setup"] = vc_setup(compiler, bit)
script = r"""
setlocal EnableDelayedExpansion
call "%%ProgramFiles%%\Microsoft SDKs\Windows\%(env_version)s\Bin\SetEnv.Cmd" /Release %(env_flags)s
set DISTUTILS_USE_SDK=1
set LIB=%%LIB%%;%%INCLIB%%\%(inc_dir)s
set INCLUDE=%%INCLUDE%%;%%INCLIB%%\%(inc_dir)s;%%INCLIB%%\tcl%(tcl_ver)s\include
setlocal
set LIB=%%LIB%%;C:\Python%(py_ver)s\tcl%(vc_setup)s
call %(python_path)s\%(executable)s setup.py %(imaging_libs)s %%BLDOPT%%
call %(python_path)s\%(executable)s -c "from PIL import _webp;import os, shutil;shutil.copy(r'%%INCLIB%%\freetype.dll', os.path.dirname(_webp.__file__));"
endlocal
endlocal
""" # noqa: E501
return script % args
def clean():
try:
shutil.rmtree("../build")
except Exception:
# could already be removed
pass
run_script(("virtualenvs", setup_vms()))
def main(op):
scripts = []
for py_version, py_info in pythons.items():
py_compilers = compilers[py_info["compiler"]][py_info["vc"]]
scripts.append(
(
py_version,
"\n".join(
[header(op), build_one(py_version, py_compilers[32], 32), footer()]
),
)
)
scripts.append(
(
"{}{}".format(py_version, X64_EXT),
"\n".join(
[
header(op),
build_one("%sx64" % py_version, py_compilers[64], 64),
footer(),
]
),
)
)
results = map(run_script, scripts)
for (version, status, trace, err) in results:
print("Compiled {}: {}".format(version, status and "ERR" or "OK"))
def run_one(op):
compiler = compiler_from_env()
py_version = pyversion_from_env()
bit = bit_from_env()
run_script(
(
py_version,
"\n".join([header(op), build_one(py_version, compiler, bit), footer()]),
)
)
if __name__ == "__main__":
opts, args = getopt.getopt(sys.argv[1:], "", ["clean", "wheel"])
opts = dict(opts)
if "--clean" in opts:
clean()
op = "install"
if "--wheel" in opts:
op = "bdist_wheel"
if "PYTHON" in os.environ:
run_one(op)
else:
main(op)

View File

@ -5,89 +5,112 @@ Building Pillow on Windows
<../docs/installation.rst#windows-installation>`_ should
be sufficient.
This page will describe a build setup to build Pillow against the
supported Python versions in 32 and 64-bit modes, using freely
available Microsoft compilers. This has been developed and tested
against 64-bit Windows 7 Professional and Windows Server 2012
64-bit version on Amazon EC2.
This page describes the steps necessary to build Pillow using the same
scripts used on GitHub Actions and AppVeyor CIs.
Prerequisites
-------------
Extra Build Helpers
^^^^^^^^^^^^^^^^^^^
* Powershell (available by default on Windows Server)
* GitHub client (provides git+bash shell)
Optional:
* GPG (for checking signatures) (UNDONE -- Python signature checking)
Pythons
^^^^^^^
The build routines expect Python to be installed at C:\PythonXX for
32-bit versions or C:\PythonXXx64 for the 64-bit versions.
Download Python 3.4, install it, and add it to the path. This is the
Python that we will use to bootstrap the build process. (The download
routines are using 3 features, and installing 3.4 gives us pip and
virtualenv as well, reducing the number of packages that we need to
install.)
Download the rest of the Pythons by opening a command window, changing
to the ``winbuild`` directory, and running ``python
get_pythons.py``.
UNDONE -- gpg verify the signatures (note that we can download from
https)
Run each installer and set the proper path to the installation. Don't
set any of them as the default Python, or add them to the path.
Python
^^^^^^
While the scripts can target any version of Python supported by Pillow,
Python 3.6+ is required to generate valid build scripts.
Compilers
^^^^^^^^^
Download and install:
* `Microsoft Windows SDK for Windows 7 and .NET Framework
4 <https://www.microsoft.com/en-us/download/details.aspx?id=8279>`_
* `Microsoft Visual Studio 2017 or newer or Build Tools for Visual Studio 2017 or newer
<https://visualstudio.microsoft.com/downloads/>`_
(MSVC C++ build tools, and any Windows SDK version required)
* `CMake-2.8.10.2-win32-x86.exe
<https://cmake.org/files/v2.8/cmake-2.8.10.2-win32-x86.exe>`_
* `CMake 3.12 or newer <https://cmake.org/download/>`_
(also available as Visual Studio component C++ CMake tools for Windows)
The samples and the .NET SDK portions aren't required, just the
compilers and other tools. UNDONE -- check exact wording.
* `NASM <https://www.nasm.us/pub/nasm/releasebuilds/?C=M;O=D>`_
Any version of Visual Studio 2017 or newer should be supported,
including Visual Studio 2017 Community, or Build Tools for Visual Studio 2019.
Paths to CMake (if standalone) and NASM must be added to the ``PATH`` environment variable.
Visual Studio is found automatically with ``vswhere.exe``.
Build configuration
-------------------
The following environment variables, if set, will override the default
behaviour of ``build_prepare.py``:
* ``PYTHON`` + ``EXECUTABLE`` point to the target version of Python.
If ``PYTHON`` is unset, the version of Python used to run
``build_prepare.py`` will be used. If only ``PYTHON`` is set,
``EXECUTABLE`` defaults to ``python.exe``.
* ``ARCHITECTURE`` is used to select a ``x86`` or ``x64`` build. By default,
uses same architecture as the version of Python used to run ``build_prepare.py``.
is used.
* ``PILLOW_BUILD`` can be used to override the ``winbuild\build`` directory
path, used to store generated build scripts and compiled libraries.
**Warning:** This directory is wiped when ``build_prepare.py`` is run.
* ``PILLOW_DEPS`` points to the directory used to store downloaded
dependencies. By default ``winbuild\depends`` is used.
``build_prepare.py`` also supports the following command line parameters:
* ``-v`` will print generated scripts.
* ``--no-imagequant`` will skip GPL-licensed ``libimagequant`` optional dependency
* ``--no-raqm`` will skip optional dependency Raqm (which itself depends on
LGPL-licensed ``fribidi``).
* ``--python=<path>`` and ``--executable=<exe>`` override ``PYTHON`` and ``EXECUTABLE``.
* ``--architecture=<arch>`` overrides ``ARCHITECTURE``.
* ``--dir=<path>`` and ``--depends=<path>`` override ``PILLOW_BUILD``
and ``PILLOW_DEPS``.
Dependencies
------------
The script 'build_dep.py' downloads and builds the dependencies. Open
a command window, change directory into ``winbuild`` and run ``python
build_dep.py``.
Dependencies will be automatically downloaded by ``build_prepare.py``.
By default, downloaded dependencies are stored in ``winbuild\depends``;
set the ``PILLOW_DEPS`` environment variable to override this location.
This will download libjpeg, libtiff, libz, and freetype. It will then
compile 32 and 64-bit versions of the libraries, with both versions of
the compilers.
UNDONE -- lcms fails.
UNDONE -- webp, jpeg2k not recognized
To build all dependencies, run ``winbuild\build\build_dep_all.cmd``,
or run the individual scripts to build each dependency separately.
Building Pillow
---------------
Once the dependencies are built, run ``python build.py --clean`` to
build and install Pillow in virtualenvs for each Python
build. ``build.py --wheel`` will build wheels instead of
installing into virtualenvs.
UNDONE -- suppressed output, what about failures.
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.
Testing Pillow
--------------
Build and install Pillow, then run ``python test.py`` from the
``winbuild`` directory.
Some binary dependencies (e.g. ``libraqm.dll``) will be stored in the
``winbuild\build\bin`` directory; this directory should be added to ``PATH``
before running tests.
Build and install Pillow, then run ``python -m pytest Tests``
from the root Pillow directory.
Example
-------
The following is a simplified version of the script used on AppVeyor:
.. code-block::
set PYTHON=C:\Python35\bin
cd /D C:\Pillow\winbuild
C:\Python37\bin\python.exe build_prepare.py -v --depends=C:\pillow-depends
build\build_dep_all.cmd
build\build_pillow.cmd install
cd ..
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

View File

@ -1,328 +0,0 @@
import os
from build import vc_setup
from config import all_compilers, bit_from_env, compiler_from_env, compilers, libs
from fetch import fetch
from untar import untar
from unzip import unzip
def _relpath(*args):
return os.path.join(os.getcwd(), *args)
build_dir = _relpath("build")
inc_dir = _relpath("depends")
def check_sig(filename, signame):
# UNDONE -- need gpg
return filename
def mkdirs():
try:
os.mkdir(build_dir)
except OSError:
pass
try:
os.mkdir(inc_dir)
except OSError:
pass
for compiler in all_compilers():
try:
os.mkdir(os.path.join(inc_dir, compiler["inc_dir"]))
except OSError:
pass
def extract(src, dest):
if ".zip" in src:
return unzip(src, dest)
if ".tar.gz" in src or ".tgz" in src:
return untar(src, dest)
def extract_libs():
for name, lib in libs.items():
filename = fetch(lib["url"])
if name == "openjpeg":
for compiler in all_compilers():
if not os.path.exists(
os.path.join(build_dir, lib["dir"] + compiler["inc_dir"])
):
extract(filename, build_dir)
os.rename(
os.path.join(build_dir, lib["dir"]),
os.path.join(build_dir, lib["dir"] + compiler["inc_dir"]),
)
else:
extract(filename, build_dir)
def extract_openjpeg(compiler):
return (
r"""
rem build openjpeg
setlocal
cd %%BUILD%%
mkdir %%INCLIB%%\openjpeg-2.0
copy /Y /B openjpeg-2.0.0-win32-x86\include\openjpeg-2.0 %%INCLIB%%\openjpeg-2.0
copy /Y /B openjpeg-2.0.0-win32-x86\bin\ %%INCLIB%%
copy /Y /B openjpeg-2.0.0-win32-x86\lib\ %%INCLIB%%
endlocal
"""
% compiler
)
def cp_tk(ver_85, ver_86):
versions = {"ver_85": ver_85, "ver_86": ver_86}
return (
r"""
mkdir %%INCLIB%%\tcl85\include\X11
copy /Y /B %%BUILD%%\tcl%(ver_85)s\generic\*.h %%INCLIB%%\tcl85\include\
copy /Y /B %%BUILD%%\tk%(ver_85)s\generic\*.h %%INCLIB%%\tcl85\include\
copy /Y /B %%BUILD%%\tk%(ver_85)s\xlib\X11\* %%INCLIB%%\tcl85\include\X11\
mkdir %%INCLIB%%\tcl86\include\X11
copy /Y /B %%BUILD%%\tcl%(ver_86)s\generic\*.h %%INCLIB%%\tcl86\include\
copy /Y /B %%BUILD%%\tk%(ver_86)s\generic\*.h %%INCLIB%%\tcl86\include\
copy /Y /B %%BUILD%%\tk%(ver_86)s\xlib\X11\* %%INCLIB%%\tcl86\include\X11\
"""
% versions
)
def header():
return r"""setlocal
set MSBUILD=C:\Windows\Microsoft.NET\Framework64\v4.0.30319\MSBuild.exe
set CMAKE="cmake.exe"
set INCLIB=%~dp0\depends
set BUILD=%~dp0\build
""" + "\n".join(
r"set {}=%BUILD%\{}".format(k.upper(), v["dir"])
for (k, v) in libs.items()
if v["dir"]
)
def setup_compiler(compiler):
return (
r"""setlocal EnableDelayedExpansion
call "%%ProgramFiles%%\Microsoft SDKs\Windows\%(env_version)s\Bin\SetEnv.Cmd" /Release %(env_flags)s
echo on
set INCLIB=%%INCLIB%%\%(inc_dir)s
""" # noqa: E501
% compiler
)
def end_compiler():
return """
endlocal
"""
def nmake_openjpeg(compiler, bit):
if compiler["env_version"] == "v7.0":
return ""
atts = {"op_ver": "2.3.1"}
atts.update(compiler)
return (
r"""
rem build openjpeg
setlocal
"""
+ vc_setup(compiler, bit)
+ r"""
cd /D %%OPENJPEG%%%(inc_dir)s
%%CMAKE%% -DBUILD_THIRDPARTY:BOOL=OFF -DBUILD_SHARED_LIBS:BOOL=OFF -DCMAKE_BUILD_TYPE=Release -G "NMake Makefiles" .
nmake -nologo -f Makefile clean
nmake -nologo -f Makefile
copy /Y /B bin\* %%INCLIB%%
mkdir %%INCLIB%%\openjpeg-%(op_ver)s
copy /Y /B src\lib\openjp2\*.h %%INCLIB%%\openjpeg-%(op_ver)s
endlocal
""" # noqa: E501
% atts
)
def nmake_libs(compiler, bit):
# undone -- pre, makes, headers, libs
script = (
r"""
rem Build libjpeg
setlocal
"""
+ vc_setup(compiler, bit)
+ r"""
cd /D %%JPEG%%
nmake -nologo -f makefile.vc setup-vc6
nmake -nologo -f makefile.vc clean
nmake -nologo -f makefile.vc nodebug=1 libjpeg.lib
copy /Y /B *.dll %%INCLIB%%
copy /Y /B *.lib %%INCLIB%%
copy /Y /B j*.h %%INCLIB%%
endlocal
rem Build zlib
setlocal
cd /D %%ZLIB%%
nmake -nologo -f win32\Makefile.msc clean
nmake -nologo -f win32\Makefile.msc zlib.lib
copy /Y /B *.dll %%INCLIB%%
copy /Y /B *.lib %%INCLIB%%
copy /Y /B zlib.lib %%INCLIB%%\z.lib
copy /Y /B zlib.h %%INCLIB%%
copy /Y /B zconf.h %%INCLIB%%
endlocal
rem Build webp
setlocal
"""
+ vc_setup(compiler, bit)
+ r"""
cd /D %%WEBP%%
rd /S /Q %%WEBP%%\output\release-static
nmake -nologo -f Makefile.vc CFG=release-static RTLIBCFG=static OBJDIR=output all
copy /Y /B output\release-static\%(webp_platform)s\lib\* %%INCLIB%%
mkdir %%INCLIB%%\webp
copy /Y /B src\webp\*.h %%INCLIB%%\\webp
endlocal
rem Build libtiff
setlocal
"""
+ vc_setup(compiler, bit)
+ r"""
rem do after building jpeg and zlib
copy %%~dp0\tiff.opt %%TIFF%%\nmake.opt
cd /D %%TIFF%%
nmake -nologo -f makefile.vc clean
nmake -nologo -f makefile.vc lib
copy /Y /B libtiff\*.dll %%INCLIB%%
copy /Y /B libtiff\*.lib %%INCLIB%%
copy /Y /B libtiff\tiff*.h %%INCLIB%%
endlocal
"""
)
return script % compiler
def msbuild_freetype(compiler, bit):
script = r"""
rem Build freetype
setlocal
rd /S /Q %%FREETYPE%%\objs
set DefaultPlatformToolset=v100
"""
properties = r"""/p:Configuration="Release" /p:Platform=%(platform)s"""
if bit == 64:
script += (
r"copy /Y /B "
r'"C:\Program Files (x86)\Microsoft SDKs\Windows\v7.1A\Lib\x64\*.Lib" '
r"%%FREETYPE%%\builds\windows\vc2010"
)
properties += r" /p:_IsNativeEnvironment=false"
script += (
r"""
%%MSBUILD%% %%FREETYPE%%\builds\windows\vc2010\freetype.sln /t:Clean;Build """
+ properties
+ r""" /m
xcopy /Y /E /Q %%FREETYPE%%\include %%INCLIB%%
"""
)
freetypeReleaseDir = r"%%FREETYPE%%\objs\%(platform)s\Release"
script += (
r"""
copy /Y /B """
+ freetypeReleaseDir
+ r"""\freetype.lib %%INCLIB%%\freetype.lib
copy /Y /B """
+ freetypeReleaseDir
+ r"""\freetype.dll %%INCLIB%%\..\freetype.dll
endlocal
"""
)
return script % compiler
def build_lcms2(compiler):
if compiler["env_version"] == "v7.1":
return build_lcms_71(compiler)
return build_lcms_70(compiler)
def build_lcms_70(compiler):
"""Link error here on x64"""
if compiler["platform"] == "x64":
return ""
"""Build LCMS on VC2008. This version is only 32bit/Win32"""
return (
r"""
rem Build lcms2
setlocal
set LCMS=%%LCMS-2.7%%
rd /S /Q %%LCMS%%\Lib
rd /S /Q %%LCMS%%\Projects\VC%(vc_version)s\Release
%%MSBUILD%% %%LCMS%%\Projects\VC%(vc_version)s\lcms2.sln /t:Clean /p:Configuration="Release" /p:Platform=Win32 /m
%%MSBUILD%% %%LCMS%%\Projects\VC%(vc_version)s\lcms2.sln /t:lcms2_static /p:Configuration="Release" /p:Platform=Win32 /p:PlatformToolset=v90 /m
xcopy /Y /E /Q %%LCMS%%\include %%INCLIB%%
copy /Y /B %%LCMS%%\Lib\MS\*.lib %%INCLIB%%
endlocal
""" # noqa: E501
% compiler
)
def build_lcms_71(compiler):
return (
r"""
rem Build lcms2
setlocal
set LCMS=%%LCMS-2.8%%
rd /S /Q %%LCMS%%\Lib
rd /S /Q %%LCMS%%\Projects\VC%(vc_version)s\Release
powershell -Command "(gc Projects\VC2015\lcms2_static\lcms2_static.vcxproj) -replace 'MultiThreadedDLL', 'MultiThreaded' | Out-File -encoding ASCII Projects\VC2015\lcms2_static\lcms2_static.vcxproj"
%%MSBUILD%% %%LCMS%%\Projects\VC%(vc_version)s\lcms2.sln /t:Clean /p:Configuration="Release" /p:Platform=%(platform)s /m
%%MSBUILD%% %%LCMS%%\Projects\VC%(vc_version)s\lcms2.sln /t:lcms2_static /p:Configuration="Release" /p:Platform=%(platform)s /m
xcopy /Y /E /Q %%LCMS%%\include %%INCLIB%%
copy /Y /B %%LCMS%%\Lib\MS\*.lib %%INCLIB%%
endlocal
""" # noqa: E501
% compiler
)
def add_compiler(compiler, bit):
script.append(setup_compiler(compiler))
script.append(nmake_libs(compiler, bit))
# script.append(extract_openjpeg(compiler))
script.append(msbuild_freetype(compiler, bit))
script.append(build_lcms2(compiler))
script.append(nmake_openjpeg(compiler, bit))
script.append(end_compiler())
mkdirs()
extract_libs()
script = [header(), cp_tk(libs["tk-8.5"]["version"], libs["tk-8.6"]["version"])]
if "PYTHON" in os.environ:
add_compiler(compiler_from_env(), bit_from_env())
else:
# for compiler in all_compilers():
# add_compiler(compiler)
add_compiler(compilers[7.0][2010][32], 32)
with open("build_deps.cmd", "w") as f:
f.write("\n".join(script))

557
winbuild/build_prepare.py Normal file
View File

@ -0,0 +1,557 @@
import os
import shutil
import struct
import subprocess
import sys
def cmd_cd(path):
return "cd /D {path}".format(**locals())
def cmd_set(name, value):
return "set {name}={value}".format(**locals())
def cmd_append(name, value):
op = "path " if name == "PATH" else "set {name}="
return (op + "%{name}%;{value}").format(**locals())
def cmd_copy(src, tgt):
return 'copy /Y /B "{src}" "{tgt}"'.format(**locals())
def cmd_xcopy(src, tgt):
return 'xcopy /Y /E "{src}" "{tgt}"'.format(**locals())
def cmd_mkdir(path):
return 'mkdir "{path}"'.format(**locals())
def cmd_rmdir(path):
return 'rmdir /S /Q "{path}"'.format(**locals())
def cmd_nmake(makefile=None, target="", params=None):
if params is None:
params = ""
elif isinstance(params, list) or isinstance(params, tuple):
params = " ".join(params)
else:
params = str(params)
return " ".join(
[
"{{nmake}}",
"-nologo",
'-f "{makefile}"' if makefile is not None else "",
"{params}",
'"{target}"',
]
).format(**locals())
def cmd_cmake(params=None, file="."):
if params is None:
params = ""
elif isinstance(params, list) or isinstance(params, tuple):
params = " ".join(params)
else:
params = str(params)
return " ".join(
[
"{{cmake}}",
"-DCMAKE_VERBOSE_MAKEFILE=ON",
"-DCMAKE_RULE_MESSAGES:BOOL=OFF",
"-DCMAKE_BUILD_TYPE=Release",
"{params}",
'-G "NMake Makefiles"',
'"{file}"',
]
).format(**locals())
def cmd_msbuild(
file, configuration="Release", target="Build", platform="{msbuild_arch}"
):
return " ".join(
[
"{{msbuild}}",
"{file}",
'/t:"{target}"',
'/p:Configuration="{configuration}"',
"/p:Platform={platform}",
"/m",
]
).format(**locals())
SF_MIRROR = "http://iweb.dl.sourceforge.net"
architectures = {
"x86": {"vcvars_arch": "x86", "msbuild_arch": "Win32"},
"x64": {"vcvars_arch": "x86_amd64", "msbuild_arch": "x64"},
}
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": {
"url": SF_MIRROR + "/project/libjpeg-turbo/2.0.3/libjpeg-turbo-2.0.3.tar.gz",
"filename": "libjpeg-turbo-2.0.3.tar.gz",
"dir": "libjpeg-turbo-2.0.3",
"build": [
cmd_cmake(
[
"-DENABLE_SHARED:BOOL=FALSE",
"-DWITH_JPEG8:BOOL=TRUE",
"-DWITH_CRT_DLL:BOOL=TRUE",
]
),
cmd_nmake(target="clean"),
cmd_nmake(target="jpeg-static"),
cmd_copy("jpeg-static.lib", "libjpeg.lib"),
cmd_nmake(target="cjpeg-static"),
cmd_copy("cjpeg-static.exe", "cjpeg.exe"),
cmd_nmake(target="djpeg-static"),
cmd_copy("djpeg-static.exe", "djpeg.exe"),
],
"headers": ["j*.h"],
"libs": ["libjpeg.lib"],
"bins": ["cjpeg.exe", "djpeg.exe"],
},
"zlib": {
"url": "http://zlib.net/zlib1211.zip",
"filename": "zlib1211.zip",
"dir": "zlib-1.2.11",
"build": [
cmd_nmake(r"win32\Makefile.msc", "clean"),
cmd_nmake(r"win32\Makefile.msc", "zlib.lib"),
cmd_copy("zlib.lib", "z.lib"),
],
"headers": [r"z*.h"],
"libs": [r"*.lib"],
},
"libtiff": {
"url": "https://download.osgeo.org/libtiff/tiff-4.1.0.tar.gz",
"filename": "tiff-4.1.0.tar.gz",
"dir": "tiff-4.1.0",
"build": [
cmd_copy(r"{winbuild_dir}\tiff.opt", "nmake.opt"),
cmd_nmake("makefile.vc", "clean"),
cmd_nmake("makefile.vc", "lib"),
],
"headers": [r"libtiff\tiff*.h"],
"libs": [r"libtiff\*.lib"],
# "bins": [r"libtiff\*.dll"],
},
"libwebp": {
"url": "http://downloads.webmproject.org/releases/webp/libwebp-1.1.0.tar.gz", # noqa: E501
"filename": "libwebp-1.1.0.tar.gz",
"dir": "libwebp-1.1.0",
"build": [
cmd_rmdir(r"output\release-static"), # clean
cmd_nmake(
"Makefile.vc",
"all",
["CFG=release-static", "OBJDIR=output", "ARCH={architecture}"],
),
cmd_mkdir(r"{inc_dir}\webp"),
cmd_copy(r"src\webp\*.h", r"{inc_dir}\webp"),
],
"libs": [r"output\release-static\{architecture}\lib\*.lib"],
},
"freetype": {
"url": "https://download.savannah.gnu.org/releases/freetype/freetype-2.10.2.tar.gz", # noqa: E501
"filename": "freetype-2.10.2.tar.gz",
"dir": "freetype-2.10.2",
"patch": {
r"builds\windows\vc2010\freetype.vcxproj": {
# freetype setting is /MD for .dll and /MT for .lib, we need /MD
"<RuntimeLibrary>MultiThreaded</RuntimeLibrary>": "<RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>", # noqa: E501
# freetype doesn't specify SDK version, MSBuild may guess incorrectly
'<PropertyGroup Label="Globals">': '<PropertyGroup Label="Globals">\n <WindowsTargetPlatformVersion>$(WindowsSDKVersion)</WindowsTargetPlatformVersion>', # noqa: E501
}
},
"build": [
cmd_rmdir("objs"),
cmd_msbuild(
r"builds\windows\vc2010\freetype.sln", "Release Static", "Clean"
),
cmd_msbuild(
r"builds\windows\vc2010\freetype.sln", "Release Static", "Build"
),
cmd_xcopy("include", "{inc_dir}"),
],
"libs": [r"objs\{msbuild_arch}\Release Static\freetype.lib"],
# "bins": [r"objs\{msbuild_arch}\Release\freetype.dll"],
},
"lcms2": {
"url": SF_MIRROR + "/project/lcms/lcms/2.9/lcms2-2.9.tar.gz",
"filename": "lcms2-2.9.tar.gz",
"dir": "lcms2-2.9",
"patch": {
r"Projects\VC2017\lcms2_static\lcms2_static.vcxproj": {
# default is /MD for x86 and /MT for x64, we need /MD always
"<RuntimeLibrary>MultiThreaded</RuntimeLibrary>": "<RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>", # noqa: E501
# retarget to default toolset (selected by vcvarsall.bat)
"<PlatformToolset>v141</PlatformToolset>": "<PlatformToolset>$(DefaultPlatformToolset)</PlatformToolset>", # noqa: E501
# retarget to latest (selected by vcvarsall.bat)
"<WindowsTargetPlatformVersion>8.1</WindowsTargetPlatformVersion>": "<WindowsTargetPlatformVersion>$(WindowsSDKVersion)</WindowsTargetPlatformVersion>", # noqa: E501
}
},
"build": [
cmd_rmdir("Lib"),
cmd_rmdir(r"Projects\VC2017\Release"),
cmd_msbuild(r"Projects\VC2017\lcms2.sln", "Release", "Clean"),
cmd_msbuild(r"Projects\VC2017\lcms2.sln", "Release", "lcms2_static"),
cmd_xcopy("include", "{inc_dir}"),
],
"libs": [r"Lib\MS\*.lib"],
},
"openjpeg": {
"url": "https://github.com/uclouvain/openjpeg/archive/v2.3.1.tar.gz",
"filename": "openjpeg-2.3.1.tar.gz",
"dir": "openjpeg-2.3.1",
"build": [
cmd_cmake(("-DBUILD_THIRDPARTY:BOOL=OFF", "-DBUILD_SHARED_LIBS:BOOL=OFF")),
cmd_nmake(target="clean"),
cmd_nmake(target="openjp2"),
cmd_mkdir(r"{inc_dir}\openjpeg-2.3.1"),
cmd_copy(r"src\lib\openjp2\*.h", r"{inc_dir}\openjpeg-2.3.1"),
],
"libs": [r"bin\*.lib"],
},
"libimagequant": {
# e5d454b: Merge tag '2.12.6' into msvc
"url": "https://github.com/ImageOptim/libimagequant/archive/e5d454bc7f5eb63ee50c84a83a7fa5ac94f68ec4.zip", # noqa: E501
"filename": "libimagequant-e5d454bc7f5eb63ee50c84a83a7fa5ac94f68ec4.zip",
"dir": "libimagequant-e5d454bc7f5eb63ee50c84a83a7fa5ac94f68ec4",
"patch": {
"CMakeLists.txt": {
"add_library": "add_compile_options(-openmp-)\r\nadd_library",
" SHARED": " STATIC",
}
},
"build": [
# lint: do not inline
cmd_cmake(),
cmd_nmake(target="clean"),
cmd_nmake(),
],
"headers": [r"*.h"],
"libs": [r"*.lib"],
},
"harfbuzz": {
"url": "https://github.com/harfbuzz/harfbuzz/archive/2.6.4.zip",
"filename": "harfbuzz-2.6.4.zip",
"dir": "harfbuzz-2.6.4",
"build": [
cmd_cmake("-DHB_HAVE_FREETYPE:BOOL=TRUE"),
cmd_nmake(target="clean"),
cmd_nmake(target="harfbuzz"),
],
"headers": [r"src\*.h"],
"libs": [r"*.lib"],
},
"fribidi": {
"url": "https://github.com/fribidi/fribidi/archive/v1.0.9.zip",
"filename": "fribidi-1.0.9.zip",
"dir": "fribidi-1.0.9",
"build": [
cmd_copy(r"{winbuild_dir}\fribidi.cmake", r"CMakeLists.txt"),
cmd_cmake(),
cmd_nmake(target="clean"),
cmd_nmake(target="fribidi"),
],
"headers": [r"lib\*.h"],
"libs": [r"*.lib"],
},
"libraqm": {
"url": "https://github.com/HOST-Oman/libraqm/archive/v0.7.0.zip",
"filename": "libraqm-0.7.0.zip",
"dir": "libraqm-0.7.0",
"build": [
cmd_copy(r"{winbuild_dir}\raqm.cmake", r"CMakeLists.txt"),
cmd_cmake(),
cmd_nmake(target="clean"),
cmd_nmake(target="libraqm"),
],
"headers": [r"src\*.h"],
"bins": [r"libraqm.dll"],
},
}
# based on distutils._msvccompiler from CPython 3.7.4
def find_msvs():
root = os.environ.get("ProgramFiles(x86)") or os.environ.get("ProgramFiles")
if not root:
print("Program Files not found")
return None
try:
vspath = (
subprocess.check_output(
[
os.path.join(
root, "Microsoft Visual Studio", "Installer", "vswhere.exe"
),
"-latest",
"-prerelease",
"-requires",
"Microsoft.VisualStudio.Component.VC.Tools.x86.x64",
"-property",
"installationPath",
"-products",
"*",
]
)
.decode(encoding="mbcs")
.strip()
)
except (subprocess.CalledProcessError, OSError, UnicodeDecodeError):
print("vswhere not found")
return None
if not os.path.isdir(os.path.join(vspath, "VC", "Auxiliary", "Build")):
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"] = '"{}"'.format(msbuild)
else:
# vs2019
msbuild = os.path.join(vspath, "MSBuild", "Current", "Bin", "MSBuild.exe")
if os.path.isfile(msbuild):
vs["msbuild"] = '"{}"'.format(msbuild)
else:
print("Visual Studio MSBuild not found")
return None
vcvarsall = os.path.join(vspath, "VC", "Auxiliary", "Build", "vcvarsall.bat")
if not os.path.isfile(vcvarsall):
print("Visual Studio vcvarsall not found")
return None
vs["header"].append('call "{}" {{vcvars_arch}}'.format(vcvarsall))
return vs
def extract_dep(url, filename):
import urllib.request
import tarfile
import zipfile
file = os.path.join(depends_dir, filename)
if not os.path.exists(file):
ex = None
for i in range(3):
try:
print("Fetching %s (attempt %d)..." % (url, i + 1))
content = urllib.request.urlopen(url).read()
with open(file, "wb") as f:
f.write(content)
break
except urllib.error.URLError as e:
ex = e
else:
raise RuntimeError(ex)
print("Extracting " + filename)
if filename.endswith(".zip"):
with zipfile.ZipFile(file) as zf:
zf.extractall(build_dir)
elif filename.endswith(".tar.gz") or filename.endswith(".tgz"):
with tarfile.open(file, "r:gz") as tgz:
tgz.extractall(build_dir)
else:
raise RuntimeError("Unknown archive type: " + filename)
def write_script(name, lines):
name = os.path.join(build_dir, name)
lines = [line.format(**prefs) for line in lines]
print("Writing " + name)
with open(name, "w") as f:
f.write("\n\r".join(lines))
if verbose:
for line in lines:
print(" " + line)
def get_footer(dep):
lines = []
for out in dep.get("headers", []):
lines.append(cmd_copy(out, "{inc_dir}"))
for out in dep.get("libs", []):
lines.append(cmd_copy(out, "{lib_dir}"))
for out in dep.get("bins", []):
lines.append(cmd_copy(out, "{bin_dir}"))
return lines
def build_dep(name):
dep = deps[name]
dir = dep["dir"]
file = "build_dep_{name}.cmd".format(**locals())
extract_dep(dep["url"], dep["filename"])
for patch_file, patch_list in dep.get("patch", {}).items():
patch_file = os.path.join(build_dir, dir, patch_file.format(**prefs))
with open(patch_file, "r") as f:
text = f.read()
for patch_from, patch_to in patch_list.items():
text = text.replace(patch_from.format(**prefs), patch_to.format(**prefs))
with open(patch_file, "w") as f:
f.write(text)
banner = "Building {name} ({dir})".format(**locals())
lines = [
"@echo " + ("=" * 70),
"@echo ==== {:<60} ====".format(banner),
"@echo " + ("=" * 70),
"cd /D %s" % os.path.join(build_dir, dir),
*prefs["header"],
*dep.get("build", []),
*get_footer(dep),
]
write_script(file, lines)
return file
def build_dep_all():
lines = ["@echo on"]
for dep_name in deps:
if dep_name in disabled:
continue
lines.append(r'cmd.exe /c "{{build_dir}}\{}"'.format(build_dep(dep_name)))
lines.append("if errorlevel 1 echo Build failed! && exit /B 1")
lines.append("@echo All Pillow dependencies built successfully!")
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("MSSdk", "1"), # for Python 3.5 and PyPy3.6
cmd_set("py_vcruntime_redist", "true"), # use /MD, not /MT
r'"{python_dir}\{python_exe}" setup.py build_ext %*',
]
write_script("build_pillow.cmd", lines)
if __name__ == "__main__":
# winbuild directory
winbuild_dir = os.path.dirname(os.path.realpath(__file__))
verbose = False
disabled = []
depends_dir = os.environ.get("PILLOW_DEPS", os.path.join(winbuild_dir, "depends"))
python_dir = os.environ.get("PYTHON")
python_exe = os.environ.get("EXECUTABLE", "python.exe")
architecture = os.environ.get(
"ARCHITECTURE", "x86" if struct.calcsize("P") == 4 else "x64"
)
build_dir = os.environ.get("PILLOW_BUILD", os.path.join(winbuild_dir, "build"))
for arg in sys.argv[1:]:
if arg == "-v":
verbose = True
elif arg == "--no-imagequant":
disabled += ["libimagequant"]
elif arg == "--no-raqm":
disabled += ["harfbuzz", "fribidi", "libraqm"]
elif arg.startswith("--depends="):
depends_dir = arg[10:]
elif arg.startswith("--python="):
python_dir = arg[9:]
elif arg.startswith("--executable="):
python_exe = arg[13:]
elif arg.startswith("--architecture="):
architecture = arg[15:]
elif arg.startswith("--dir="):
build_dir = arg[6:]
else:
raise ValueError("Unknown parameter: " + arg)
# dependency cache directory
os.makedirs(depends_dir, exist_ok=True)
print("Caching dependencies in:", depends_dir)
if python_dir is None:
python_dir = os.path.dirname(os.path.realpath(sys.executable))
python_exe = os.path.basename(sys.executable)
print("Target Python:", os.path.join(python_dir, python_exe))
arch_prefs = architectures[architecture]
print("Target Architecture:", architecture)
msvs = find_msvs()
if msvs is None:
raise RuntimeError(
"Visual Studio not found. Please install Visual Studio 2017 or newer."
)
print("Found Visual Studio at:", msvs["vs_dir"])
print("Using output directory:", build_dir)
# build directory for *.h files
inc_dir = os.path.join(build_dir, "inc")
# build directory for *.lib files
lib_dir = os.path.join(build_dir, "lib")
# build directory for *.bin files
bin_dir = os.path.join(build_dir, "bin")
shutil.rmtree(build_dir, ignore_errors=True)
for path in [build_dir, inc_dir, lib_dir, bin_dir]:
os.makedirs(path)
prefs = {
# Python paths / preferences
"python_dir": python_dir,
"python_exe": python_exe,
"architecture": architecture,
**arch_prefs,
# Pillow paths
"pillow_dir": os.path.realpath(os.path.join(winbuild_dir, "..")),
"winbuild_dir": winbuild_dir,
# Build paths
"build_dir": build_dir,
"inc_dir": inc_dir,
"lib_dir": lib_dir,
"bin_dir": bin_dir,
# Compilers / Tools
**msvs,
"cmake": "cmake.exe", # TODO find CMAKE automatically
# TODO find NASM automatically
# script header
"header": sum([header, msvs["header"], ["@echo on"]], []),
}
print()
build_dep_all()
build_pillow()

View File

@ -1,199 +0,0 @@
import os
SF_MIRROR = "https://iweb.dl.sourceforge.net"
pythons = {
"pypy3": {"compiler": 7.1, "vc": 2015},
# for AppVeyor
"35": {"compiler": 7.1, "vc": 2015},
"36": {"compiler": 7.1, "vc": 2015},
"37": {"compiler": 7.1, "vc": 2015},
"38": {"compiler": 7.1, "vc": 2015},
# for GitHub Actions
"3.5": {"compiler": 7.1, "vc": 2015},
"3.6": {"compiler": 7.1, "vc": 2015},
"3.7": {"compiler": 7.1, "vc": 2015},
"3.8": {"compiler": 7.1, "vc": 2015},
}
VIRT_BASE = "c:/vp/"
X64_EXT = os.environ.get("X64_EXT", "x64")
libs = {
# 'openjpeg': {
# 'filename': 'openjpeg-2.0.0-win32-x86.zip',
# 'version': '2.0'
# },
"zlib": {
"url": "http://zlib.net/zlib1211.zip",
"filename": "zlib1211.zip",
"dir": "zlib-1.2.11",
},
"jpeg": {
"url": "http://www.ijg.org/files/jpegsr9d.zip",
"filename": "jpegsr9d.zip",
"dir": "jpeg-9d",
},
"tiff": {
"url": "ftp://download.osgeo.org/libtiff/tiff-4.1.0.tar.gz",
"filename": "tiff-4.1.0.tar.gz",
"dir": "tiff-4.1.0",
},
"freetype": {
"url": "https://download.savannah.gnu.org/releases/freetype/freetype-2.10.2.tar.gz", # noqa: E501
"filename": "freetype-2.10.2.tar.gz",
"dir": "freetype-2.10.2",
},
"lcms-2.7": {
"url": SF_MIRROR + "/project/lcms/lcms/2.7/lcms2-2.7.zip",
"filename": "lcms2-2.7.zip",
"dir": "lcms2-2.7",
},
"lcms-2.8": {
"url": SF_MIRROR + "/project/lcms/lcms/2.8/lcms2-2.8.zip",
"filename": "lcms2-2.8.zip",
"dir": "lcms2-2.8",
},
"tcl-8.5": {
"url": SF_MIRROR + "/project/tcl/Tcl/8.5.19/tcl8519-src.zip",
"filename": "tcl8519-src.zip",
"dir": "",
},
"tk-8.5": {
"url": SF_MIRROR + "/project/tcl/Tcl/8.5.19/tk8519-src.zip",
"filename": "tk8519-src.zip",
"dir": "",
"version": "8.5.19",
},
"tcl-8.6": {
"url": SF_MIRROR + "/project/tcl/Tcl/8.6.10/tcl8610-src.zip",
"filename": "tcl8610-src.zip",
"dir": "",
},
"tk-8.6": {
"url": SF_MIRROR + "/project/tcl/Tcl/8.6.10/tk8610-src.zip",
"filename": "tk8610-src.zip",
"dir": "",
"version": "8.6.10",
},
"webp": {
"url": "http://downloads.webmproject.org/releases/webp/libwebp-1.1.0.tar.gz",
"filename": "libwebp-1.1.0.tar.gz",
"dir": "libwebp-1.1.0",
},
"openjpeg": {
"url": "https://github.com/uclouvain/openjpeg/archive/v2.3.1.tar.gz",
"filename": "openjpeg-2.3.1.tar.gz",
"dir": "openjpeg-2.3.1",
},
"jpeg-turbo": {
"url": SF_MIRROR + "/project/libjpeg-turbo/2.0.3/libjpeg-turbo-2.0.3.tar.gz",
"filename": "libjpeg-turbo-2.0.3.tar.gz",
"dir": "libjpeg-turbo-2.0.3",
},
# e5d454b: Merge tag '2.12.6' into msvc
"imagequant": {
"url": "https://github.com/ImageOptim/libimagequant/archive/e5d454bc7f5eb63ee50c84a83a7fa5ac94f68ec4.zip", # noqa: E501
"filename": "libimagequant-e5d454bc7f5eb63ee50c84a83a7fa5ac94f68ec4.zip",
"dir": "libimagequant-e5d454bc7f5eb63ee50c84a83a7fa5ac94f68ec4",
},
"harfbuzz": {
"url": "https://github.com/harfbuzz/harfbuzz/archive/2.6.4.zip",
"filename": "harfbuzz-2.6.4.zip",
"dir": "harfbuzz-2.6.4",
},
"fribidi": {
"url": "https://github.com/fribidi/fribidi/archive/v1.0.9.zip",
"filename": "fribidi-1.0.9.zip",
"dir": "fribidi-1.0.9",
},
"libraqm": {
"url": "https://github.com/HOST-Oman/libraqm/archive/v0.7.0.zip",
"filename": "libraqm-0.7.0.zip",
"dir": "libraqm-0.7.0",
},
}
compilers = {
7: {
2010: {
64: {
"env_version": "v7.0",
"vc_version": "2010",
"env_flags": "/x64 /xp",
"inc_dir": "msvcr90-x64",
"platform": "x64",
"webp_platform": "x64",
},
32: {
"env_version": "v7.0",
"vc_version": "2010",
"env_flags": "/x86 /xp",
"inc_dir": "msvcr90-x32",
"platform": "Win32",
"webp_platform": "x86",
},
}
},
7.1: {
2015: {
64: {
"env_version": "v7.1",
"vc_version": "2015",
"env_flags": "/x64 /vista",
"inc_dir": "msvcr10-x64",
"platform": "x64",
"webp_platform": "x64",
},
32: {
"env_version": "v7.1",
"vc_version": "2015",
"env_flags": "/x86 /vista",
"inc_dir": "msvcr10-x32",
"platform": "Win32",
"webp_platform": "x86",
},
}
},
}
def pyversion_from_env():
py = os.environ["PYTHON"]
py_version = "35"
for k in pythons:
if k in py:
py_version = k
break
if "64" in py:
py_version = "{}{}".format(py_version, X64_EXT)
return py_version
def compiler_from_env():
py = os.environ["PYTHON"]
for k, v in pythons.items():
if k in py:
py_info = v
break
bit = bit_from_env()
return compilers[py_info["compiler"]][py_info["vc"]][bit]
def bit_from_env():
py = os.environ["PYTHON"]
return 64 if "64" in py else 32
def all_compilers():
all = []
for vc_compilers in compilers.values():
for bit_compilers in vc_compilers.values():
all += bit_compilers.values()
return all

View File

@ -1,44 +0,0 @@
import os
import sys
import urllib.parse
import urllib.request
from config import libs
def fetch(url):
depends_filename = None
for lib in libs.values():
if lib["url"] == url:
depends_filename = lib["filename"]
break
if depends_filename and os.path.exists(depends_filename):
return depends_filename
name = urllib.parse.urlsplit(url)[2].split("/")[-1]
if not os.path.exists(name):
def retrieve(request_url):
print("Fetching", request_url)
try:
return urllib.request.urlopen(request_url)
except urllib.error.URLError:
return urllib.request.urlopen(request_url)
try:
r = retrieve(url)
except urllib.error.HTTPError:
if depends_filename:
r = retrieve(
"https://github.com/python-pillow/pillow-depends/raw/master/"
+ depends_filename
)
name = depends_filename
content = r.read()
with open(name, "wb") as fd:
fd.write(content)
return name
if __name__ == "__main__":
fetch(sys.argv[1])

View File

@ -1,4 +1,4 @@
cmake_minimum_required(VERSION 3.13)
cmake_minimum_required(VERSION 3.12)
project(fribidi)

View File

@ -1,15 +0,0 @@
import os
from fetch import fetch
if __name__ == "__main__":
for version in ["3.4.4"]:
for platform in ["", ".amd64"]:
for extension in ["", ".asc"]:
fetch(
"https://www.python.org/ftp/python/%s/python-%s%s.msi%s"
% (version, version, platform, extension)
)
# find pip, if it's not in the path!
os.system("pip install virtualenv")

View File

@ -1,9 +0,0 @@
Get-ChildItem .\Projects\VC2015\ *.vcxproj -recurse |
Foreach-Object {
$c = ($_ | Get-Content)
$c = $c -replace 'MultiThreaded<','MultiThreadedDLL<'
$c = $c -replace '<WindowsTargetPlatformVersion>8.1</WindowsTargetPlatformVersion>','<WindowsTargetPlatformVersion>10</WindowsTargetPlatformVersion>'
$c = $c -replace '<PlatformToolset>v140</PlatformToolset>','<PlatformToolset>v142</PlatformToolset>'
[IO.File]::WriteAllText($_.FullName, ($c -join "`r`n"))
}

View File

@ -1,4 +1,4 @@
cmake_minimum_required(VERSION 3.13)
cmake_minimum_required(VERSION 3.12)
project(libraqm)

View File

@ -1,45 +0,0 @@
#!/usr/bin/env python3
import glob
import os
import subprocess
import sys
from config import VIRT_BASE, X64_EXT, pythons
def test_one(params):
python, architecture = params
try:
print("Running: %s, %s" % params)
command = [
r"{}\{}{}\Scripts\python.exe".format(VIRT_BASE, python, architecture),
"test-installed.py",
"--processes=-0",
"--process-timeout=30",
]
command.extend(glob.glob("Tests/test*.py"))
proc = subprocess.Popen(command, stdin=subprocess.PIPE, stdout=subprocess.PIPE)
(trace, stderr) = proc.communicate()
status = proc.returncode
print("Done with {}, {} -- {}".format(python, architecture, status))
return (python, architecture, status, trace)
except Exception as msg:
print("Error with {}, {}: {}".format(python, architecture, msg))
return (python, architecture, -1, str(msg))
if __name__ == "__main__":
os.chdir("..")
matrix = [
(python, architecture) for python in pythons for architecture in ("", X64_EXT)
]
results = map(test_one, matrix)
for (python, architecture, status, trace) in results:
print("{}{}: {}".format(python, architecture, status and "ERR" or "PASS"))
res = all(status for (python, architecture, status, trace) in results)
sys.exit(res)

View File

@ -1,11 +0,0 @@
import sys
import tarfile
def untar(src, dest):
with tarfile.open(src, "r:gz") as tgz:
tgz.extractall(dest)
if __name__ == "__main__":
untar(sys.argv[1], sys.argv[2])

View File

@ -1,11 +0,0 @@
import sys
import zipfile
def unzip(src, dest):
with zipfile.ZipFile(src) as zf:
zf.extractall(dest)
if __name__ == "__main__":
unzip(sys.argv[1], sys.argv[2])