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

This commit is contained in:
Brandt Bucher
2025-05-02 11:17:15 -07:00
committed by GitHub
parent a0bc0c462f
commit bfcbb28223
9 changed files with 44 additions and 13 deletions

View File

@@ -95,10 +95,10 @@ jobs:
with: with:
python-version: '3.11' python-version: '3.11'
# PCbuild downloads LLVM automatically:
- name: Windows - name: Windows
if: runner.os == 'Windows' if: runner.os == 'Windows'
run: | 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/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 ./PCbuild/rt.bat ${{ matrix.debug && '-d' || '' }} -p ${{ matrix.architecture }} -q --multiprocess 0 --timeout 4500 --verbose2 --verbose3

View File

@@ -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.

View File

@@ -111,6 +111,7 @@ if "%IncludeExternals%"=="" set IncludeExternals=true
if "%IncludeCTypes%"=="" set IncludeCTypes=true if "%IncludeCTypes%"=="" set IncludeCTypes=true
if "%IncludeSSL%"=="" set IncludeSSL=true if "%IncludeSSL%"=="" set IncludeSSL=true
if "%IncludeTkinter%"=="" set IncludeTkinter=true if "%IncludeTkinter%"=="" set IncludeTkinter=true
if "%UseJIT%" NEQ "true" set IncludeLLVM=false
if "%IncludeExternals%"=="true" call "%dir%get_externals.bat" if "%IncludeExternals%"=="true" call "%dir%get_externals.bat"

View File

