Skip to content

Commit 6db8992

Browse files
authored
Bundle dynamically selected libstdc++ (#853)
1 parent f477bd0 commit 6db8992

File tree

1 file changed

+71
-2
lines changed

1 file changed

+71
-2
lines changed

src/PackageCompiler.jl

Lines changed: 71 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1193,6 +1193,54 @@ function glob_pattern_lib(lib)
11931193
error("unknown os")
11941194
end
11951195

1196+
# Take `path` to a libstdc++ library and return aliases for shared library symlinks:
1197+
# - Unix:
1198+
# `path/to/libstdc++.so.6.0.30` will yield aliases
1199+
# `libstdc++.so.6.0.30`, `libstdc++.so.6`, `libstdc++.so`
1200+
# - Apple:
1201+
# `path/to/libstdc++.6.0.30.dylib` will yield aliases
1202+
# `libstdc++.6.0.30.dylib`, `libstdc++.6.dylib`, `libstdc++.dylib`
1203+
# - Windows:
1204+
# `path/to/libstdc++-6.0.30.dll` will yield aliases
1205+
# `libstdc++-6.0.30.dll`, `libstdc++-6.dll`, `libstdc++.dll`
1206+
# If the major version cannot be inferred from the filename, return only full name and the
1207+
# alias without any version information.
1208+
function get_libstdcxx_aliases(path)
1209+
# Extract library name and version from path
1210+
os = if Sys.iswindows()
1211+
"windows"
1212+
elseif Sys.isapple()
1213+
"macos"
1214+
elseif Sys.isunix()
1215+
"unix"
1216+
else
1217+
error("unable to determine aliases; system is not Windows, macOS, or UNIX")
1218+
end
1219+
libname, version = Base.BinaryPlatforms.parse_dl_name_version(path, os)
1220+
1221+
# Always add full filename and base library name to list of aliases
1222+
ext = Libdl.dlext
1223+
aliases = String[basename(path), "$libname.$ext"]
1224+
1225+
# If version exists, also add alias for major version
1226+
if !isnothing(version)
1227+
major = version.major
1228+
if Sys.iswindows()
1229+
# libname-major.dll
1230+
push!(aliases, "$libname-$major.$ext")
1231+
elseif Sys.isapple()
1232+
# libname.major.dylib
1233+
push!(aliases, "$libname.$major.$ext")
1234+
else
1235+
# libname.so.major
1236+
push!(aliases, "$libname.$ext.$major")
1237+
end
1238+
end
1239+
1240+
# Return unique list in case, e.g., filename is identical to base library name
1241+
return unique(aliases)
1242+
end
1243+
11961244
# TODO: Detangle printing from business logic
11971245
function bundle_julia_libraries(dest_dir, stdlibs)
11981246
app_lib_dir = joinpath(dest_dir, Sys.isunix() ? "lib" : "bin")
@@ -1210,11 +1258,22 @@ function bundle_julia_libraries(dest_dir, stdlibs)
12101258
tot_libsize = 0
12111259
printstyled("PackageCompiler: bundled libraries:\n")
12121260

1213-
# Reqiored libraries
1261+
# Bundle the libstdc++ that is actually loaded by Julia
1262+
# xref: https://discourse.julialang.org/t/precedence-of-local-and-julia-shipped-shared-libraries/104258?u=sloede
1263+
libstdcxx = filter(contains("libstdc++"), Libdl.dllist())
1264+
# Load libstdc++ if not yet loaded to figure out which one Julia would load
1265+
if isempty(libstdcxx)
1266+
Libdl.dlopen("libstdc++")
1267+
libstdcxx = filter(contains("libstdc++"), Libdl.dllist())
1268+
end
1269+
# Resolve symbolic links
1270+
libstdcxx = map(realpath abspath, libstdcxx)
1271+
1272+
# Required libraries
12141273
println(" ├── Base:")
12151274
os = Sys.islinux() ? "linux" : Sys.isapple() ? "mac" : "windows"
12161275
for lib in required_libraries[os]
1217-
matches = glob(glob_pattern_lib(lib), libjulia_dir)
1276+
matches = (lib == "libstdc++") ? libstdcxx : glob(glob_pattern_lib(lib), libjulia_dir)
12181277
for match in matches
12191278
dest = joinpath(app_libjulia_dir, basename(match))
12201279
isfile(dest) && continue
@@ -1228,6 +1287,16 @@ function bundle_julia_libraries(dest_dir, stdlibs)
12281287
end
12291288
end
12301289

1290+
# For libstdc++, add additional symlinks since above only the library itself was copied
1291+
libstdcxx_path = first(libstdcxx)
1292+
for alias in get_libstdcxx_aliases(libstdcxx_path)
1293+
link = joinpath(app_libjulia_dir, alias)
1294+
if isfile(link) || islink(link)
1295+
continue
1296+
end
1297+
symlink(basename(libstdcxx_path), link)
1298+
end
1299+
12311300
major, minor, patch = VERSION.major, VERSION.minor, VERSION.patch
12321301
r = if Sys.isapple()
12331302
Regex("^libjulia(\\.$major(\\.$minor(\\.$patch)?)?)?\\.dylib\$")

0 commit comments

Comments
 (0)