https://github.com/python/cpython/commit/bfcbb28223b733b9cb88f152a059a9e1416f3467
commit: bfcbb28223b733b9cb88f152a059a9e1416f3467
branch: main
author: Brandt Bucher <brandtbuc...@microsoft.com>
committer: brandtbucher <brandtbuc...@gmail.com>
date: 2025-05-02T11:17:15-07:00
summary:

GH-113464: Get LLVM from cpython-bin-deps on Windows (GH-133278)

files:
A Misc/NEWS.d/next/Build/2025-05-01-17-27-06.gh-issue-113464.vjE5X4.rst
M .github/workflows/jit.yml
M PCbuild/build.bat
M PCbuild/get_externals.bat
M PCbuild/regen.targets
M Tools/jit/README.md
M Tools/jit/_llvm.py
M Tools/jit/_targets.py
M Tools/jit/build.py

diff --git a/.github/workflows/jit.yml b/.github/workflows/jit.yml
index 6ea238d294ca89..116e0c1e945e38 100644
--- a/.github/workflows/jit.yml
+++ b/.github/workflows/jit.yml
@@ -95,10 +95,10 @@ jobs:
         with:
           python-version: '3.11'
 
+      # PCbuild downloads LLVM automatically:
       - name: Windows
         if: runner.os == 'Windows'
         run: |
-          choco install llvm --allow-downgrade --no-progress --version ${{ 
matrix.llvm }}.1.0
           ./PCbuild/build.bat --experimental-jit ${{ matrix.debug && '-d' || 
'' }} -p ${{ matrix.architecture }}
           ./PCbuild/rt.bat ${{ matrix.debug && '-d' || '' }} -p ${{ 
matrix.architecture }} -q --multiprocess 0 --timeout 4500 --verbose2 --verbose3
 
diff --git 
a/Misc/NEWS.d/next/Build/2025-05-01-17-27-06.gh-issue-113464.vjE5X4.rst 
b/Misc/NEWS.d/next/Build/2025-05-01-17-27-06.gh-issue-113464.vjE5X4.rst
new file mode 100644
index 00000000000000..d150b19d5f1687
--- /dev/null
+++ b/Misc/NEWS.d/next/Build/2025-05-01-17-27-06.gh-issue-113464.vjE5X4.rst
@@ -0,0 +1,3 @@
+Use the cpython-bin-deps "externals" repository for Windows LLVM dependency
+management. Installing LLVM manually is no longer necessary for Windows JIT
+builds.
diff --git a/PCbuild/build.bat b/PCbuild/build.bat
index 88d6b99d651cf3..2f358991e484ce 100644
--- a/PCbuild/build.bat
+++ b/PCbuild/build.bat
@@ -111,6 +111,7 @@ if "%IncludeExternals%"=="" set IncludeExternals=true
 if "%IncludeCTypes%"=="" set IncludeCTypes=true
 if "%IncludeSSL%"=="" set IncludeSSL=true
 if "%IncludeTkinter%"=="" set IncludeTkinter=true
+if "%UseJIT%" NEQ "true" set IncludeLLVM=false
 
 if "%IncludeExternals%"=="true" call "%dir%get_externals.bat"
 
diff --git a/PCbuild/get_externals.bat b/PCbuild/get_externals.bat
index b28ce9caeb3f13..49ace616793d8e 100644
--- a/PCbuild/get_externals.bat
+++ b/PCbuild/get_externals.bat
@@ -15,6 +15,7 @@ set IncludeSSLSrc=false
 if "%~1"=="--no-tkinter" (set IncludeTkinter=false) & shift & goto CheckOpts
 if "%~1"=="--no-openssl" (set IncludeSSL=false) & shift & goto CheckOpts
 if "%~1"=="--no-libffi" (set IncludeLibffi=false) & shift & goto CheckOpts
+if "%~1"=="--no-llvm" (set IncludeLLVM=false) & shift & goto CheckOpts
 if "%~1"=="--tkinter-src" (set IncludeTkinterSrc=true) & shift & goto CheckOpts
 if "%~1"=="--openssl-src" (set IncludeSSLSrc=true) & shift & goto CheckOpts
 if "%~1"=="--libffi-src" (set IncludeLibffiSrc=true) & shift & goto CheckOpts
@@ -80,6 +81,7 @@ if NOT "%IncludeLibffi%"=="false"  set binaries=%binaries% 
libffi-3.4.4
 if NOT "%IncludeSSL%"=="false"     set binaries=%binaries% openssl-bin-3.0.16.2
 if NOT "%IncludeTkinter%"=="false" set binaries=%binaries% tcltk-8.6.15.0
 if NOT "%IncludeSSLSrc%"=="false"  set binaries=%binaries% nasm-2.11.06
