Skip to content

Commit 12269e0

Browse files
committed
Lazy base JLLs
This implements lazy libraries for the three potentially-lazy libraries needed by Base, `PCRE2_jll`, `GMP_jll` and `MPFR_jll`. Because these libraries are needed by bootstrap (and in the case of `PCRE2`, needed by Julia's own initialization) we have to go through some extra work to get them to properly load lazily. We move the `LazyLibraryPath` definition to be much earlier in the bootstrap process and use this path to load e.g. `GMP` and `MPFR` during bootstrap, but then replace that path with a `LazyLibrary` after bootstrap has finished. This provides a mechanism for loading things before e.g. `dlopen()` has been defined.
1 parent 30dfa21 commit 12269e0

File tree

10 files changed

+355
-340
lines changed

10 files changed

+355
-340
lines changed

base/Base.jl

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -291,6 +291,7 @@ include("iobuffer.jl")
291291
# strings & printing
292292
include("intfuncs.jl")
293293
include("strings/strings.jl")
294+
include("base_jll_adapters.jl")
294295
include("regex.jl")
295296
include("parse.jl")
296297
include("shell.jl")
@@ -497,6 +498,9 @@ include(mapexpr::Function, mod::Module, _path::AbstractString) = _include(mapexp
497498
Core.println("JuliaSyntax/src/JuliaSyntax.jl")
498499
include(@__MODULE__, "JuliaSyntax/src/JuliaSyntax.jl")
499500

501+
# Finish up by inserting lazy libraries for the JLLS that must be loaded during bootstrap
502+
include(@__MODULE__, "base_jll_insertion.jl")
503+
500504
end_base_include = time_ns()
501505

502506
const _sysimage_modules = PkgId[]

base/base_jll_adapters.jl

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
# This file serves as a way to provide access to libraries that Base needs
2+
# that are usually included as artifacts. We can't use Artifacts this early
3+
# becuase we haven't bootstrapped far enough, so this file contains "Base"
4+
# JLLs that are manually adapted to load things from hardcoded paths so that
5+
# we can bootstrap, but once GMP_jll, MPFR_jll, etc... are loaded, we replace
6+
# the definitions here with the `LazyLibrary` objects defined there, as they
7+
# may be overloaded by Preferences and refer to a different library than we
8+
# would have used during bootstrap.
9+
module JLLAdapters
10+
11+
# We're early enough that we don't have access to `Sys.iswindows()`, etc...
12+
const UNAME = ccall(:jl_get_UNAME, Any, ())::Symbol
13+
const early_pathsep = (UNAME === :Windows || UNAME === :NT) ? "\\" : "/"
14+
15+
function early_joinpath(pieces...)
16+
result = pieces[1]
17+
for piece in pieces[2:end]
18+
result = string(result, early_pathsep, piece)
19+
end
20+
return result
21+
end
22+
23+
"""
24+
LazyLibraryPath
25+
26+
Helper type for lazily constructed library paths for use with `LazyLibrary`.
27+
Arguments are passed to `joinpath()`. Arguments must be able to have
28+
`string()` called on them.
29+
30+
```
31+
libfoo = LazyLibrary(LazyLibraryPath(prefix, "lib/libfoo.so.1.2.3"))
32+
```
33+
"""
34+
struct LazyLibraryPath
35+
pieces::Vector
36+
LazyLibraryPath(pieces::Vector) = new(pieces)
37+
end
38+
LazyLibraryPath(args...) = LazyLibraryPath(collect(args))
39+
Base.string(llp::LazyLibraryPath) = early_joinpath([string(p) for p in llp.pieces]...)
40+
Base.cconvert(::Type{Cstring}, llp::LazyLibraryPath) = Base.cconvert(Cstring, string(llp))
41+
# Define `print` so that we can wrap this in a `LazyString`
42+
Base.print(io::IO, llp::LazyLibraryPath) = print(io, string(llp))
43+
44+
# Helper to get `Sys.BINDIR` at runtime
45+
struct SysBindirGetter; end
46+
Base.string(::SysBindirGetter) = string(ccall(:jl_get_julia_bindir, Any, ())::String, early_pathsep, "..")
47+
48+
"""
49+
BundledLazyLibraryPath
50+
51+
Helper type for lazily constructed library paths that are stored within the
52+
bundled Julia distribution, primarily for use by Base modules.
53+
54+
```
55+
libfoo = LazyLibrary(BundledLazyLibraryPath("lib/libfoo.so.1.2.3"))
56+
```
57+
"""
58+
BundledLazyLibraryPath(subpath) = LazyLibraryPath(SysBindirGetter(), subpath)
59+
60+
# PCRE
61+
if (UNAME === :Windows || UNAME === :NT)
62+
const libpcre2_8_name = "bin/libpcre2-8-0.dll"
63+
elseif (UNAME === :Apple || UNAME === :Darwin)
64+
const libpcre2_8_name = "lib/libpcre2-8.0.dylib"
65+
else
66+
const libpcre2_8_name = "lib/libpcre2-8.so.0"
67+
end
68+
69+
const libpcre2_8 = Ref{Any}(BundledLazyLibraryPath(libpcre2_8_name))
70+
function get_libpcre2_8()
71+
if isa(libpcre2_8[], LazyLibraryPath)
72+
return string(libpcre2_8[])
73+
end
74+
return libpcre2_8[]
75+
end
76+
77+
# GMP
78+
if (UNAME === :Windows || UNAME === :NT)
79+
const libgmp_name = "bin/libgmp-10.dll"
80+
elseif (UNAME === :Apple || UNAME === :Darwin)
81+
const libgmp_name = "lib/libgmp.10.dylib"
82+
else
83+
const libgmp_name = "lib/libgmp.so.10"
84+
end
85+
const libgmp = Ref{Any}(BundledLazyLibraryPath(libgmp_name))
86+
function get_libgmp()
87+
if isa(libgmp[], LazyLibraryPath)
88+
return string(libgmp[])
89+
end
90+
return libgmp[]
91+
end
92+
93+
94+
# MPFR
95+
if (UNAME === :Windows || UNAME === :NT)
96+
const libmpfr_name = "bin/libmpfr-6.dll"
97+
elseif (UNAME === :Apple || UNAME === :Darwin)
98+
const libmpfr_name = "lib/libmpfr.6.dylib"
99+
else
100+
const libmpfr_name = "lib/libmpfr.so.6"
101+
end
102+
const libmpfr = Ref{Any}(BundledLazyLibraryPath(libmpfr_name))
103+
function get_libmpfr()
104+
# Work around early bootstrap problems where we need to load `libgmp`
105+
# when `libmpfr` is loaded. This only works if we're far enough along
106+
# in bootstrap to be able to call `dlopen()`! Later, `libmpfr[]`
107+
# is going to return a `LazyLibrary` that will have a dependency on
108+
# `libgmp[]`.
109+
if isa(libmpfr[], LazyLibraryPath)
110+
Base.Libc.Libdl.dlopen(get_libgmp())
111+
return string(libmpfr[])
112+
end
113+
return libmpfr[]
114+
end
115+
116+
117+
end # module JLLAdapters

base/base_jll_insertion.jl

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# Insert LazyLibrary for `libpcre`, although at this time this gets lazily
2+
# on startup because `include()` calls `isdirpath()` which has a regex in it.
3+
using Base.JLLAdapters: libpcre2_8, libpcre2_8_name
4+
using Base.Libc.Libdl: BundledLazyLibraryPath, LazyLibrary
5+
libpcre2_8[] = LazyLibrary(BundledLazyLibraryPath(libpcre2_8_name))
6+
7+
8+
# Insert LazyLibrary for `libgmp`
9+
using Base.JLLAdapters: libgmp, libgmp_name
10+
using Base.Libc.Libdl: BundledLazyLibraryPath, LazyLibrary
11+
using Base.GMP: libgmp_init
12+
libgmp[] = LazyLibrary(BundledLazyLibraryPath(libgmp_name); on_load_callback=libgmp_init)
13+
14+
# Insert LazyLibrary for `libmpfr`
15+
using Base.JLLAdapters: libgmp, libmpfr, libmpfr_name
16+
using Base.Libc.Libdl: BundledLazyLibraryPath, LazyLibrary
17+
using Base.MPFR: libmpfr_init
18+
libmpfr[] = LazyLibrary(BundledLazyLibraryPath(libmpfr_name); dependencies=[libgmp[]], on_load_callback=libmpfr_init)

0 commit comments

Comments
 (0)