Skip to content

Commit 5047f00

Browse files
libstdcxx version-bounds (#243)
* add libstdcxx version-bounds option * Update src/cpython/context.jl Co-authored-by: Elliot Saba <staticfloat@gmail.com> * adapt to @staticfloat's changes * fixup * JULIA_CONDAPKG_LIBSTDCXX_VERSION_BOUND -> JULIA_PYTHONCALL_LIBSTDCXX_VERSION_BOUND * tweak look-up table for version mapping * handle the case where Julia is unable to discover the version of libstdc++ * fixup code comment Co-authored-by: Elliot Saba <staticfloat@gmail.com>
1 parent d3aa2e7 commit 5047f00

File tree

3 files changed

+76
-17
lines changed

3 files changed

+76
-17
lines changed

docs/src/pythoncall.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -285,6 +285,23 @@ into it. If you want to use a pre-existing Conda environment, see the previous s
285285
If `conda`, `mamba` or `micromamba` is not in your `PATH` you will also need to set
286286
`JULIA_CONDAPKG_EXE` to its path.
287287

288+
#### If you installed a newer version of libstdc++
289+
PythonCall injects a dependency to bound the allowed versions of the `libstdcxx-ng`
290+
Conda package. It finds the bound by runtime discovery of the libstdc++ version. To
291+
override this value, use:
292+
293+
```julia
294+
[PythonCall]
295+
ENV["JULIA_PYTHONCALL_LIBSTDCXX_VERSION_BOUND"] = ">=3.4,<=12"
296+
```
297+
298+
To figure out installed version, run
299+
```bash
300+
strings /path/to/julia/lib/julia/libstdc++.so.6 | grep GLIBCXX
301+
```
302+
Then look at <https://gcc.gnu.org/onlinedocs/gcc-12.1.0/libstdc++/manual/manual/abi.html>
303+
for the GCC version compatible with the GLIBCXX version.
304+
288305
## [Installing Python packages](@id python-deps)
289306

290307
Assuming you haven't [opted out](@ref pythoncall-config), PythonCall uses

src/cpython/context.jl

Lines changed: 53 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,54 @@ function _atpyexit()
3030
return
3131
end
3232

33+
# By default, ensure libstdc++ in the Conda environment is compatible with
34+
# the one linked in Julia. This is platform/version dependent, so needs to
35+
# occur at runtime.
36+
#
37+
# Allow the user to override the default. This is useful when the version
38+
# of libstdcxx linked in Julia is customized in the local installation of
39+
# Julia.
40+
#
41+
# To figure out cxx_version for a given Julia version, run
42+
# strings /path/to/julia/lib/julia/libstdc++.so.6 | grep GLIBCXX
43+
# then look at
44+
# https://gcc.gnu.org/onlinedocs/gcc-12.1.0/libstdc++/manual/manual/abi.html
45+
# for the highest GCC version compatible with the highest GLIBCXX version.
46+
function get_libstdcxx_version_bound()
47+
# This list comes from: https://gcc.gnu.org/onlinedocs/libstdc++/manual/abi.html
48+
# Start with GCC 4.8, as it's extremely difficult to build Julia with anything older
49+
vers_mapping = Dict(
50+
18 => v"4.8.0",
51+
19 => v"4.8.3",
52+
20 => v"4.9.0",
53+
21 => v"5.1.0",
54+
22 => v"6.1.0",
55+
23 => v"7.1.0",
56+
24 => v"7.2.0",
57+
25 => v"8.1.0",
58+
26 => v"9.1.0",
59+
27 => v"9.2.0",
60+
28 => v"9.5.0",
61+
29 => v"11.3.0",
62+
30 => v"12.2.0",
63+
31 => v"13.1.0",
64+
)
65+
# Get the libstdcxx version that is currently loaded in this Julia process
66+
loaded_libstdcxx_version = Base.BinaryPlatforms.detect_libstdcxx_version()
67+
68+
if loaded_libstdcxx_version !== nothing
69+
# Map it through to get a GCC version; if the version is unknown, we simply return
70+
# the highest GCC version we know about, which should be a fairly safe choice.
71+
max_version = get(vers_mapping, loaded_libstdcxx_version.patch, vers_mapping[maximum(keys(vers_mapping))])
72+
return get(ENV, "JULIA_PYTHONCALL_LIBSTDCXX_VERSION_BOUND", ">=3.4,<=$(max_version.major).$(max_version.minor)")
73+
elseif haskey(ENV, "JULIA_PYTHONCALL_LIBSTDCXX_VERSION_BOUND")
74+
return ENV["JULIA_PYTHONCALL_LIBSTDCXX_VERSION_BOUND"]
75+
else
76+
# Julia does not link against any version of libstdc++ known to Julia (e.g. using clang instead, or something not in the 3.4.x series)
77+
return nothing
78+
end
79+
end
80+
3381
function init_context()
3482

3583
CTX.is_embedded = haskey(ENV, "JULIA_PYTHONCALL_LIBPTR")
@@ -60,24 +108,12 @@ function init_context()
60108
exe_path::String
61109
else
62110
if Sys.islinux()
63-
# Ensure libstdc++ in the Conda environment is compatible with the one
64-
# linked in Julia. This is platform/version dependent, so needs to occur at
65-
# runtime.
66-
#
67-
# To figure out cxx_version for a given Julia version, run
68-
# strings /path/to/julia/lib/julia/libstdc++.so.6 | grep GLIBCXX
69-
# then look at
70-
# https://gcc.gnu.org/onlinedocs/gcc-12.1.0/libstdc++/manual/manual/abi.html
71-
# for the highest GCC version compatible with the highest GLIBCXX version.
72-
if Base.VERSION <= v"1.6.2"
73-
# GLIBCXX_3.4.26
74-
cxx_version = ">=3.4,<9.2"
75-
else
76-
# GLIBCXX_3.4.29
77-
# checked up to v1.8.0
78-
cxx_version = ">=3.4,<11.4"
111+
cxx_version = get_libstdcxx_version_bound()
112+
if cxx_version !== nothing
113+
CondaPkg.add("libstdcxx-ng", version=cxx_version, channel="conda-forge", temp=true, file=joinpath(@__DIR__, "..", "..", "CondaPkg.toml"), resolve=false)
79114
end
80-
CondaPkg.add("libstdcxx-ng", version=cxx_version, channel="conda-forge", temp=true, file=joinpath(@__DIR__, "..", "..", "CondaPkg.toml"), resolve=false)
115+
# if cxx_version is nothing, then we assume that Julia does not link against any version ob libstdc++ known by Julia, and so we do not
116+
# enforce a version bound.
81117
end
82118
# By default, we use Python installed by CondaPkg.
83119
exe_path = Sys.iswindows() ? joinpath(CondaPkg.envdir(), "python.exe") : joinpath(CondaPkg.envdir(), "bin", "python")

test/context.jl

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
@testitem "libstdc++ version" begin
2+
ENV["JULIA_PYTHONCALL_LIBSTDCXX_VERSION_BOUND"] = ">=3.4,<=12"
3+
4+
cxxversion = PythonCall.C.get_libstdcxx_version_bound()
5+
@test cxxversion == ">=3.4,<=12"
6+
end

0 commit comments

Comments
 (0)