@@ -15,6 +15,7 @@ set IncludeSSLSrc=false
if "%~1"=="--no-tkinter" (set IncludeTkinter=false) & shift & goto CheckOpts if "%~1"=="--no-tkinter" (set IncludeTkinter=false) & shift & goto CheckOpts
if "%~1"=="--no-openssl" (set IncludeSSL=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-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"=="--tkinter-src" (set IncludeTkinterSrc=true) & shift & goto CheckOpts
if "%~1"=="--openssl-src" (set IncludeSSLSrc=true) & shift & goto CheckOpts if "%~1"=="--openssl-src" (set IncludeSSLSrc=true) & shift & goto CheckOpts
if "%~1"=="--libffi-src" (set IncludeLibffiSrc=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 "%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 "%IncludeTkinter%"=="false" set binaries=%binaries% tcltk-8.6.15.0
if NOT "%IncludeSSLSrc%"=="false" set binaries=%binaries% nasm-2.11.06 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 ( for %%b in (%binaries%) do (
if exist "%EXTERNALS_DIR%\%%b" ( if exist "%EXTERNALS_DIR%\%%b" (
@@ -98,7 +100,7 @@ goto end
:usage :usage
echo.Valid options: -c, --clean, --clean-only, --organization, --python, echo.Valid options: -c, --clean, --clean-only, --organization, --python,
echo.--no-tkinter, --no-openssl echo.--no-tkinter, --no-openssl, --no-llvm
echo. echo.
echo.Pull all sources and binaries necessary for compiling optional extension echo.Pull all sources and binaries necessary for compiling optional extension
echo.modules that rely on external libraries. echo.modules that rely on external libraries.

View File

@@ -30,7 +30,11 @@
<_KeywordOutputs Include="$(PySourcePath)Lib\keyword.py" /> <_KeywordOutputs Include="$(PySourcePath)Lib\keyword.py" />
<!-- Taken from _Target._compute_digest in Tools\jit\_targets.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\**"/> <_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.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;"/> <_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"/> <_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" /> <_SbomSources Include="$(PySourcePath)PCbuild\get_externals.bat" />
@@ -124,6 +128,9 @@
<Exec Command='$(PythonForBuild) "$(PySourcePath)Tools\jit\build.py" $(JITArgs)' <Exec Command='$(PythonForBuild) "$(PySourcePath)Tools\jit\build.py" $(JITArgs)'
WorkingDirectory="$(GeneratedPyConfigDir)"/> WorkingDirectory="$(GeneratedPyConfigDir)"/>
</Target> </Target>
<Target Name="_CleanJIT" AfterTargets="Clean">
<Delete Files="@(_JITOutputs)"/>
</Target>
<Target Name="_RegenNoPGUpdate" <Target Name="_RegenNoPGUpdate"
Condition="$(Configuration) != 'PGUpdate'" Condition="$(Configuration) != 'PGUpdate'"

View File

@@ -41,7 +41,9 @@ Homebrew won't add any of the tools to your `$PATH`. That's okay; the build scri
### Windows ### 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): Alternatively, you can use [chocolatey](https://chocolatey.org):

View File

@@ -8,8 +8,11 @@ import shlex
import subprocess import subprocess
import typing import typing
import _targets
_LLVM_VERSION = 19 _LLVM_VERSION = 19
_LLVM_VERSION_PATTERN = re.compile(rf"version\s+{_LLVM_VERSION}\.\d+\.\d+\S*\s+") _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") _P = typing.ParamSpec("_P")
_R = typing.TypeVar("_R") _R = typing.TypeVar("_R")
@@ -72,6 +75,11 @@ async def _find_tool(tool: str, *, echo: bool = False) -> str | None:
return path return path
# Versioned executables: # Versioned executables:
path = f"{tool}-{_LLVM_VERSION}" 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): if await _check_tool_version(path, echo=echo):
return path return path
# Homebrew-installed executables: # Homebrew-installed executables:

View File

@@ -23,8 +23,10 @@ TOOLS_JIT_BUILD = pathlib.Path(__file__).resolve()
TOOLS_JIT = TOOLS_JIT_BUILD.parent TOOLS_JIT = TOOLS_JIT_BUILD.parent
TOOLS = TOOLS_JIT.parent TOOLS = TOOLS_JIT.parent
CPYTHON = TOOLS.parent CPYTHON = TOOLS.parent
EXTERNALS = CPYTHON / "externals"
PYTHON_EXECUTOR_CASES_C_H = CPYTHON / "Python" / "executor_cases.c.h" PYTHON_EXECUTOR_CASES_C_H = CPYTHON / "Python" / "executor_cases.c.h"
TOOLS_JIT_TEMPLATE_C = TOOLS_JIT / "template.c" TOOLS_JIT_TEMPLATE_C = TOOLS_JIT / "template.c"
ASYNCIO_RUNNER = asyncio.Runner() ASYNCIO_RUNNER = asyncio.Runner()
_S = typing.TypeVar("_S", _schema.COFFSection, _schema.ELFSection, _schema.MachOSection) _S = typing.TypeVar("_S", _schema.COFFSection, _schema.ELFSection, _schema.MachOSection)

View File

@@ -8,6 +8,7 @@ import sys
import _targets import _targets
if __name__ == "__main__": if __name__ == "__main__":
out = pathlib.Path.cwd().resolve()
comment = f"$ {shlex.join([pathlib.Path(sys.executable).name] + sys.argv)}" comment = f"$ {shlex.join([pathlib.Path(sys.executable).name] + sys.argv)}"
parser = argparse.ArgumentParser(description=__doc__) parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument( parser.add_argument(
@@ -31,17 +32,22 @@ if __name__ == "__main__":
target.force = args.force target.force = args.force
target.verbose = args.verbose target.verbose = args.verbose
target.build( target.build(
pathlib.Path.cwd(), out,
comment=comment, comment=comment,
stencils_h=f"jit_stencils-{target.triple}.h", stencils_h=f"jit_stencils-{target.triple}.h",
force=args.force, force=args.force,
) )
jit_stencils_h = out / "jit_stencils.h"
with open("jit_stencils.h", "w") as fp: lines = [f"// {comment}\n"]
for idx, target in enumerate(args.target): guard = "#if"
fp.write(f"#{'if' if idx == 0 else 'elif'} {target.condition}\n") for target in args.target:
fp.write(f'#include "jit_stencils-{target.triple}.h"\n') lines.append(f"{guard} {target.condition}\n")
lines.append(f'#include "jit_stencils-{target.triple}.h"\n')
fp.write("#else\n") guard = "#elif"
fp.write('#error "unexpected target"\n') lines.append("#else\n")
fp.write("#endif\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)