@@ -370,7 +370,7 @@ def hack_props(
370370
371371 mpdecimal_version = DOWNLOADS ["mpdecimal" ]["version" ]
372372
373- if meets_python_minimum_version (python_version , "3.14" ):
373+ if meets_python_minimum_version (python_version , "3.14" ) or arch == "arm64" :
374374 tcltk_commit = DOWNLOADS ["tk-windows-bin" ]["git_commit" ]
375375 else :
376376 tcltk_commit = DOWNLOADS ["tk-windows-bin-8612" ]["git_commit" ]
@@ -464,6 +464,8 @@ def hack_props(
464464 suffix = b"-x64"
465465 elif arch == "win32" :
466466 suffix = b""
467+ elif arch == "arm64" :
468+ suffix = b""
467469 else :
468470 raise Exception ("unhandled architecture: %s" % arch )
469471
@@ -505,6 +507,7 @@ def hack_project_files(
505507 build_directory : str ,
506508 python_version : str ,
507509 zlib_entry : str ,
510+ arch : str ,
508511):
509512 """Hacks Visual Studio project files to work with our build."""
510513
@@ -518,6 +521,17 @@ def hack_project_files(
518521 zlib_entry ,
519522 )
520523
524+ # `--include-tcltk` is forced off on arm64, undo that
525+ # See https://github.com/python/cpython/pull/132650
526+ try :
527+ static_replace_in_file (
528+ cpython_source_path / "PC" / "layout" / "main.py" ,
529+ rb'if ns.arch in ("arm32", "arm64"):' ,
530+ rb'if ns.arch == "arm32":' ,
531+ )
532+ except NoSearchStringError :
533+ pass
534+
521535 # Our SQLite directory is named weirdly. This throws off version detection
522536 # in the project file. Replace the parsing logic with a static string.
523537 sqlite3_version = DOWNLOADS ["sqlite" ]["actual_version" ].encode ("ascii" )
@@ -603,14 +617,18 @@ def hack_project_files(
603617 # have a standalone zlib DLL, so we remove references to it. For Python
604618 # 3.14+, we're using tk-windows-bin 8.6.14 which includes a prebuilt zlib
605619 # DLL, so we skip this patch there.
606- if meets_python_minimum_version (
607- python_version , "3.12"
608- ) and meets_python_maximum_version (python_version , "3.13" ):
609- static_replace_in_file (
610- pcbuild_path / "_tkinter.vcxproj" ,
611- rb'<_TclTkDLL Include="$(tcltkdir)\bin\$(tclZlibDllName)" />' ,
612- rb"" ,
613- )
620+ # On arm64, we use the new version of tk-windows-bin for all versions.
621+ if meets_python_minimum_version (python_version , "3.12" ) and (
622+ meets_python_maximum_version (python_version , "3.13" ) or arch == "arm64"
623+ ):
624+ try :
625+ static_replace_in_file (
626+ pcbuild_path / "_tkinter.vcxproj" ,
627+ rb'<_TclTkDLL Include="$(tcltkdir)\bin\$(tclZlibDllName)" />' ,
628+ rb"" ,
629+ )
630+ except NoSearchStringError :
631+ pass
614632
615633 # We don't need to produce python_uwp.exe and its *w variant. Or the
616634 # python3.dll, pyshellext, or pylauncher.
@@ -730,9 +748,11 @@ def build_openssl_for_arch(
730748 elif arch == "amd64" :
731749 configure = "VC-WIN64A"
732750 prefix = "64"
751+ elif arch == "arm64" :
752+ configure = "VC-WIN64-ARM"
753+ prefix = "arm64"
733754 else :
734- print ("invalid architecture: %s" % arch )
735- sys .exit (1 )
755+ raise Exception ("unhandled architecture: %s" % arch )
736756
737757 # The official CPython OpenSSL builds hack ms/uplink.c to change the
738758 # ``GetModuleHandle(NULL)`` invocation to load things from _ssl.pyd
@@ -780,6 +800,12 @@ def build_openssl_for_arch(
780800 log ("copying %s to %s" % (source , dest ))
781801 shutil .copyfile (source , dest )
782802
803+ # Copy `applink.c` to the include directory.
804+ source_applink = source_root / "ms" / "applink.c"
805+ dest_applink = install_root / "include" / "openssl" / "applink.c"
806+ log ("copying %s to %s" % (source_applink , dest_applink ))
807+ shutil .copyfile (source_applink , dest_applink )
808+
783809
784810def build_openssl (
785811 entry : str ,
@@ -801,6 +827,7 @@ def build_openssl(
801827
802828 root_32 = td / "x86"
803829 root_64 = td / "x64"
830+ root_arm64 = td / "arm64"
804831
805832 if arch == "x86" :
806833 root_32 .mkdir ()
@@ -824,13 +851,28 @@ def build_openssl(
824851 root_64 ,
825852 jom_archive = jom_archive ,
826853 )
854+ elif arch == "arm64" :
855+ root_arm64 .mkdir ()
856+ build_openssl_for_arch (
857+ perl_path ,
858+ "arm64" ,
859+ openssl_archive ,
860+ openssl_version ,
861+ nasm_archive ,
862+ root_arm64 ,
863+ jom_archive = jom_archive ,
864+ )
827865 else :
828- raise ValueError ("unhandled arch : %s" % arch )
866+ raise Exception ("unhandled architecture : %s" % arch )
829867
830868 install = td / "out"
831869
832870 if arch == "x86" :
833871 shutil .copytree (root_32 / "install" / "32" , install / "openssl" / "win32" )
872+ elif arch == "arm64" :
873+ shutil .copytree (
874+ root_arm64 / "install" / "arm64" , install / "openssl" / "arm64"
875+ )
834876 else :
835877 shutil .copytree (root_64 / "install" / "64" , install / "openssl" / "amd64" )
836878
@@ -901,9 +943,14 @@ def build_libffi(
901943 if arch == "x86" :
902944 args .append ("-x86" )
903945 artifacts_path = ffi_source_path / "i686-pc-cygwin"
904- else :
946+ elif arch == "arm64" :
947+ args .append ("-arm64" )
948+ artifacts_path = ffi_source_path / "aarch64-w64-cygwin"
949+ elif arch == "amd64" :
905950 args .append ("-x64" )
906951 artifacts_path = ffi_source_path / "x86_64-w64-cygwin"
952+ else :
953+ raise Exception ("unhandled architecture: %s" % arch )
907954
908955 subprocess .run (args , env = env , check = True )
909956
@@ -1069,8 +1116,10 @@ def find_additional_dependencies(project: pathlib.Path):
10691116 abi_platform = "win_amd64"
10701117 elif arch == "win32" :
10711118 abi_platform = "win32"
1119+ elif arch == "arm64" :
1120+ abi_platform = "win_arm64"
10721121 else :
1073- raise ValueError ("unhandled arch : %s" % arch )
1122+ raise Exception ("unhandled architecture : %s" % arch )
10741123
10751124 if freethreaded :
10761125 abi_tag = ".cp%st-%s" % (python_majmin , abi_platform )
@@ -1171,8 +1220,8 @@ def find_additional_dependencies(project: pathlib.Path):
11711220 if name == "zlib" :
11721221 name = zlib_entry
11731222
1174- # On 3.14+, we use the latest tcl/tk version
1175- if ext == "_tkinter" and python_majmin == "314" :
1223+ # On 3.14+ and aarch64 , we use the latest tcl/tk version
1224+ if ext == "_tkinter" and ( python_majmin == "314" or arch == "arm64" ) :
11761225 name = name .replace ("-8612" , "" )
11771226
11781227 download_entry = DOWNLOADS [name ]
@@ -1258,16 +1307,18 @@ def build_cpython(
12581307 setuptools_wheel = download_entry ("setuptools" , BUILD )
12591308 pip_wheel = download_entry ("pip" , BUILD )
12601309
1261- # On CPython 3.14+, we use the latest tcl/tk version which has additional runtime
1262- # dependencies, so we are conservative and use the old version elsewhere.
1263- if meets_python_minimum_version (python_version , "3.14" ):
1264- tk_bin_archive = download_entry (
1265- "tk-windows-bin" , BUILD , local_name = "tk-windows-bin.tar.gz"
1266- )
1267- else :
1268- tk_bin_archive = download_entry (
1269- "tk-windows-bin-8612" , BUILD , local_name = "tk-windows-bin.tar.gz"
1270- )
1310+ # On CPython 3.14+, we use the latest tcl/tk version which has additional
1311+ # runtime dependencies, so we are conservative and use the old version
1312+ # elsewhere. The old version isn't built for arm64, so we use the new
1313+ # version there too
1314+ tk_bin_entry = (
1315+ "tk-windows-bin"
1316+ if meets_python_minimum_version (python_version , "3.14" ) or arch == "arm64"
1317+ else "tk-windows-bin-8612"
1318+ )
1319+ tk_bin_archive = download_entry (
1320+ tk_bin_entry , BUILD , local_name = "tk-windows-bin.tar.gz"
1321+ )
12711322
12721323 # On CPython 3.14+, zstd is included
12731324 if meets_python_minimum_version (python_version , "3.14" ):
@@ -1297,8 +1348,11 @@ def build_cpython(
12971348 elif arch == "x86" :
12981349 build_platform = "win32"
12991350 build_directory = "win32"
1351+ elif arch == "arm64" :
1352+ build_platform = "arm64"
1353+ build_directory = "arm64"
13001354 else :
1301- raise ValueError ("unhandled arch : %s" % arch )
1355+ raise Exception ("unhandled architecture : %s" % arch )
13021356
13031357 tempdir_opts = (
13041358 {"ignore_cleanup_errors" : True } if sys .version_info >= (3 , 12 ) else {}
@@ -1332,7 +1386,7 @@ def build_cpython(
13321386
13331387 # We need all the OpenSSL library files in the same directory to appease
13341388 # install rules.
1335- openssl_arch = {"amd64" : "amd64" , "x86" : "win32" }[arch ]
1389+ openssl_arch = {"amd64" : "amd64" , "x86" : "win32" , "arm64" : "arm64" }[arch ]
13361390 openssl_root = td / "openssl" / openssl_arch
13371391 openssl_bin_path = openssl_root / "bin"
13381392 openssl_lib_path = openssl_root / "lib"
@@ -1346,6 +1400,17 @@ def build_cpython(
13461400 log ("copying %s to %s" % (source , dest ))
13471401 shutil .copyfile (source , dest )
13481402
1403+ # Delete the tk nmake helper, it's not needed and links msvc
1404+ tcltk_commit : str = DOWNLOADS [tk_bin_entry ]["git_commit" ]
1405+ tcltk_path = td / ("cpython-bin-deps-%s" % tcltk_commit )
1406+ (
1407+ tcltk_path
1408+ / build_directory
1409+ / "lib"
1410+ / "nmake"
1411+ / "x86_64-w64-mingw32-nmakehlp.exe"
1412+ ).unlink ()
1413+
13491414 cpython_source_path = td / ("Python-%s" % python_version )
13501415 pcbuild_path = cpython_source_path / "PCbuild"
13511416
@@ -1368,6 +1433,7 @@ def build_cpython(
13681433 build_directory ,
13691434 python_version = python_version ,
13701435 zlib_entry = zlib_entry ,
1436+ arch = arch ,
13711437 )
13721438
13731439 if pgo :
@@ -1790,9 +1856,14 @@ def main() -> None:
17901856 if os .environ .get ("Platform" ) == "x86" :
17911857 target_triple = "i686-pc-windows-msvc"
17921858 arch = "x86"
1793- else :
1859+ elif os .environ .get ("Platform" ) == "arm64" :
1860+ target_triple = "aarch64-pc-windows-msvc"
1861+ arch = "arm64"
1862+ elif os .environ .get ("Platform" ) == "x64" :
17941863 target_triple = "x86_64-pc-windows-msvc"
17951864 arch = "amd64"
1865+ else :
1866+ raise Exception ("unhandled architecture: %s" % os .environ .get ("Platform" ))
17961867
17971868 # TODO need better dependency checking.
17981869
0 commit comments