Skip to content

Commit

Permalink
Lazy base JLLs
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
staticfloat committed Jul 28, 2023
1 parent 30dfa21 commit 12269e0
Show file tree
Hide file tree
Showing 10 changed files with 355 additions and 340 deletions.
4 changes: 4 additions & 0 deletions base/Base.jl
Original file line number Diff line number Diff line change
Expand Up @@ -291,6 +291,7 @@ include("iobuffer.jl")
# strings & printing
include("intfuncs.jl")
include("strings/strings.jl")
include("base_jll_adapters.jl")
include("regex.jl")
include("parse.jl")
include("shell.jl")
Expand Down Expand Up @@ -497,6 +498,9 @@ include(mapexpr::Function, mod::Module, _path::AbstractString) = _include(mapexp
Core.println("JuliaSyntax/src/JuliaSyntax.jl")
include(@__MODULE__, "JuliaSyntax/src/JuliaSyntax.jl")

# Finish up by inserting lazy libraries for the JLLS that must be loaded during bootstrap
include(@__MODULE__, "base_jll_insertion.jl")

end_base_include = time_ns()

const _sysimage_modules = PkgId[]
Expand Down
117 changes: 117 additions & 0 deletions base/base_jll_adapters.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
# This file serves as a way to provide access to libraries that Base needs
# that are usually included as artifacts. We can't use Artifacts this early
# becuase we haven't bootstrapped far enough, so this file contains "Base"
# JLLs that are manually adapted to load things from hardcoded paths so that
# we can bootstrap, but once GMP_jll, MPFR_jll, etc... are loaded, we replace
# the definitions here with the `LazyLibrary` objects defined there, as they
# may be overloaded by Preferences and refer to a different library than we
# would have used during bootstrap.
module JLLAdapters

# We're early enough that we don't have access to `Sys.iswindows()`, etc...
const UNAME = ccall(:jl_get_UNAME, Any, ())::Symbol
const early_pathsep = (UNAME === :Windows || UNAME === :NT) ? "\\" : "/"

function early_joinpath(pieces...)
result = pieces[1]
for piece in pieces[2:end]
result = string(result, early_pathsep, piece)
end
return result
end

"""
LazyLibraryPath
Helper type for lazily constructed library paths for use with `LazyLibrary`.
Arguments are passed to `joinpath()`. Arguments must be able to have
`string()` called on them.
```
libfoo = LazyLibrary(LazyLibraryPath(prefix, "lib/libfoo.so.1.2.3"))
```
"""
struct LazyLibraryPath
pieces::Vector
LazyLibraryPath(pieces::Vector) = new(pieces)
end
LazyLibraryPath(args...) = LazyLibraryPath(collect(args))
Base.string(llp::LazyLibraryPath) = early_joinpath([string(p) for p in llp.pieces]...)
Base.cconvert(::Type{Cstring}, llp::LazyLibraryPath) = Base.cconvert(Cstring, string(llp))
# Define `print` so that we can wrap this in a `LazyString`
Base.print(io::IO, llp::LazyLibraryPath) = print(io, string(llp))

# Helper to get `Sys.BINDIR` at runtime
struct SysBindirGetter; end
Base.string(::SysBindirGetter) = string(ccall(:jl_get_julia_bindir, Any, ())::String, early_pathsep, "..")

"""
BundledLazyLibraryPath
Helper type for lazily constructed library paths that are stored within the
bundled Julia distribution, primarily for use by Base modules.
```
libfoo = LazyLibrary(BundledLazyLibraryPath("lib/libfoo.so.1.2.3"))
```
"""
BundledLazyLibraryPath(subpath) = LazyLibraryPath(SysBindirGetter(), subpath)

# PCRE
if (UNAME === :Windows || UNAME === :NT)
const libpcre2_8_name = "bin/libpcre2-8-0.dll"
elseif (UNAME === :Apple || UNAME === :Darwin)
const libpcre2_8_name = "lib/libpcre2-8.0.dylib"
else
const libpcre2_8_name = "lib/libpcre2-8.so.0"
end

const libpcre2_8 = Ref{Any}(BundledLazyLibraryPath(libpcre2_8_name))
function get_libpcre2_8()
if isa(libpcre2_8[], LazyLibraryPath)
return string(libpcre2_8[])
end
return libpcre2_8[]
end

# GMP
if (UNAME === :Windows || UNAME === :NT)
const libgmp_name = "bin/libgmp-10.dll"
elseif (UNAME === :Apple || UNAME === :Darwin)
const libgmp_name = "lib/libgmp.10.dylib"
else
const libgmp_name = "lib/libgmp.so.10"
end
const libgmp = Ref{Any}(BundledLazyLibraryPath(libgmp_name))
function get_libgmp()
if isa(libgmp[], LazyLibraryPath)
return string(libgmp[])
end
return libgmp[]
end


# MPFR
if (UNAME === :Windows || UNAME === :NT)
const libmpfr_name = "bin/libmpfr-6.dll"
elseif (UNAME === :Apple || UNAME === :Darwin)
const libmpfr_name = "lib/libmpfr.6.dylib"
else
const libmpfr_name = "lib/libmpfr.so.6"
end
const libmpfr = Ref{Any}(BundledLazyLibraryPath(libmpfr_name))
function get_libmpfr()
# Work around early bootstrap problems where we need to load `libgmp`
# when `libmpfr` is loaded. This only works if we're far enough along
# in bootstrap to be able to call `dlopen()`! Later, `libmpfr[]`
# is going to return a `LazyLibrary` that will have a dependency on
# `libgmp[]`.
if isa(libmpfr[], LazyLibraryPath)
Base.Libc.Libdl.dlopen(get_libgmp())
return string(libmpfr[])
end
return libmpfr[]
end


end # module JLLAdapters
18 changes: 18 additions & 0 deletions base/base_jll_insertion.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Insert LazyLibrary for `libpcre`, although at this time this gets lazily
# on startup because `include()` calls `isdirpath()` which has a regex in it.
using Base.JLLAdapters: libpcre2_8, libpcre2_8_name
using Base.Libc.Libdl: BundledLazyLibraryPath, LazyLibrary
libpcre2_8[] = LazyLibrary(BundledLazyLibraryPath(libpcre2_8_name))


# Insert LazyLibrary for `libgmp`
using Base.JLLAdapters: libgmp, libgmp_name
using Base.Libc.Libdl: BundledLazyLibraryPath, LazyLibrary
using Base.GMP: libgmp_init
libgmp[] = LazyLibrary(BundledLazyLibraryPath(libgmp_name); on_load_callback=libgmp_init)

# Insert LazyLibrary for `libmpfr`
using Base.JLLAdapters: libgmp, libmpfr, libmpfr_name
using Base.Libc.Libdl: BundledLazyLibraryPath, LazyLibrary
using Base.MPFR: libmpfr_init
libmpfr[] = LazyLibrary(BundledLazyLibraryPath(libmpfr_name); dependencies=[libgmp[]], on_load_callback=libmpfr_init)
Loading

0 comments on commit 12269e0

Please sign in to comment.