diff --git a/.github/setup-rust-windows/action.yml b/.github/setup-rust-windows/action.yml new file mode 100644 index 00000000000000..97e0288592b146 --- /dev/null +++ b/.github/setup-rust-windows/action.yml @@ -0,0 +1,38 @@ +name: Set up Rust and LLVM for Windows +description: >- + Install the pinned Rust toolchain with Windows cross-compile targets and + a version of LLVM whose libclang is loadable by bindgen. + +inputs: + arch: + description: CPU architecture (x64, Win32, arm64) + required: true + +runs: + using: composite + steps: + - uses: dtolnay/rust-toolchain@1.91.1 + with: + targets: i686-pc-windows-msvc,x86_64-pc-windows-msvc,aarch64-pc-windows-msvc + # LIBCLANG_PATH must be set explicitly so the vcxproj uses the LLVM we + # install here rather than the VS-bundled LLVM whose clang headers have + # AVX intrinsic bugs. The values are hardcoded literals (no attacker- + # controlled input), so the GITHUB_ENV writes are safe. + - name: Install LLVM for bindgen + if: inputs.arch != 'arm64' + shell: cmd + run: | # zizmor: ignore[github-env] + choco install llvm --allow-downgrade --no-progress --version 21.1.0 + if not exist "C:\Program Files\LLVM\bin\libclang.dll" exit /b 1 + echo LIBCLANG_PATH=C:\Program Files\LLVM\bin>> "%GITHUB_ENV%" + # Chocolatey's LLVM package only ships x64 binaries, which an ARM64-native + # cargo process cannot load. Install the official ARM64 build directly. + - name: Install LLVM for bindgen (ARM64) + if: inputs.arch == 'arm64' + shell: pwsh + run: | # zizmor: ignore[github-env] + $installer = Join-Path $env:RUNNER_TEMP 'LLVM-21.1.0-woa64.exe' + Invoke-WebRequest 'https://github.com/llvm/llvm-project/releases/download/llvmorg-21.1.0/LLVM-21.1.0-woa64.exe' -OutFile $installer + Start-Process -Wait -FilePath $installer -ArgumentList '/S','/D=C:\Program Files\LLVM' + if (!(Test-Path 'C:\Program Files\LLVM\bin\libclang.dll')) { exit 1 } + echo "LIBCLANG_PATH=C:\Program Files\LLVM\bin" >> $env:GITHUB_ENV diff --git a/.github/workflows/jit.yml b/.github/workflows/jit.yml index 3349eb042425dd..dc4210c59e195b 100644 --- a/.github/workflows/jit.yml +++ b/.github/workflows/jit.yml @@ -100,7 +100,6 @@ jobs: - uses: actions/setup-python@v5 with: python-version: '3.11' - # PCbuild downloads LLVM automatically: - name: Windows if: runner.os == 'Windows' diff --git a/.github/workflows/reusable-windows-msi.yml b/.github/workflows/reusable-windows-msi.yml index c95e40a38095f9..af9139d161f6b3 100644 --- a/.github/workflows/reusable-windows-msi.yml +++ b/.github/workflows/reusable-windows-msi.yml @@ -26,6 +26,9 @@ jobs: - uses: actions/checkout@v4 with: persist-credentials: false + - uses: ./.github/setup-rust-windows + with: + arch: ${{ inputs.arch }} - name: Build CPython installer run: ./Tools/msi/build.bat --doc -"${ARCH}" shell: bash diff --git a/.github/workflows/reusable-windows.yml b/.github/workflows/reusable-windows.yml index 0648b770753255..8132d205f9bf62 100644 --- a/.github/workflows/reusable-windows.yml +++ b/.github/workflows/reusable-windows.yml @@ -29,6 +29,9 @@ jobs: - uses: actions/checkout@v4 with: persist-credentials: false + - uses: ./.github/setup-rust-windows + with: + arch: ${{ inputs.arch }} - name: Register MSVC problem matcher if: inputs.arch != 'Win32' run: echo "::add-matcher::.github/problem-matchers/msvc.json" diff --git a/.github/workflows/tail-call.yml b/.github/workflows/tail-call.yml index e99e317182eaa6..795e2722d65144 100644 --- a/.github/workflows/tail-call.yml +++ b/.github/workflows/tail-call.yml @@ -78,7 +78,6 @@ jobs: - uses: actions/setup-python@v5 with: python-version: '3.11' - - name: Native Windows (debug) if: runner.os == 'Windows' && matrix.architecture != 'ARM64' shell: cmd diff --git a/Modules/cpython-sys/build.rs b/Modules/cpython-sys/build.rs index a97c1413914a02..3692d384f698e3 100644 --- a/Modules/cpython-sys/build.rs +++ b/Modules/cpython-sys/build.rs @@ -10,11 +10,17 @@ fn main() { let out_path = PathBuf::from(env::var("OUT_DIR").unwrap()); let builddir = env::var("PYTHON_BUILD_DIR").ok(); emit_rerun_instructions(builddir.as_deref()); - if gil_disabled(srcdir, builddir.as_deref()) { + let gil_disabled = gil_disabled(srcdir, builddir.as_deref()); + if gil_disabled { println!("cargo:rustc-cfg=py_gil_disabled"); } println!("cargo::rustc-check-cfg=cfg(py_gil_disabled)"); - generate_c_api_bindings(srcdir, builddir.as_deref(), out_path.as_path()); + generate_c_api_bindings( + srcdir, + builddir.as_deref(), + out_path.as_path(), + gil_disabled, + ); } // Bindgen depends on build-time env and, on iOS, can also inherit the @@ -24,6 +30,8 @@ fn emit_rerun_instructions(builddir: Option<&str>) { for var in [ "IPHONEOS_DEPLOYMENT_TARGET", "LLVM_TARGET", + "PY_DEBUG", + "PY_GIL_DISABLED", "PYTHON_BUILD_DIR", "PY_CC", "PY_CPPFLAGS", @@ -41,6 +49,10 @@ fn emit_rerun_instructions(builddir: Option<&str>) { } fn gil_disabled(srcdir: &Path, builddir: Option<&str>) -> bool { + if env_var_is_truthy("PY_GIL_DISABLED") { + return true; + } + let mut candidates = Vec::new(); if let Some(build) = builddir { candidates.push(PathBuf::from(build)); @@ -57,12 +69,30 @@ fn gil_disabled(srcdir: &Path, builddir: Option<&str>) -> bool { false } -fn generate_c_api_bindings(srcdir: &Path, builddir: Option<&str>, out_path: &Path) { +fn env_var_is_truthy(name: &str) -> bool { + env::var(name) + .map(|v| matches!(v.to_ascii_lowercase().as_str(), "1" | "true" | "yes")) + .unwrap_or(false) +} + +fn generate_c_api_bindings( + srcdir: &Path, + builddir: Option<&str>, + out_path: &Path, + gil_disabled: bool, +) { let mut builder = bindgen::Builder::default().header("wrapper.h"); // Suppress all clang warnings (deprecation warnings, etc.) builder = builder.clang_arg("-w"); + if env_var_is_truthy("PY_DEBUG") { + builder = builder.clang_arg("-D_DEBUG"); + } + if gil_disabled { + builder = builder.clang_arg("-DPy_GIL_DISABLED=1"); + } + // Tell clang the correct target triple for cross-compilation when we have // an LLVM-specific triple. Otherwise let bindgen translate Cargo's TARGET // itself (e.g. aarch64-apple-ios-sim -> arm64-apple-ios-simulator). @@ -162,10 +192,105 @@ fn generate_c_api_bindings(srcdir: &Path, builddir: Option<&str>, out_path: &Pat .generate() .expect("Unable to generate bindings"); + let dll_name = python_dll_name(srcdir, env_var_is_truthy("PY_DEBUG"), gil_disabled); + let bindings = patch_windows_imported_pointer_globals(bindings.to_string(), &dll_name); + // Write the bindings to the $OUT_DIR/c_api.rs file. - bindings - .write_to_file(out_path.join("c_api.rs")) - .expect("Couldn't write bindings!"); + std::fs::write(out_path.join("c_api.rs"), bindings).expect("Couldn't write bindings!"); +} + +/// Build the Windows DLL base name: `python{major}{minor}[t][_d]`. +fn python_dll_name(srcdir: &Path, debug: bool, gil_disabled: bool) -> String { + let patchlevel = srcdir.join("Include").join("patchlevel.h"); + let contents = + std::fs::read_to_string(&patchlevel).expect("failed to read Include/patchlevel.h"); + + let major = extract_define_int(&contents, "PY_MAJOR_VERSION"); + let minor = extract_define_int(&contents, "PY_MINOR_VERSION"); + + let mut name = format!("python{major}{minor}"); + if gil_disabled { + name.push('t'); + } + if debug { + name.push_str("_d"); + } + name +} + +fn extract_define_int(contents: &str, name: &str) -> u32 { + for line in contents.lines() { + let trimmed = line.trim(); + if let Some(rest) = trimmed.strip_prefix("#define") + && let Some(value) = rest.trim().strip_prefix(name) + && let Ok(n) = value.trim().parse() + { + return n; + } + } + panic!("could not find #define {name} in patchlevel.h"); +} + +fn patch_windows_imported_pointer_globals(bindings: String, dll_name: &str) -> String { + // On Windows/MSVC, exported data is imported through a synthetic + // "__imp_" pointer in the import address table (IAT). A plain + // `extern { pub static X: *mut T; }` linked via import library fails for + // data symbols because the import library only defines `__imp_X`, not `X`. + // + // Using `#[link_name = "__imp_X"]` links successfully but produces a + // single load — returning the *address* of the variable (the IAT slot + // value) rather than the variable's value. C's `__declspec(dllimport)` + // generates two loads to chase the indirection. + // + // The fix: annotate pointer-valued extern statics with `raw-dylib` on + // Windows so Rust generates the import thunk itself and handles the IAT + // indirection correctly — two loads, matching `__declspec(dllimport)`. + let lines: Vec<_> = bindings.lines().collect(); + let mut patched = String::with_capacity(bindings.len()); + let mut index = 0; + + while index < lines.len() { + if lines[index] == "unsafe extern \"C\" {" + && lines + .get(index + 1) + .and_then(|l| parse_pointer_static_decl(l)) + .is_some() + && lines.get(index + 2).is_some_and(|l| l.trim() == "}") + { + patched.push_str(&format!( + "#[cfg_attr(windows, link(name = \"{dll_name}\", kind = \"raw-dylib\"))]\n" + )); + // Keep the original extern block unchanged. + for i in index..index + 3 { + patched.push_str(lines[i]); + patched.push('\n'); + } + index += 3; + continue; + } + + patched.push_str(lines[index]); + patched.push('\n'); + index += 1; + } + + patched +} + +fn parse_pointer_static_decl(line: &str) -> Option<(&str, bool, &str)> { + let mut decl = line.trim().strip_prefix("pub static ")?; + let is_mut = decl.starts_with("mut "); + if is_mut { + decl = decl.strip_prefix("mut ")?; + } + + let (name, ty) = decl.split_once(':')?; + let ty = ty.trim().strip_suffix(';')?; + if !ty.starts_with('*') { + return None; + } + + Some((name.trim(), is_mut, ty)) } fn add_target_clang_args( diff --git a/PCbuild/_base64.vcxproj b/PCbuild/_base64.vcxproj new file mode 100644 index 00000000000000..916069c1aea82a --- /dev/null +++ b/PCbuild/_base64.vcxproj @@ -0,0 +1,164 @@ + + + + + Debug + ARM + + + Debug + ARM64 + + + Debug + Win32 + + + Debug + x64 + + + PGInstrument + ARM + + + PGInstrument + ARM64 + + + PGInstrument + Win32 + + + PGInstrument + x64 + + + PGUpdate + ARM + + + PGUpdate + ARM64 + + + PGUpdate + Win32 + + + PGUpdate + x64 + + + Release + ARM + + + Release + ARM64 + + + Release + Win32 + + + Release + x64 + + + + {D7B7A122-80A0-4AFB-9C06-18C29B3178E4} + _base64 + MakeFileProj + + + + + Makefile + NotSet + false + + + + $(PyStdlibPydExt) + + + + + + + + + + <_ProjectFileVersion>10.0.30319.1 + llvm-21.1.4.0 + dev + release + debug + release + i686-pc-windows-msvc + x86_64-pc-windows-msvc + thumbv7a-pc-windows-msvc + aarch64-pc-windows-msvc + $(IntDir)cargo\ + $(CargoTargetDir.TrimEnd(`\`)) + $(BuildPath.TrimEnd(`\`)) + $(PySourcePath)PC + $(PySourcePath)Cargo.toml + $(LIBCLANG_PATH) + $(ExternalsDir)$(BundledLlvmTag)\bin + $(ExternalsDir)$(BundledLlvmTag)\bin + $(LLVMInstallDir)\bin + $(LLVMInstallDir)\bin + $(VCInstallDir)Tools\Llvm\x64\bin + $(VCInstallDir)Tools\Llvm\x64\bin + $(VCInstallDir)Tools\Llvm\bin + $(VCInstallDir)Tools\Llvm\bin + C:\Program Files\LLVM\bin + C:\Program Files\LLVM\bin + $(OutDir) + $(TargetDir)$(TargetName)$(TargetExt) + $(CargoTargetDirNoSlash)\$(CargoTarget)\$(CargoBuildSubdir)\_base64.dll + $(CargoTargetDirNoSlash)\$(CargoTarget)\$(CargoBuildSubdir)\_base64.pdb + $(OutDir)$(TargetName).pdb + setlocal +if "$(Configuration)"=="Debug" (set "PY_DEBUG=1") else (set "PY_DEBUG=") +if "$(DisableGil)"=="true" (set "PY_GIL_DISABLED=1") else (set "PY_GIL_DISABLED=") +where cargo >NUL 2>NUL || (echo WARNING: cargo was not found on PATH, skipping _base64 & exit /b 0) +if not "$(LibClangPath)"=="" set "LIBCLANG_PATH=$(LibClangPath)" +if "$(LibClangPath)"=="" if not defined LIBCLANG_PATH (echo WARNING: libclang was not found, skipping _base64 & exit /b 0) +if defined LIBCLANG_PATH if not exist "%LIBCLANG_PATH%\libclang.dll" if not exist "%LIBCLANG_PATH%\clang.dll" (echo WARNING: libclang.dll not found at %LIBCLANG_PATH%, skipping _base64 & exit /b 0) +if not exist "$(CargoTargetDirNoSlash)" mkdir "$(CargoTargetDirNoSlash)" +set "CARGO_TARGET_DIR=$(CargoTargetDirNoSlash)" +set "PYTHON_BUILD_DIR=$(PythonBuildDir)" +set "LIBPYTHON="/LIBPATH:$(BuildPathNoSlash)" "$(PyDllName).lib"" +cargo build --lib --locked --package _base64 --manifest-path "$(CargoWorkspaceManifest)" --profile $(CargoProfile) --target $(CargoTarget) +if errorlevel 1 exit /b 1 +if not exist "$(RustExtensionPath)" (echo ERROR: cargo did not produce $(RustExtensionPath) & exit /b 1) +copy /Y "$(RustExtensionPath)" "$(TargetPath)" >NUL +if errorlevel 1 exit /b 1 +if exist "$(RustExtensionPdb)" copy /Y "$(RustExtensionPdb)" "$(TargetPdb)" >NUL + if exist "$(TargetPath)" del /F /Q "$(TargetPath)" +if exist "$(TargetPdb)" del /F /Q "$(TargetPdb)" +if exist "$(CargoTargetDirNoSlash)" rmdir /S /Q "$(CargoTargetDirNoSlash)" + $(TargetPath) + + + + + + + + + {CF7AC3D1-E2DF-41D2-BEA6-1E2556CDEA26} + false + + + + + + + + + + diff --git a/PCbuild/_base64.vcxproj.filters b/PCbuild/_base64.vcxproj.filters new file mode 100644 index 00000000000000..0ba48e32c44518 --- /dev/null +++ b/PCbuild/_base64.vcxproj.filters @@ -0,0 +1,19 @@ + + + + + {86A93473-99A3-4B0B-9B78-4A09FF3A0A6F} + + + + + Rust Sources + + + Rust Sources + + + Rust Sources + + + diff --git a/PCbuild/build.bat b/PCbuild/build.bat index 602357048867d6..43183fe06132cb 100644 --- a/PCbuild/build.bat +++ b/PCbuild/build.bat @@ -111,7 +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 "%IncludeLLVM%"=="" if "%UseJIT%"=="true" (set IncludeLLVM=true) else (set IncludeLLVM=false) if "%IncludeExternals%"=="true" call "%dir%get_externals.bat" diff --git a/PCbuild/pcbuild.proj b/PCbuild/pcbuild.proj index 7a5327bf016cea..97a4e952e221b6 100644 --- a/PCbuild/pcbuild.proj +++ b/PCbuild/pcbuild.proj @@ -66,7 +66,7 @@ - + diff --git a/PCbuild/pcbuild.sln b/PCbuild/pcbuild.sln index 7296ea75301157..2f441dd47eb9fe 100644 --- a/PCbuild/pcbuild.sln +++ b/PCbuild/pcbuild.sln @@ -23,6 +23,7 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "python", "python.vcxproj", {28B5D777-DDF2-4B6B-B34F-31D938813856} = {28B5D777-DDF2-4B6B-B34F-31D938813856} {36D0C52C-DF4E-45D0-8BC7-E294C3ABC781} = {36D0C52C-DF4E-45D0-8BC7-E294C3ABC781} {384C224A-7474-476E-A01B-750EA7DE918C} = {384C224A-7474-476E-A01B-750EA7DE918C} + {D7B7A122-80A0-4AFB-9C06-18C29B3178E4} = {D7B7A122-80A0-4AFB-9C06-18C29B3178E4} {447F05A8-F581-4CAC-A466-5AC7936E207E} = {447F05A8-F581-4CAC-A466-5AC7936E207E} {4946ECAC-2E69-4BF8-A90A-F5136F5094DF} = {4946ECAC-2E69-4BF8-A90A-F5136F5094DF} {494BAC80-A60C-43A9-99E7-ACB691CE2C4D} = {494BAC80-A60C-43A9-99E7-ACB691CE2C4D} @@ -134,6 +135,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "_testconsole", "_testconsol EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "_asyncio", "_asyncio.vcxproj", "{384C224A-7474-476E-A01B-750EA7DE918C}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "_base64", "_base64.vcxproj", "{D7B7A122-80A0-4AFB-9C06-18C29B3178E4}" +EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "_zoneinfo", "_zoneinfo.vcxproj", "{FCBE1EF2-E0F0-40B1-88B5-00A35D378742}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "_queue", "_queue.vcxproj", "{78D80A15-BD8C-44E2-B49E-1F05B0A0A687}" @@ -1352,6 +1355,38 @@ Global {384C224A-7474-476E-A01B-750EA7DE918C}.Release|Win32.Build.0 = Release|Win32 {384C224A-7474-476E-A01B-750EA7DE918C}.Release|x64.ActiveCfg = Release|x64 {384C224A-7474-476E-A01B-750EA7DE918C}.Release|x64.Build.0 = Release|x64 + {D7B7A122-80A0-4AFB-9C06-18C29B3178E4}.Debug|ARM.ActiveCfg = Debug|ARM + {D7B7A122-80A0-4AFB-9C06-18C29B3178E4}.Debug|ARM.Build.0 = Debug|ARM + {D7B7A122-80A0-4AFB-9C06-18C29B3178E4}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {D7B7A122-80A0-4AFB-9C06-18C29B3178E4}.Debug|ARM64.Build.0 = Debug|ARM64 + {D7B7A122-80A0-4AFB-9C06-18C29B3178E4}.Debug|Win32.ActiveCfg = Debug|Win32 + {D7B7A122-80A0-4AFB-9C06-18C29B3178E4}.Debug|Win32.Build.0 = Debug|Win32 + {D7B7A122-80A0-4AFB-9C06-18C29B3178E4}.Debug|x64.ActiveCfg = Debug|x64 + {D7B7A122-80A0-4AFB-9C06-18C29B3178E4}.Debug|x64.Build.0 = Debug|x64 + {D7B7A122-80A0-4AFB-9C06-18C29B3178E4}.PGInstrument|ARM.ActiveCfg = PGInstrument|ARM + {D7B7A122-80A0-4AFB-9C06-18C29B3178E4}.PGInstrument|ARM.Build.0 = PGInstrument|ARM + {D7B7A122-80A0-4AFB-9C06-18C29B3178E4}.PGInstrument|ARM64.ActiveCfg = PGInstrument|ARM64 + {D7B7A122-80A0-4AFB-9C06-18C29B3178E4}.PGInstrument|ARM64.Build.0 = PGInstrument|ARM64 + {D7B7A122-80A0-4AFB-9C06-18C29B3178E4}.PGInstrument|Win32.ActiveCfg = PGInstrument|Win32 + {D7B7A122-80A0-4AFB-9C06-18C29B3178E4}.PGInstrument|Win32.Build.0 = PGInstrument|Win32 + {D7B7A122-80A0-4AFB-9C06-18C29B3178E4}.PGInstrument|x64.ActiveCfg = PGInstrument|x64 + {D7B7A122-80A0-4AFB-9C06-18C29B3178E4}.PGInstrument|x64.Build.0 = PGInstrument|x64 + {D7B7A122-80A0-4AFB-9C06-18C29B3178E4}.PGUpdate|ARM.ActiveCfg = PGUpdate|ARM + {D7B7A122-80A0-4AFB-9C06-18C29B3178E4}.PGUpdate|ARM.Build.0 = PGUpdate|ARM + {D7B7A122-80A0-4AFB-9C06-18C29B3178E4}.PGUpdate|ARM64.ActiveCfg = PGUpdate|ARM64 + {D7B7A122-80A0-4AFB-9C06-18C29B3178E4}.PGUpdate|ARM64.Build.0 = PGUpdate|ARM64 + {D7B7A122-80A0-4AFB-9C06-18C29B3178E4}.PGUpdate|Win32.ActiveCfg = PGUpdate|Win32 + {D7B7A122-80A0-4AFB-9C06-18C29B3178E4}.PGUpdate|Win32.Build.0 = PGUpdate|Win32 + {D7B7A122-80A0-4AFB-9C06-18C29B3178E4}.PGUpdate|x64.ActiveCfg = PGUpdate|x64 + {D7B7A122-80A0-4AFB-9C06-18C29B3178E4}.PGUpdate|x64.Build.0 = PGUpdate|x64 + {D7B7A122-80A0-4AFB-9C06-18C29B3178E4}.Release|ARM.ActiveCfg = Release|ARM + {D7B7A122-80A0-4AFB-9C06-18C29B3178E4}.Release|ARM.Build.0 = Release|ARM + {D7B7A122-80A0-4AFB-9C06-18C29B3178E4}.Release|ARM64.ActiveCfg = Release|ARM64 + {D7B7A122-80A0-4AFB-9C06-18C29B3178E4}.Release|ARM64.Build.0 = Release|ARM64 + {D7B7A122-80A0-4AFB-9C06-18C29B3178E4}.Release|Win32.ActiveCfg = Release|Win32 + {D7B7A122-80A0-4AFB-9C06-18C29B3178E4}.Release|Win32.Build.0 = Release|Win32 + {D7B7A122-80A0-4AFB-9C06-18C29B3178E4}.Release|x64.ActiveCfg = Release|x64 + {D7B7A122-80A0-4AFB-9C06-18C29B3178E4}.Release|x64.Build.0 = Release|x64 {FCBE1EF2-E0F0-40B1-88B5-00A35D378742}.Debug|ARM.ActiveCfg = Debug|ARM {FCBE1EF2-E0F0-40B1-88B5-00A35D378742}.Debug|ARM.Build.0 = Debug|ARM {FCBE1EF2-E0F0-40B1-88B5-00A35D378742}.Debug|ARM64.ActiveCfg = Debug|ARM64 diff --git a/PCbuild/readme.txt b/PCbuild/readme.txt index 27c0d382281bdb..18ee141e7f8b8d 100644 --- a/PCbuild/readme.txt +++ b/PCbuild/readme.txt @@ -171,6 +171,7 @@ The following sub-projects are for individual modules of the standard library which are implemented in C; each one builds a DLL (renamed to .pyd) of the same name as the project: * _asyncio + * _base64 * _ctypes * _ctypes_test * _decimal diff --git a/Tools/msi/freethreaded/freethreaded_files.wxs b/Tools/msi/freethreaded/freethreaded_files.wxs index 0707e77b5e9ab2..67ec4f911e2683 100644 --- a/Tools/msi/freethreaded/freethreaded_files.wxs +++ b/Tools/msi/freethreaded/freethreaded_files.wxs @@ -103,7 +103,7 @@ - + diff --git a/Tools/msi/lib/lib_files.wxs b/Tools/msi/lib/lib_files.wxs index 4d44299f783909..dfb10f47c85398 100644 --- a/Tools/msi/lib/lib_files.wxs +++ b/Tools/msi/lib/lib_files.wxs @@ -1,6 +1,6 @@  - +