Skip to content

Commit b474315

Browse files
committed
windows: build libffi separately
Building libffi takes a while and iterating on Python's build while having to wait on libffi is annoying. This commit refactors the libffi building so it occurs as its own step in the build, similarly to how OpenSSL is built. As part of this, we need to hack up python.props to reference the new location of the libffi assets.
1 parent 6f78aa9 commit b474315

File tree

1 file changed

+81
-51
lines changed

1 file changed

+81
-51
lines changed

cpython-windows/build.py

Lines changed: 81 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -636,6 +636,7 @@ def hack_props(td: pathlib.Path, pcbuild_path: pathlib.Path, arch: str, static:
636636

637637
sqlite_path = td / ("sqlite-autoconf-%s" % sqlite_version)
638638
bzip2_path = td / ("bzip2-%s" % bzip2_version)
639+
libffi_path = td / "libffi"
639640
tcltk_path = td / ("cpython-bin-deps-%s" % tcltk_commit)
640641
xz_path = td / ("xz-%s" % xz_version)
641642
zlib_path = td / ("zlib-%s" % zlib_version)
@@ -654,6 +655,9 @@ def hack_props(td: pathlib.Path, pcbuild_path: pathlib.Path, arch: str, static:
654655
if b"<bz2Dir>" in line:
655656
line = b"<bz2Dir>%s\\</bz2Dir>" % bzip2_path
656657

658+
elif b"<libffiOutDir>" in line:
659+
line = b"<libffiOutDir>%s\\</libffiOutDir>" % libffi_path
660+
657661
elif b"<lzmaDir>" in line:
658662
line = b"<lzmaDir>%s\\</lzmaDir>" % xz_path
659663

@@ -1233,47 +1237,76 @@ def build_openssl(perl_path: pathlib.Path, arch: str, profile: str):
12331237

12341238

12351239
def build_libffi(
1236-
build_dir: pathlib.Path, arch: str, prepare_ffi: pathlib.Path, sh_exe: pathlib.Path
1240+
python: str, arch: str, sh_exe: pathlib.Path, dest_archive: pathlib.Path
12371241
):
1238-
ffi_source_path = build_dir / "libffi"
1242+
with tempfile.TemporaryDirectory(prefix="libffi-build-") as td:
1243+
td = pathlib.Path(td)
12391244

1240-
# As of April 15, 2020, the libffi source release on GitHub doesn't
1241-
# have patches that we need to build. https://bugs.python.org/issue40293
1242-
# tracks getting a proper release. Until then, git clone the repo.
1243-
subprocess.run(
1244-
[
1245-
"git.exe",
1246-
"clone",
1247-
"--single-branch",
1248-
"--branch",
1249-
"libffi",
1250-
"https://github.com/python/cpython-source-deps.git",
1251-
str(ffi_source_path),
1252-
],
1253-
check=True,
1254-
)
1245+
ffi_source_path = td / "libffi"
12551246

1256-
subprocess.run(
1257-
["git.exe", "checkout", "ed22026f39b37f892ded95d7b30e77dfb5126334"],
1258-
cwd=ffi_source_path,
1259-
check=True,
1260-
)
1247+
# As of April 15, 2020, the libffi source release on GitHub doesn't
1248+
# have patches that we need to build. https://bugs.python.org/issue40293
1249+
# tracks getting a proper release. Until then, git clone the repo.
1250+
subprocess.run(
1251+
[
1252+
"git.exe",
1253+
"clone",
1254+
"--single-branch",
1255+
"--branch",
1256+
"libffi",
1257+
"https://github.com/python/cpython-source-deps.git",
1258+
str(ffi_source_path),
1259+
],
1260+
check=True,
1261+
)
12611262

1262-
# We build libffi by running the build script that CPython ships.
1263-
env = dict(os.environ)
1264-
env["LIBFFI_SOURCE"] = str(ffi_source_path)
1265-
env["VCVARSALL"] = str(find_vcvarsall_path())
1266-
env["SH"] = str(sh_exe)
1263+
subprocess.run(
1264+
["git.exe", "checkout", "ed22026f39b37f892ded95d7b30e77dfb5126334"],
1265+
cwd=ffi_source_path,
1266+
check=True,
1267+
)
12671268

1268-
args = [str(prepare_ffi), "-pdb"]
1269-
if arch == "x86":
1270-
args.append("-x86")
1271-
else:
1272-
args.append("-x64")
1269+
# We build libffi by running the build script that CPython ships.
1270+
python_archive = download_entry(python, BUILD)
1271+
extract_tar_to_directory(python_archive, td)
12731272

1274-
# Running the build script from Python will install the files into the
1275-
# appropriate directory.
1276-
subprocess.run(args, env=env, check=True)
1273+
python_entry = DOWNLOADS[python]
1274+
prepare_libffi = (
1275+
td
1276+
/ ("Python-%s" % python_entry["version"])
1277+
/ "PCBuild"
1278+
/ "prepare_libffi.bat"
1279+
)
1280+
1281+
env = dict(os.environ)
1282+
env["LIBFFI_SOURCE"] = str(ffi_source_path)
1283+
env["VCVARSALL"] = str(find_vcvarsall_path())
1284+
env["SH"] = str(sh_exe)
1285+
1286+
args = [str(prepare_libffi), "-pdb"]
1287+
if arch == "x86":
1288+
args.append("-x86")
1289+
artifacts_path = ffi_source_path / "i686-pc-cygwin"
1290+
else:
1291+
args.append("-x64")
1292+
artifacts_path = ffi_source_path / "x86_64-w64-cygwin"
1293+
1294+
subprocess.run(args, env=env, check=True)
1295+
1296+
out_dir = td / "out" / "libffi"
1297+
out_dir.mkdir(parents=True)
1298+
1299+
for f in os.listdir(artifacts_path / ".libs"):
1300+
if f.endswith((".lib", ".exp", ".dll", ".pdb")):
1301+
shutil.copyfile(artifacts_path / ".libs" / f, out_dir / f)
1302+
1303+
shutil.copytree(artifacts_path / "include", out_dir / "include")
1304+
shutil.copyfile(
1305+
artifacts_path / "fficonfig.h", out_dir / "include" / "fficonfig.h"
1306+
)
1307+
1308+
with dest_archive.open("wb") as fh:
1309+
create_tar_from_directory(fh, td / "out")
12771310

12781311

12791312
RE_ADDITIONAL_DEPENDENCIES = re.compile(
@@ -1564,7 +1597,7 @@ def find_additional_dependencies(project: pathlib.Path):
15641597
return res
15651598

15661599

1567-
def build_cpython(python_entry_name: str, arch: str, sh_exe, profile):
1600+
def build_cpython(python_entry_name: str, arch: str, profile, libffi_archive=None):
15681601
static = profile == "static"
15691602
pgo = "-pgo" in profile
15701603

@@ -1607,7 +1640,7 @@ def build_cpython(python_entry_name: str, arch: str, sh_exe, profile):
16071640
with tempfile.TemporaryDirectory(prefix="python-build-") as td:
16081641
td = pathlib.Path(td)
16091642

1610-
with concurrent.futures.ThreadPoolExecutor(8) as e:
1643+
with concurrent.futures.ThreadPoolExecutor(9) as e:
16111644
fs = []
16121645
for a in (
16131646
python_archive,
@@ -1625,19 +1658,12 @@ def build_cpython(python_entry_name: str, arch: str, sh_exe, profile):
16251658
for f in fs:
16261659
f.result()
16271660

1661+
if libffi_archive:
1662+
extract_tar_to_directory(libffi_archive, td)
1663+
16281664
with zipfile.ZipFile(setuptools_archive) as zf:
16291665
zf.extractall(td)
16301666

1631-
cpython_source_path = td / ("Python-%s" % python_version)
1632-
pcbuild_path = cpython_source_path / "PCBuild"
1633-
1634-
prepare_libffi = pcbuild_path / "prepare_libffi.bat"
1635-
1636-
if prepare_libffi.exists():
1637-
assert sh_exe
1638-
1639-
build_libffi(td, arch, prepare_libffi, sh_exe)
1640-
16411667
# We need all the OpenSSL library files in the same directory to appease
16421668
# install rules.
16431669
if not static:
@@ -2002,12 +2028,16 @@ def main():
20022028
LOG_PREFIX[0] = "openssl"
20032029
build_openssl(perl_path, arch, profile=args.profile)
20042030

2031+
if "3.7" not in args.python:
2032+
libffi_archive = BUILD / ("libffi-windows-%s-%s.tar" % (arch, args.profile))
2033+
if not libffi_archive.exists():
2034+
build_libffi(args.python, arch, pathlib.Path(args.sh), libffi_archive)
2035+
else:
2036+
libffi_archive = None
2037+
20052038
LOG_PREFIX[0] = "cpython"
20062039
tar_path = build_cpython(
2007-
args.python,
2008-
arch,
2009-
sh_exe=pathlib.Path(args.sh) if args.sh else None,
2010-
profile=args.profile,
2040+
args.python, arch, profile=args.profile, libffi_archive=libffi_archive
20112041
)
20122042

20132043
compress_python_archive(

0 commit comments

Comments
 (0)