Skip to content

Commit c3fa21f

Browse files
committed
windows: link _ctypes to libffi in static builds
CPython 3.8 moved to an out-of-tree libffi. As far as I can tell, our first official Python 3.8 static builds did not handle libffi correctly. In 3.8 and 3.9 builds, we hacked up the Python project to link against libffi.lib. This ensured the symbols were present at link time. However, we didn't ship libffi.lib and the dependency of _ctypes on libffi.lib wasn't captured in PYTHON.json. If we attempted to produce a new link library from the object files + static libraries (such as what PyOxidizer does), we'd get a linker error for missing libffi symbols. This commit attempts to fix matters. We now explicitly link the _ctypes extension to libffi.lib via project files hackery. This dependency is captured in the metadata so the link dependency is present in PYTHON.json. We also need to manually copy libffi.lib into the build directory so it is available. Finally, we undo the dependency of pythoncore on libffi because I now believe it is redundant with _ctypes. Although I'm not 100% confident about this. This should hopefully fix indygreg/PyOxidizer#360.
1 parent 4f5efbc commit c3fa21f

File tree

2 files changed

+34
-27
lines changed

2 files changed

+34
-27
lines changed

cpython-windows/build.py

Lines changed: 12 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,10 @@
4747
"allow_missing_preprocessor": True
4848
},
4949
"_bz2": {},
50-
"_ctypes": {"shared_depends": ["libffi-7"]},
50+
"_ctypes": {
51+
"shared_depends": ["libffi-7"],
52+
"static_depends_no_project": ["libffi"],
53+
},
5154
"_decimal": {},
5255
"_elementtree": {},
5356
"_hashlib": {
@@ -775,6 +778,7 @@ def hack_props(
775778
)
776779

777780
# We need to copy linking settings for dynamic libraries to static libraries.
781+
copy_link_to_lib(pcbuild_path / "libffi.props")
778782
copy_link_to_lib(pcbuild_path / "openssl.props")
779783

780784
# We should look against the static library variants.
@@ -892,32 +896,6 @@ def hack_project_files(
892896

893897
pythoncore_proj = pcbuild_path / "pythoncore.vcxproj"
894898

895-
# normally the _ctypes extension/project pulls in libffi via
896-
# <Link><AdditionalDependencies>. However, as part of converting this
897-
# extension to static, we lose the transitive dependency. Here, we
898-
# hack pythoncore as a one-off to add the dependency. Ideally we would
899-
# handle this when hacking the extension's project. But it is easier to
900-
# do here.
901-
if static:
902-
libffi_path = td / "libffi" / "libffi.lib"
903-
try:
904-
# Python 3.9 version
905-
static_replace_in_file(
906-
pythoncore_proj,
907-
b"<AdditionalDependencies>version.lib;shlwapi.lib;ws2_32.lib;pathcch.lib;%(AdditionalDependencies)</AdditionalDependencies>",
908-
b"<AdditionalDependencies>version.lib;shlwapi.lib;ws2_32.lib;pathcch.lib;"
909-
+ bytes(libffi_path)
910-
+ b";%(AdditionalDependencies)</AdditionalDependencies>",
911-
)
912-
except NoSearchStringError:
913-
static_replace_in_file(
914-
pythoncore_proj,
915-
b"<AdditionalDependencies>version.lib;shlwapi.lib;ws2_32.lib;%(AdditionalDependencies)</AdditionalDependencies>",
916-
b"<AdditionalDependencies>version.lib;shlwapi.lib;ws2_32.lib;"
917-
+ bytes(libffi_path)
918-
+ b";%(AdditionalDependencies)</AdditionalDependencies>",
919-
)
920-
921899
if static:
922900
for extension, entry in sorted(CONVERT_TO_BUILTIN_EXTENSIONS.items()):
923901
if entry.get("ignore_static"):
@@ -2170,6 +2148,13 @@ def build_cpython(
21702148
for record in entries:
21712149
record["required"] = extension in REQUIRED_EXTENSIONS
21722150

2151+
# Copy libffi static library as a one-off.
2152+
if static:
2153+
source = td / "libffi" / "libffi.lib"
2154+
dest = out_dir / "python" / "build" / "lib" / "libffi.lib"
2155+
log("copying %s to %s" % (source, dest))
2156+
shutil.copyfile(source, dest)
2157+
21732158
# Copy OpenSSL libraries as a one-off.
21742159
for lib in ("crypto", "ssl"):
21752160
if static:

src/main.rs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -359,6 +359,19 @@ const MACHO_BANNED_SYMBOLS_NON_AARCH64: &[&str] = &[
359359
"_preadv", "_pwritev",
360360
];
361361

362+
static WANTED_WINDOWS_STATIC_PATHS: Lazy<BTreeSet<PathBuf>> = Lazy::new(|| {
363+
[
364+
PathBuf::from("python/build/lib/libffi.lib"),
365+
PathBuf::from("python/build/lib/libcrypto_static.lib"),
366+
PathBuf::from("python/build/lib/liblzma.lib"),
367+
PathBuf::from("python/build/lib/libssl_static.lib"),
368+
PathBuf::from("python/build/lib/sqlite3.lib"),
369+
]
370+
.iter()
371+
.cloned()
372+
.collect()
373+
});
374+
362375
fn allowed_dylibs_for_triple(triple: &str) -> Vec<MachOAllowedDylib> {
363376
match triple {
364377
"aarch64-apple-darwin" => DARWIN_ALLOWED_DYLIBS.clone(),
@@ -563,6 +576,7 @@ fn validate_json(json: &PythonJsonMain, triple: &str) -> Result<Vec<String>> {
563576
fn validate_distribution(dist_path: &Path) -> Result<Vec<String>> {
564577
let mut errors = vec![];
565578
let mut seen_dylibs = BTreeSet::new();
579+
let mut seen_paths = BTreeSet::new();
566580

567581
let fh = std::fs::File::open(&dist_path)
568582
.with_context(|| format!("unable to open {}", dist_path.display()))?;
@@ -589,6 +603,8 @@ fn validate_distribution(dist_path: &Path) -> Result<Vec<String>> {
589603
let mut entry = entry.map_err(|e| anyhow!("failed to iterate over archive: {}", e))?;
590604
let path = entry.path()?.to_path_buf();
591605

606+
seen_paths.insert(path.clone());
607+
592608
let mut data = Vec::new();
593609
entry.read_to_end(&mut data)?;
594610

@@ -642,6 +658,12 @@ fn validate_distribution(dist_path: &Path) -> Result<Vec<String>> {
642658
errors.push(format!("required library dependency {} not seen", lib));
643659
}
644660

661+
if triple.contains("-windows-") && dist_path.to_string_lossy().contains("-static-") {
662+
for path in WANTED_WINDOWS_STATIC_PATHS.difference(&seen_paths) {
663+
errors.push(format!("required path {} not seen", path.display()));
664+
}
665+
}
666+
645667
Ok(errors)
646668
}
647669

0 commit comments

Comments
 (0)