Skip to content

Commit 5046879

Browse files
committed
windows: support building CPython 3.8
The remaining work to build 3.8 was mostly related to building libffi. This is definitely one of the wonkier dependencies to build from source, especially since the source release published by Python lacks a commit necessary to make the build not error. I wasted a few hours tracking this down. *sigh*. After a few false starts, including reimplementing aspects of the `prepare_libffi.bat` script, I gave up and just used it verbatim. We may come to regret this shortcut to support static builds, which don't yet work for non-ffi reasons. But at least `--profile shared` works! libffi requiring cygwin is a bit annoying. But what are you going to do? Once libffi was being built, we encountered a packaging error due to a new test extension module. So that change snuck in as well.
1 parent 682c5f2 commit 5046879

File tree

2 files changed

+91
-6
lines changed

2 files changed

+91
-6
lines changed

cpython-windows/build.py

Lines changed: 78 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,7 @@ def find_vswhere():
150150
return vswhere
151151

152152

153-
def find_msbuild():
153+
def find_vs_path(path):
154154
vswhere = find_vswhere()
155155

156156
p = subprocess.check_output(
@@ -169,7 +169,7 @@ def find_msbuild():
169169
# Strictly speaking the output may not be UTF-8.
170170
p = pathlib.Path(p.strip().decode("utf-8"))
171171

172-
p = p / "MSBuild" / "Current" / "Bin" / "MSBuild.exe"
172+
p = p / path
173173

174174
if not p.exists():
175175
print("%s does not exist" % p)
@@ -178,6 +178,15 @@ def find_msbuild():
178178
return p
179179

180180

181+
def find_msbuild():
182+
return find_vs_path(pathlib.Path("MSBuild") / "Current" / "Bin" / "MSBuild.exe")
183+
184+
185+
def find_vcvarsall_path():
186+
"""Find path to vcvarsall.bat"""
187+
return find_vs_path(pathlib.Path("VC") / "Auxiliary" / "Build" / "vcvarsall.bat")
188+
189+
181190
def find_vctools_path():
182191
vswhere = find_vswhere()
183192

@@ -1071,6 +1080,9 @@ def run_msbuild(
10711080
"/property:OverrideVersion=%s" % python_version,
10721081
]
10731082

1083+
if not python_version.startswith("3.7"):
1084+
args.append("/property:IncludeCTypes=true")
1085+
10741086
exec_and_log(args, str(pcbuild_path), os.environ)
10751087

10761088

@@ -1194,6 +1206,50 @@ def build_openssl(perl_path: pathlib.Path, arch: str, profile: str):
11941206
create_tar_from_directory(fh, install)
11951207

11961208

1209+
def build_libffi(
1210+
build_dir: pathlib.Path, arch: str, prepare_ffi: pathlib.Path, sh_exe: pathlib.Path
1211+
):
1212+
ffi_source_path = build_dir / "libffi"
1213+
1214+
# As of April 15, 2020, the libffi source release on GitHub doesn't
1215+
# have patches that we need to build. https://bugs.python.org/issue40293
1216+
# tracks getting a proper release. Until then, git clone the repo.
1217+
subprocess.run(
1218+
[
1219+
"git.exe",
1220+
"clone",
1221+
"--single-branch",
1222+
"--branch",
1223+
"libffi",
1224+
"https://github.com/python/cpython-source-deps.git",
1225+
str(ffi_source_path),
1226+
],
1227+
check=True,
1228+
)
1229+
1230+
subprocess.run(
1231+
["git.exe", "checkout", "ed22026f39b37f892ded95d7b30e77dfb5126334"],
1232+
cwd=ffi_source_path,
1233+
check=True,
1234+
)
1235+
1236+
# We build libffi by running the build script that CPython ships.
1237+
env = dict(os.environ)
1238+
env["LIBFFI_SOURCE"] = str(ffi_source_path)
1239+
env["VCVARSALL"] = str(find_vcvarsall_path())
1240+
env["SH"] = str(sh_exe)
1241+
1242+
args = [str(prepare_ffi), "-pdb"]
1243+
if arch == "x86":
1244+
args.append("-x86")
1245+
else:
1246+
args.append("-x64")
1247+
1248+
# Running the build script from Python will install the files into the
1249+
# appropriate directory.
1250+
subprocess.run(args, env=env, check=True)
1251+
1252+
11971253
RE_ADDITIONAL_DEPENDENCIES = re.compile(
11981254
"<AdditionalDependencies>([^<]+)</AdditionalDependencies>"
11991255
)
@@ -1249,6 +1305,7 @@ def collect_python_build_artifacts(
12491305
"_testconsole",
12501306
"_testembed",
12511307
"_testimportmultiple",
1308+
"_testinternalcapi",
12521309
"_testmultiphase",
12531310
"xxlimited",
12541311
}
@@ -1481,7 +1538,7 @@ def find_additional_dependencies(project: pathlib.Path):
14811538
return res
14821539

14831540

1484-
def build_cpython(python_entry_name: str, arch: str, profile):
1541+
def build_cpython(python_entry_name: str, arch: str, sh_exe, profile):
14851542
static = profile == "static"
14861543
pgo = "-pgo" in profile
14871544

@@ -1545,6 +1602,16 @@ def build_cpython(python_entry_name: str, arch: str, profile):
15451602
with zipfile.ZipFile(setuptools_archive) as zf:
15461603
zf.extractall(td)
15471604

1605+
cpython_source_path = td / ("Python-%s" % python_version)
1606+
pcbuild_path = cpython_source_path / "PCBuild"
1607+
1608+
prepare_libffi = pcbuild_path / "prepare_libffi.bat"
1609+
1610+
if prepare_libffi.exists():
1611+
assert sh_exe
1612+
1613+
build_libffi(td, arch, prepare_libffi, sh_exe)
1614+
15481615
# We need all the OpenSSL library files in the same directory to appease
15491616
# install rules.
15501617
if not static:
@@ -1879,9 +1946,14 @@ def main():
18791946
default="static",
18801947
help="How to compile Python",
18811948
)
1949+
parser.add_argument("--sh", help="Path to sh.exe in a cygwin or mingw installation")
18821950