+if NOT "%IncludeLLVM%"=="false"    set binaries=%binaries% llvm-19.1.7.0
 
 for %%b in (%binaries%) do (
     if exist "%EXTERNALS_DIR%\%%b" (
@@ -98,7 +100,7 @@ goto end
 
 :usage
 echo.Valid options: -c, --clean, --clean-only, --organization, --python,
-echo.--no-tkinter, --no-openssl
+echo.--no-tkinter, --no-openssl, --no-llvm
 echo.
 echo.Pull all sources and binaries necessary for compiling optional extension
 echo.modules that rely on external libraries.
diff --git a/PCbuild/regen.targets b/PCbuild/regen.targets
index 416241d9d0df10..e7822a126c6304 100644
--- a/PCbuild/regen.targets
+++ b/PCbuild/regen.targets
@@ -30,7 +30,11 @@
     <_KeywordOutputs Include="$(PySourcePath)Lib\keyword.py" />
     <!-- Taken from _Target._compute_digest in Tools\jit\_targets.py: -->
     <_JITSources 
Include="$(PySourcePath)Python\executor_cases.c.h;$(GeneratedPyConfigDir)pyconfig.h;$(PySourcePath)Tools\jit\**"/>
+    <!-- Need to explicitly enumerate these, since globbing doesn't work for 
missing outputs: -->
     <_JITOutputs Include="$(GeneratedPyConfigDir)jit_stencils.h"/>
+    <_JITOutputs 
Include="$(GeneratedPyConfigDir)jit_stencils-aarch64-pc-windows-msvc.h" 
Condition="$(Platform) == 'ARM64'"/>
+    <_JITOutputs 
Include="$(GeneratedPyConfigDir)jit_stencils-i686-pc-windows-msvc.h" 
Condition="$(Platform) == 'Win32'"/>
+    <_JITOutputs 
Include="$(GeneratedPyConfigDir)jit_stencils-x86_64-pc-windows-msvc.h" 
Condition="$(Platform) == 'x64'"/>
     <_CasesSources 
Include="$(PySourcePath)Python\bytecodes.c;$(PySourcePath)Python\optimizer_bytecodes.c;"/>
     <_CasesOutputs 
Include="$(PySourcePath)Python\generated_cases.c.h;$(PySourcePath)Include\opcode_ids.h;$(PySourcePath)Include\internal\pycore_uop_ids.h;$(PySourcePath)Python\opcode_targets.h;$(PySourcePath)Include\internal\pycore_opcode_metadata.h;$(PySourcePath)Include\internal\pycore_uop_metadata.h;$(PySourcePath)Python\optimizer_cases.c.h;$(PySourcePath)Lib\_opcode_metadata.py"/>
     <_SbomSources Include="$(PySourcePath)PCbuild\get_externals.bat" />
@@ -124,6 +128,9 @@
     <Exec Command='$(PythonForBuild) "$(PySourcePath)Tools\jit\build.py" 
$(JITArgs)'
           WorkingDirectory="$(GeneratedPyConfigDir)"/>
   </Target>
+  <Target Name="_CleanJIT" AfterTargets="Clean">
+    <Delete Files="@(_JITOutputs)"/>
+  </Target>
 
   <Target Name="_RegenNoPGUpdate"
           Condition="$(Configuration) != 'PGUpdate'"
diff --git a/Tools/jit/README.md b/Tools/jit/README.md
index 4107265754f6ec..ff4b3964e65bad 100644
--- a/Tools/jit/README.md
+++ b/Tools/jit/README.md
@@ -41,7 +41,9 @@ Homebrew won't add any of the tools to your `$PATH`. That's 
okay; the build scri
 
 ### Windows
 
-Install LLVM 19 [by searching for it on LLVM's GitHub releases 
page](https://github.com/llvm/llvm-project/releases?q=19), clicking on 
"Assets", downloading the appropriate Windows installer for your platform 
(likely the file ending with `-win64.exe`), and running it. **When installing, 
be sure to select the option labeled "Add LLVM to the system PATH".**
+LLVM is downloaded automatically (along with other external binary 
dependencies) by `PCbuild\build.bat`.
+
+Otherwise, you can install LLVM 19 [by searching for it on LLVM's GitHub 
releases page](https://github.com/llvm/llvm-project/releases?q=19), clicking on 
"Assets", downloading the appropriate Windows installer for your platform 
(likely the file ending with `-win64.exe`), and running it. **When installing, 
be sure to select the option labeled "Add LLVM to the system PATH".**
 
 Alternatively, you can use [chocolatey](https://chocolatey.org):
 
diff --git a/Tools/jit/_llvm.py b/Tools/jit/_llvm.py
index 925b56ac669aa0..f09a8404871b24 100644
--- a/Tools/jit/_llvm.py
+++ b/Tools/jit/_llvm.py
@@ -8,8 +8,11 @@
 import subprocess
 import typing
 
+import _targets
+
 _LLVM_VERSION = 19
 _LLVM_VERSION_PATTERN = 
re.compile(rf"version\s+{_LLVM_VERSION}\.\d+\.\d+\S*\s+")
+_EXTERNALS_LLVM_TAG = "llvm-19.1.7.0"
 
 _P = typing.ParamSpec("_P")
 _R = typing.TypeVar("_R")
@@ -72,6 +75,11 @@ async def _find_tool(tool: str, *, echo: bool = False) -> 
str | None:
         return path
     # Versioned executables:
     path = f"{tool}-{_LLVM_VERSION}"
+    if await _check_tool_version(path, echo=echo):
+        return path
+    # PCbuild externals:
+    externals = os.environ.get("EXTERNALS_DIR", _targets.EXTERNALS)
+    path = os.path.join(externals, _EXTERNALS_LLVM_TAG, "bin", tool)
     if await _check_tool_version(path, echo=echo):
         return path
     # Homebrew-installed executables:
diff --git a/Tools/jit/_targets.py b/Tools/jit/_targets.py
index e0ac735a20f691..6ceb4404e74ce7 100644
--- a/Tools/jit/_targets.py
+++ b/Tools/jit/_targets.py
@@ -23,8 +23,10 @@
 TOOLS_JIT = TOOLS_JIT_BUILD.parent
 TOOLS = TOOLS_JIT.parent
 CPYTHON = TOOLS.parent
+EXTERNALS = CPYTHON / "externals"
 PYTHON_EXECUTOR_CASES_C_H = CPYTHON / "Python" / "executor_cases.c.h"
 TOOLS_JIT_TEMPLATE_C = TOOLS_JIT / "template.c"
+
 ASYNCIO_RUNNER = asyncio.Runner()
 
 _S = typing.TypeVar("_S", _schema.COFFSection, _schema.ELFSection, 
_schema.MachOSection)
diff --git a/Tools/jit/build.py b/Tools/jit/build.py
index 4d1e484b6838eb..49b08f477dbed7 100644
--- a/Tools/jit/build.py
+++ b/Tools/jit/build.py
@@ -8,6 +8,7 @@
 import _targets
 
 if __name__ == "__main__":
+    out = pathlib.Path.cwd().resolve()
     comment = f"$ {shlex.join([pathlib.Path(sys.executable).name] + sys.argv)}"
     parser = argparse.ArgumentParser(description=__doc__)
     parser.add_argument(
@@ -31,17 +32,22 @@
         target.force = args.force
         target.verbose = args.verbose
         target.build(
-            pathlib.Path.cwd(),
+            out,
             comment=comment,
             stencils_h=f"jit_stencils-{target.triple}.h",
             force=args.force,
         )
-
-    with open("jit_stencils.h", "w") as fp:
-        for idx, target in enumerate(args.target):
-            fp.write(f"#{'if' if idx == 0 else 'elif'} {target.condition}\n")
-            fp.write(f'#include "jit_stencils-{target.triple}.h"\n')
-
-        fp.write("#else\n")
-        fp.write('#error "unexpected target"\n')
-        fp.write("#endif\n")
+    jit_stencils_h = out / "jit_stencils.h"
+    lines = [f"// {comment}\n"]
+    guard = "#if"
+    for target in args.target:
+        lines.append(f"{guard} {target.condition}\n")
+        lines.append(f'#include "jit_stencils-{target.triple}.h"\n')
+        guard = "#elif"
+    lines.append("#else\n")
+    lines.append('#error "unexpected target"\n')
+    lines.append("#endif\n")
+    body = "".join(lines)
+    # Don't touch the file if it hasn't changed (so we don't trigger a 
rebuild):
+    if not jit_stencils_h.is_file() or jit_stencils_h.read_text() != body:
+        jit_stencils_h.write_text(body)

_______________________________________________
Python-checkins mailing list -- python-checkins@python.org
To unsubscribe send an email to python-checkins-le...@python.org
https://mail.python.org/mailman3/lists/python-checkins.python.org/
Member address: arch...@mail-archive.com

Reply via email to