Pillow/winbuild/build_prepare.py

703 lines
25 KiB
Python
Raw Normal View History

import os
import platform
import re
import shutil
2020-02-25 02:00:57 +03:00
import struct
import subprocess
2019-12-31 16:45:11 +03:00
import sys
2020-01-01 03:48:13 +03:00
def cmd_cd(path):
2020-07-09 22:08:15 +03:00
return f"cd /D {path}"
2020-01-01 03:48:13 +03:00
def cmd_set(name, value):
2020-07-09 22:08:15 +03:00
return f"set {name}={value}"
2020-01-01 03:48:13 +03:00
def cmd_append(name, value):
2020-07-09 22:08:15 +03:00
op = "path " if name == "PATH" else f"set {name}="
return op + f"%{name}%;{value}"
2020-01-01 03:48:13 +03:00
def cmd_copy(src, tgt):
2020-07-09 22:08:15 +03:00
return f'copy /Y /B "{src}" "{tgt}"'
2020-01-01 03:48:13 +03:00
def cmd_xcopy(src, tgt):
2020-07-09 22:08:15 +03:00
return f'xcopy /Y /E "{src}" "{tgt}"'
2020-01-01 03:48:13 +03:00
def cmd_mkdir(path):
2020-07-09 22:08:15 +03:00
return f'mkdir "{path}"'
2020-01-01 03:48:13 +03:00
def cmd_rmdir(path):
2020-07-09 22:08:15 +03:00
return f'rmdir /S /Q "{path}"'
2020-01-01 03:48:13 +03:00
def cmd_nmake(makefile=None, target="", params=None):
if params is None:
params = ""
elif isinstance(params, (list, tuple)):
2020-01-01 03:48:13 +03:00
params = " ".join(params)
else:
params = str(params)
return " ".join(
[
2020-07-09 22:08:15 +03:00
"{nmake}",
2020-01-01 03:48:13 +03:00
"-nologo",
2020-07-09 22:08:15 +03:00
f'-f "{makefile}"' if makefile is not None else "",
f"{params}",
f'"{target}"',
2020-01-01 03:48:13 +03:00
]
2020-07-09 22:08:15 +03:00
)
2020-01-01 03:48:13 +03:00
def cmd_cmake(params=None, file="."):
if params is None:
params = ""
elif isinstance(params, (list, tuple)):
2020-01-01 03:48:13 +03:00
params = " ".join(params)
else:
params = str(params)
return " ".join(
[
2020-07-09 22:08:15 +03:00
"{cmake}",
2020-01-01 03:48:13 +03:00
"-DCMAKE_VERBOSE_MAKEFILE=ON",
"-DCMAKE_RULE_MESSAGES:BOOL=OFF",
"-DCMAKE_BUILD_TYPE=Release",
2020-07-09 22:08:15 +03:00
f"{params}",
2020-01-01 03:48:13 +03:00
'-G "NMake Makefiles"',
2020-07-09 22:08:15 +03:00
f'"{file}"',
2020-01-01 03:48:13 +03:00
]
2020-07-09 22:08:15 +03:00
)
2020-01-01 03:48:13 +03:00
def cmd_msbuild(
file, configuration="Release", target="Build", platform="{msbuild_arch}"
):
return " ".join(
[
2020-07-09 22:08:15 +03:00
"{msbuild}",
f"{file}",
f'/t:"{target}"',
f'/p:Configuration="{configuration}"',
f"/p:Platform={platform}",
2020-01-01 03:48:13 +03:00
"/m",
]
2020-07-09 22:08:15 +03:00
)
2020-01-01 03:48:13 +03:00
SF_PROJECTS = "https://sourceforge.net/projects"
architectures = {
"x86": {"vcvars_arch": "x86", "msbuild_arch": "Win32"},
"x64": {"vcvars_arch": "x86_amd64", "msbuild_arch": "x64"},
"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}"),
]
2020-04-12 11:18:49 +03:00
# dependencies, listed in order of compilation
deps = {
2019-12-31 20:13:16 +03:00
"libjpeg": {
"url": SF_PROJECTS
2022-08-13 09:36:46 +03:00
+ "/libjpeg-turbo/files/2.1.4/libjpeg-turbo-2.1.4.tar.gz/download",
"filename": "libjpeg-turbo-2.1.4.tar.gz",
"dir": "libjpeg-turbo-2.1.4",
"license": ["README.ijg", "LICENSE.md"],
"license_pattern": (
"(LEGAL ISSUES\n============\n\n.+?)\n\nREFERENCES\n=========="
".+(libjpeg-turbo Licenses\n======================\n\n.+)$"
),
"build": [
2019-12-31 20:13:16 +03:00
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"],
},
2019-12-31 20:13:16 +03:00
"zlib": {
2022-10-13 12:30:11 +03:00
"url": "https://zlib.net/zlib1213.zip",
"filename": "zlib1213.zip",
"dir": "zlib-1.2.13",
"license": "README",
"license_pattern": "Copyright notice:\n\n(.+)$",
"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"],
},
"xz": {
2023-01-12 00:41:14 +03:00
"url": SF_PROJECTS + "/lzmautils/files/xz-5.4.1.tar.gz/download",
"filename": "xz-5.4.1.tar.gz",
"dir": "xz-5.4.1",
"license": "COPYING",
"patch": {
r"src\liblzma\api\lzma.h": {
"#ifndef LZMA_API_IMPORT": "#ifndef LZMA_API_IMPORT\n#define LZMA_API_STATIC", # noqa: E501
},
r"windows\vs2019\liblzma.vcxproj": {
# retarget to default toolset (selected by vcvarsall.bat)
"<PlatformToolset>v142</PlatformToolset>": "<PlatformToolset>$(DefaultPlatformToolset)</PlatformToolset>", # noqa: E501
# retarget to latest (selected by vcvarsall.bat)
"<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>": "<WindowsTargetPlatformVersion>$(WindowsSDKVersion)</WindowsTargetPlatformVersion>", # noqa: E501
},
},
"build": [
cmd_msbuild(r"windows\vs2019\liblzma.vcxproj", "Release", "Clean"),
cmd_msbuild(r"windows\vs2019\liblzma.vcxproj", "Release", "Build"),
cmd_mkdir(r"{inc_dir}\lzma"),
cmd_copy(r"src\liblzma\api\lzma\*.h", r"{inc_dir}\lzma"),
],
"headers": [r"src\liblzma\api\lzma.h"],
"libs": [r"windows\vs2019\Release\{msbuild_arch}\liblzma\liblzma.lib"],
},
2019-12-31 20:13:16 +03:00
"libwebp": {
2022-08-06 08:40:10 +03:00
"url": "http://downloads.webmproject.org/releases/webp/libwebp-1.2.4.tar.gz",
"filename": "libwebp-1.2.4.tar.gz",
"dir": "libwebp-1.2.4",
2022-09-05 13:48:42 +03:00
"license": "COPYING",
"build": [
cmd_rmdir(r"output\release-static"), # clean
cmd_nmake(
"Makefile.vc",
"all",
2022-09-05 13:48:42 +03:00
[
"CFG=release-static",
"RTLIBCFG=dynamic",
2022-09-05 13:48:42 +03:00
"OBJDIR=output",
"ARCH={architecture}",
"LIBWEBP_BASENAME=webp",
],
),
cmd_mkdir(r"{inc_dir}\webp"),
cmd_copy(r"src\webp\*.h", r"{inc_dir}\webp"),
],
"libs": [r"output\release-static\{architecture}\lib\*.lib"],
},
2019-12-31 20:13:16 +03:00
"libtiff": {
2022-12-24 06:19:32 +03:00
"url": "https://download.osgeo.org/libtiff/tiff-4.5.0.tar.gz",
"filename": "tiff-4.5.0.tar.gz",
"dir": "tiff-4.5.0",
"license": "LICENSE.md",
"patch": {
r"libtiff\tif_lzma.c": {
# link against liblzma.lib
"#ifdef LZMA_SUPPORT": '#ifdef LZMA_SUPPORT\n#pragma comment(lib, "liblzma.lib")', # noqa: E501
},
2022-09-05 13:48:42 +03:00
r"libtiff\tif_webp.c": {
# link against webp.lib
"#ifdef WEBP_SUPPORT": '#ifdef WEBP_SUPPORT\n#pragma comment(lib, "webp.lib")', # noqa: E501
},
},
"build": [
cmd_cmake("-DBUILD_SHARED_LIBS:BOOL=OFF"),
cmd_nmake(target="clean"),
cmd_nmake(target="tiff"),
],
"headers": [r"libtiff\tiff*.h"],
"libs": [r"libtiff\*.lib"],
# "bins": [r"libtiff\*.dll"],
},
"libpng": {
2022-11-21 07:42:44 +03:00
"url": SF_PROJECTS + "/libpng/files/libpng16/1.6.39/lpng1639.zip/download",
"filename": "lpng1639.zip",
"dir": "lpng1639",
"license": "LICENSE",
"build": [
# lint: do not inline
cmd_cmake(("-DPNG_SHARED:BOOL=OFF", "-DPNG_TESTS:BOOL=OFF")),
cmd_nmake(target="clean"),
cmd_nmake(),
cmd_copy("libpng16_static.lib", "libpng16.lib"),
],
"headers": [r"png*.h"],
"libs": [r"libpng16.lib"],
},
2022-09-07 20:59:55 +03:00
"brotli": {
"url": "https://github.com/google/brotli/archive/refs/tags/v1.0.9.tar.gz",
"filename": "brotli-1.0.9.tar.gz",
"dir": "brotli-1.0.9",
"license": "LICENSE",
"build": [
cmd_cmake(),
cmd_nmake(target="clean"),
cmd_nmake(target="brotlicommon-static"),
cmd_nmake(target="brotlidec-static"),
cmd_xcopy(r"c\include", "{inc_dir}"),
],
"libs": ["*.lib"],
},
2019-12-31 20:13:16 +03:00
"freetype": {
2022-05-01 14:57:59 +03:00
"url": "https://download.savannah.gnu.org/releases/freetype/freetype-2.12.1.tar.gz", # noqa: E501
"filename": "freetype-2.12.1.tar.gz",
"dir": "freetype-2.12.1",
"license": ["LICENSE.TXT", r"docs\FTL.TXT", r"docs\GPLv2.TXT"],
2019-12-31 19:34:11 +03:00
"patch": {
r"builds\windows\vc2010\freetype.vcxproj": {
# freetype setting is /MD for .dll and /MT for .lib, we need /MD
2020-04-12 11:18:49 +03:00
"<RuntimeLibrary>MultiThreaded</RuntimeLibrary>": "<RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>", # noqa: E501
2020-02-25 02:00:57 +03:00
# freetype doesn't specify SDK version, MSBuild may guess incorrectly
2020-04-12 11:18:49 +03:00
'<PropertyGroup Label="Globals">': '<PropertyGroup Label="Globals">\n <WindowsTargetPlatformVersion>$(WindowsSDKVersion)</WindowsTargetPlatformVersion>', # noqa: E501
},
r"builds\windows\vc2010\freetype.user.props": {
2022-09-07 20:59:55 +03:00
"<UserDefines></UserDefines>": "<UserDefines>FT_CONFIG_OPTION_SYSTEM_ZLIB;FT_CONFIG_OPTION_USE_PNG;FT_CONFIG_OPTION_USE_HARFBUZZ;FT_CONFIG_OPTION_USE_BROTLI</UserDefines>", # noqa: E501
"<UserIncludeDirectories></UserIncludeDirectories>": r"<UserIncludeDirectories>{dir_harfbuzz}\src;{inc_dir}</UserIncludeDirectories>", # noqa: E501
"<UserLibraryDirectories></UserLibraryDirectories>": "<UserLibraryDirectories>{lib_dir}</UserLibraryDirectories>", # noqa: E501
2022-09-07 20:59:55 +03:00
"<UserDependencies></UserDependencies>": "<UserDependencies>zlib.lib;libpng16.lib;brotlicommon-static.lib;brotlidec-static.lib</UserDependencies>", # noqa: E501
},
r"src/autofit/afshaper.c": {
# link against harfbuzz.lib
"#ifdef FT_CONFIG_OPTION_USE_HARFBUZZ": '#ifdef FT_CONFIG_OPTION_USE_HARFBUZZ\n#pragma comment(lib, "harfbuzz.lib")', # noqa: E501
},
2019-12-31 19:34:11 +03:00
},
"build": [
cmd_rmdir("objs"),
2019-12-31 20:13:16 +03:00
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"],
},
2019-12-31 20:13:16 +03:00
"lcms2": {
2022-12-29 17:16:49 +03:00
"url": SF_PROJECTS + "/lcms/files/lcms/2.14/lcms2-2.14.tar.gz/download",
2022-11-01 16:08:29 +03:00
"filename": "lcms2-2.14.tar.gz",
"dir": "lcms2-2.14",
"license": "COPYING",
2019-12-31 19:34:11 +03:00
"patch": {
2022-08-24 08:39:43 +03:00
r"Projects\VC2022\lcms2_static\lcms2_static.vcxproj": {
2019-12-31 20:13:16 +03:00
# default is /MD for x86 and /MT for x64, we need /MD always
2020-04-12 11:18:49 +03:00
"<RuntimeLibrary>MultiThreaded</RuntimeLibrary>": "<RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>", # noqa: E501
2019-12-31 20:13:16 +03:00
# retarget to default toolset (selected by vcvarsall.bat)
2022-08-24 08:39:43 +03:00
"<PlatformToolset>v143</PlatformToolset>": "<PlatformToolset>$(DefaultPlatformToolset)</PlatformToolset>", # noqa: E501
2019-12-31 20:13:16 +03:00
# retarget to latest (selected by vcvarsall.bat)
"<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>": "<WindowsTargetPlatformVersion>$(WindowsSDKVersion)</WindowsTargetPlatformVersion>", # noqa: E501
2019-12-31 20:13:16 +03:00
}
2019-12-31 19:34:11 +03:00
},
"build": [
cmd_rmdir("Lib"),
2022-08-24 08:39:43 +03:00
cmd_rmdir(r"Projects\VC2022\Release"),
cmd_msbuild(r"Projects\VC2022\lcms2.sln", "Release", "Clean"),
2021-06-04 18:53:20 +03:00
cmd_msbuild(
2022-08-24 08:39:43 +03:00
r"Projects\VC2022\lcms2.sln", "Release", "lcms2_static:Rebuild"
2021-06-04 18:53:20 +03:00
),
cmd_xcopy("include", "{inc_dir}"),
],
"libs": [r"Lib\MS\*.lib"],
},
2019-12-31 20:13:16 +03:00
"openjpeg": {
2022-05-14 03:11:42 +03:00
"url": "https://github.com/uclouvain/openjpeg/archive/v2.5.0.tar.gz",
"filename": "openjpeg-2.5.0.tar.gz",
"dir": "openjpeg-2.5.0",
"license": "LICENSE",
"patch": {
r"src\lib\openjp2\ht_dec.c": {
"#ifdef OPJ_COMPILER_MSVC\n return (OPJ_UINT32)__popcnt(val);": "#if defined(OPJ_COMPILER_MSVC) && (defined(_M_IX86) || defined(_M_AMD64))\n return (OPJ_UINT32)__popcnt(val);", # noqa: E501
}
},
"build": [
cmd_cmake(("-DBUILD_CODEC:BOOL=OFF", "-DBUILD_SHARED_LIBS:BOOL=OFF")),
cmd_nmake(target="clean"),
2020-01-01 03:48:13 +03:00
cmd_nmake(target="openjp2"),
2022-05-14 03:11:42 +03:00
cmd_mkdir(r"{inc_dir}\openjpeg-2.5.0"),
cmd_copy(r"src\lib\openjp2\*.h", r"{inc_dir}\openjpeg-2.5.0"),
],
"libs": [r"bin\*.lib"],
},
2019-12-31 20:13:16 +03:00
"libimagequant": {
2021-12-30 07:42:30 +03:00
# commit: Merge branch 'master' into msvc (matches 2.17.0 tag)
"url": "https://github.com/ImageOptim/libimagequant/archive/e4c1334be0eff290af5e2b4155057c2953a313ab.zip", # noqa: E501
"filename": "libimagequant-e4c1334be0eff290af5e2b4155057c2953a313ab.zip",
"dir": "libimagequant-e4c1334be0eff290af5e2b4155057c2953a313ab",
"license": "COPYRIGHT",
2019-12-31 20:13:16 +03:00
"patch": {
"CMakeLists.txt": {
2021-09-21 05:49:35 +03:00
"if(OPENMP_FOUND)": "if(false)",
"install": "#install",
2019-12-31 20:13:16 +03:00
}
},
"build": [
# lint: do not inline
cmd_cmake(),
cmd_nmake(target="clean"),
2021-09-21 05:49:35 +03:00
cmd_nmake(target="imagequant_a"),
cmd_copy("imagequant_a.lib", "imagequant.lib"),
2019-12-31 20:13:16 +03:00
],
"headers": [r"*.h"],
2021-09-21 05:49:35 +03:00
"libs": [r"imagequant.lib"],
2019-12-31 20:13:16 +03:00
},
"harfbuzz": {
2022-12-18 23:04:05 +03:00
"url": "https://github.com/harfbuzz/harfbuzz/archive/6.0.0.zip",
"filename": "harfbuzz-6.0.0.zip",
"dir": "harfbuzz-6.0.0",
"license": "COPYING",
2019-12-31 20:13:16 +03:00
"build": [
cmd_set("CXXFLAGS", "-d2FH4-"),
2019-12-31 20:13:16 +03:00
cmd_cmake("-DHB_HAVE_FREETYPE:BOOL=TRUE"),
cmd_nmake(target="clean"),
cmd_nmake(target="harfbuzz"),
],
"headers": [r"src\*.h"],
"libs": [r"*.lib"],
},
"fribidi": {
2022-04-20 01:37:45 +03:00
"url": "https://github.com/fribidi/fribidi/archive/v1.0.12.zip",
"filename": "fribidi-1.0.12.zip",
"dir": "fribidi-1.0.12",
"license": "COPYING",
2019-12-31 20:13:16 +03:00
"build": [
2022-08-24 23:04:43 +03:00
cmd_copy(r"COPYING", r"{bin_dir}\fribidi-1.0.12-COPYING"),
2020-01-01 15:02:27 +03:00
cmd_copy(r"{winbuild_dir}\fribidi.cmake", r"CMakeLists.txt"),
2019-12-31 20:13:16 +03:00
cmd_cmake(),
cmd_nmake(target="clean"),
cmd_nmake(target="fribidi"),
],
2020-11-25 14:21:42 +03:00
"bins": [r"*.dll"],
2019-12-31 20:13:16 +03:00
},
}
# based on distutils._msvccompiler from CPython 3.7.4
2019-12-31 22:57:17 +03:00
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:
2019-12-31 20:13:16 +03:00
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",
2019-12-31 16:45:11 +03:00
"vs_dir": vspath,
}
2019-12-31 03:10:22 +03:00
# vs2017
msbuild = os.path.join(vspath, "MSBuild", "15.0", "Bin", "MSBuild.exe")
if os.path.isfile(msbuild):
2020-07-09 22:08:15 +03:00
vs["msbuild"] = f'"{msbuild}"'
else:
2019-12-31 03:10:22 +03:00
# vs2019
msbuild = os.path.join(vspath, "MSBuild", "Current", "Bin", "MSBuild.exe")
if os.path.isfile(msbuild):
2020-07-09 22:08:15 +03:00
vs["msbuild"] = f'"{msbuild}"'
2019-12-31 03:10:22 +03:00
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
2020-07-09 22:08:15 +03:00
vs["header"].append(f'call "{vcvarsall}" {{vcvars_arch}}')
return vs
def extract_dep(url, filename):
import tarfile
2020-09-01 20:16:46 +03:00
import urllib.request
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)
2020-01-01 03:48:13 +03:00
print("Extracting " + filename)
2022-10-31 02:45:49 +03:00
sources_dir_abs = os.path.abspath(sources_dir)
if filename.endswith(".zip"):
with zipfile.ZipFile(file) as zf:
2022-10-31 02:45:49 +03:00
for member in zf.namelist():
member_abspath = os.path.abspath(os.path.join(sources_dir, member))
member_prefix = os.path.commonpath([sources_dir_abs, member_abspath])
2022-10-31 02:45:49 +03:00
if sources_dir_abs != member_prefix:
msg = "Attempted Path Traversal in Zip File"
raise RuntimeError(msg)
2020-09-01 20:16:46 +03:00
zf.extractall(sources_dir)
elif filename.endswith(".tar.gz") or filename.endswith(".tgz"):
with tarfile.open(file, "r:gz") as tgz:
for member in tgz.getnames():
member_abspath = os.path.abspath(os.path.join(sources_dir, member))
member_prefix = os.path.commonpath([sources_dir_abs, member_abspath])
2022-10-31 02:45:49 +03:00
if sources_dir_abs != member_prefix:
msg = "Attempted Path Traversal in Tar File"
raise RuntimeError(msg)
2020-09-01 20:16:46 +03:00
tgz.extractall(sources_dir)
else:
msg = "Unknown archive type: " + filename
raise RuntimeError(msg)
def write_script(name, lines):
2020-01-01 03:48:13 +03:00
name = os.path.join(build_dir, name)
lines = [line.format(**prefs) for line in lines]
print("Writing " + name)
with open(name, "w", newline="") as f:
f.write(os.linesep.join(lines))
2020-02-25 02:00:57 +03:00
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]
2019-12-31 20:13:16 +03:00
dir = dep["dir"]
2020-07-09 22:08:15 +03:00
file = f"build_dep_{name}.cmd"
extract_dep(dep["url"], dep["filename"])
licenses = dep["license"]
if isinstance(licenses, str):
licenses = [licenses]
license_text = ""
for license_file in licenses:
with open(os.path.join(sources_dir, dir, license_file)) as f:
license_text += f.read()
if "license_pattern" in dep:
match = re.search(dep["license_pattern"], license_text, re.DOTALL)
license_text = "\n".join(match.groups())
assert len(license_text) > 50
with open(os.path.join(license_dir, f"{dir}.txt"), "w") as f:
print(f"Writing license {dir}.txt")
f.write(license_text)
2019-12-31 19:34:11 +03:00
for patch_file, patch_list in dep.get("patch", {}).items():
2020-09-01 20:16:46 +03:00
patch_file = os.path.join(sources_dir, dir, patch_file.format(**prefs))
with open(patch_file) as f:
2019-12-31 19:34:11 +03:00
text = f.read()
for patch_from, patch_to in patch_list.items():
2020-09-01 20:16:46 +03:00
patch_from = patch_from.format(**prefs)
patch_to = patch_to.format(**prefs)
assert patch_from in text
text = text.replace(patch_from, patch_to)
2019-12-31 19:34:11 +03:00
with open(patch_file, "w") as f:
2021-09-21 05:49:35 +03:00
print(f"Patching {patch_file}")
2019-12-31 19:34:11 +03:00
f.write(text)
2020-07-09 22:08:15 +03:00
banner = f"Building {name} ({dir})"
2019-12-31 03:10:22 +03:00
lines = [
2020-01-01 03:48:13 +03:00
"@echo " + ("=" * 70),
2020-07-09 22:08:15 +03:00
f"@echo ==== {banner:<60} ====",
2020-01-01 03:48:13 +03:00
"@echo " + ("=" * 70),
2020-09-01 20:16:46 +03:00
"cd /D %s" % os.path.join(sources_dir, dir),
2019-12-31 03:10:22 +03:00
*prefs["header"],
*dep.get("build", []),
*get_footer(dep),
]
write_script(file, lines)
return file
def build_dep_all():
2020-01-01 03:48:13 +03:00
lines = ["@echo on"]
2019-12-31 20:13:16 +03:00
for dep_name in deps:
2020-02-25 02:00:57 +03:00
if dep_name in disabled:
continue
2020-07-09 22:08:15 +03:00
script = build_dep(dep_name)
2022-03-04 08:42:24 +03:00
lines.append(rf'cmd.exe /c "{{build_dir}}\{script}"')
2020-01-01 03:48:13 +03:00
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)
2020-01-01 15:02:27 +03:00
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
2022-09-05 15:58:41 +03:00
cmd_set("py_vcruntime_redist", "true"), # always use /MD, never /MT
2021-01-02 15:08:38 +03:00
r'"{python_dir}\{python_exe}" setup.py build_ext --vendor-raqm --vendor-fribidi %*', # noqa: E501
2020-01-01 15:02:27 +03:00
]
2019-12-31 16:45:11 +03:00
write_script("build_pillow.cmd", lines)
if __name__ == "__main__":
# winbuild directory
winbuild_dir = os.path.dirname(os.path.realpath(__file__))
2020-02-25 02:00:57 +03:00
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",
"ARM64"
if platform.machine() == "ARM64"
else ("x86" if struct.calcsize("P") == 4 else "x64"),
)
build_dir = os.environ.get("PILLOW_BUILD", os.path.join(winbuild_dir, "build"))
2020-09-01 20:16:46 +03:00
sources_dir = ""
2020-02-25 02:00:57 +03:00
for arg in sys.argv[1:]:
if arg == "-v":
verbose = True
elif arg == "--no-imagequant":
disabled += ["libimagequant"]
2020-11-25 14:21:42 +03:00
elif arg == "--no-raqm" or arg == "--no-fribidi":
disabled += ["fribidi"]
2020-02-25 02:00:57 +03:00
elif arg.startswith("--depends="):
depends_dir = arg[10:]
2020-02-25 02:00:57 +03:00
elif arg.startswith("--python="):
python_dir = arg[9:]
2020-02-25 02:00:57 +03:00
elif arg.startswith("--executable="):
python_exe = arg[13:]
2020-02-25 02:00:57 +03:00
elif arg.startswith("--architecture="):
architecture = arg[15:]
2020-02-25 02:00:57 +03:00
elif arg.startswith("--dir="):
build_dir = arg[6:]
2020-09-01 20:16:46 +03:00
elif arg == "--srcdir":
sources_dir = os.path.sep + "src"
2020-02-25 02:00:57 +03:00
else:
msg = "Unknown parameter: " + arg
raise ValueError(msg)
2020-02-25 02:00:57 +03:00
2020-01-01 03:48:13 +03:00
# dependency cache directory
2020-01-01 15:02:27 +03:00
os.makedirs(depends_dir, exist_ok=True)
2020-01-01 03:48:13 +03:00
print("Caching dependencies in:", depends_dir)
2020-01-01 15:02:27 +03:00
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]
2020-01-01 03:48:13 +03:00
print("Target Architecture:", architecture)
2019-12-31 16:45:11 +03:00
2019-12-31 22:57:17 +03:00
msvs = find_msvs()
if msvs is None:
msg = "Visual Studio not found. Please install Visual Studio 2017 or newer."
raise RuntimeError(msg)
2020-01-01 03:48:13 +03:00
print("Found Visual Studio at:", msvs["vs_dir"])
2020-01-01 03:48:13 +03:00
print("Using output directory:", build_dir)
2020-01-01 03:48:13 +03:00
# build directory for *.h files
inc_dir = os.path.join(build_dir, "inc")
2020-01-01 03:48:13 +03:00
# 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")
2020-09-01 20:16:46 +03:00
# directory for storing project files
sources_dir = build_dir + sources_dir
# copy dependency licenses to this directory
license_dir = os.path.join(build_dir, "license")
2019-12-31 22:57:17 +03:00
shutil.rmtree(build_dir, ignore_errors=True)
2020-09-01 20:16:46 +03:00
os.makedirs(build_dir, exist_ok=False)
for path in [inc_dir, lib_dir, bin_dir, sources_dir, license_dir]:
2020-09-01 20:16:46 +03:00
os.makedirs(path, exist_ok=True)
prefs = {
2020-01-01 15:02:27 +03:00
# Python paths / preferences
"python_dir": python_dir,
2020-01-01 15:02:27 +03:00
"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,
2020-01-01 15:02:27 +03:00
"lib_dir": lib_dir,
"bin_dir": bin_dir,
2020-09-01 20:16:46 +03:00
"src_dir": sources_dir,
"license_dir": license_dir,
2020-01-01 15:02:27 +03:00
# Compilers / Tools
**msvs,
"cmake": "cmake.exe", # TODO find CMAKE automatically
# TODO find NASM automatically
# script header
"header": sum([header, msvs["header"], ["@echo on"]], []),
}
for k, v in deps.items():
prefs[f"dir_{k}"] = os.path.join(sources_dir, v["dir"])
2020-02-25 02:00:57 +03:00
print()
2020-09-01 20:16:46 +03:00
write_script(".gitignore", ["*"])
build_dep_all()
build_pillow()