18831951
args = parser.parse_args()
18841952

1953+
if args.python == "cpython-3.8" and not args.sh:
1954+
print("--sh required when building Python 3.8+")
1955+
return 1
1956+
18851957
now = datetime.datetime.utcnow()
18861958

18871959
log_path = BUILD / "build.log"
@@ -1899,7 +1971,9 @@ def main():
18991971
build_openssl(perl_path, arch, profile=args.profile)
19001972

19011973
LOG_PREFIX[0] = "cpython"
1902-
tar_path = build_cpython(args.python, arch, profile=args.profile)
1974+
tar_path = build_cpython(
1975+
args.python, arch, sh_exe=pathlib.Path(args.sh), profile=args.profile
1976+
)
19031977

19041978
compress_python_archive(
19051979
tar_path, DIST, "%s-%s" % (tar_path.stem, now.strftime("%Y%m%dT%H%M")),

docs/building.rst

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,16 +52,27 @@ Windows
5252
Visual Studio 2017 (or later) is required. A compatible Windows SDK is required
5353
(10.0.17763.0 as per CPython 3.7.2).
5454

55+
If building CPython 3.8+, there are the following additional requirements:
56+
57+
* A ``git.exe`` on ``PATH`` (to clone ``libffi`` from source).
58+
* An installation of Cywgin with the ``autoconf``, ``automake``, ``libtool``,
59+
and ``make`` packages installed. (``libffi`` build dependency.)
60+
5561
To build a Python distribution for Windows x64::
5662

57-
# From a Visual Studio 2017 x64 native tools command prompt:
58-
$ py.exe build-windows.py
63+
# From a Visual Studio 2017/2019 x64 native tools command prompt:
64+
$ py.exe build-windows.py --profile static
5965

6066
It is also possible to build a more traditional dynamically linked
6167
distribution, optionally with PGO optimizations::
6268

6369
$ py.exe build-windows.py --profile shared
6470
$ py.exe build-windows.py --profile shared-pgo
6571

72+
If building CPython 3.8+, you will need to specify the path to a
73+
``sh.exe`` installed from cygwin. e.g.
74+
75+
$ py.exe build-windows.py --python cpython-3.8 --sh c:\cygwin\bin\sh.exe --profile shared
76+
6677
To build a 32-bit x86 binary, simply use an ``x86 Native Tools
6778
Command Prompt`` instead of ``x64``.

0 commit comments

Comments
 (0)