Skip to content

Commit 7df0d9c

Browse files
committed
Showcase example of using LazyLibrary in shipped JLLs
This converts the following JLLs to LazyLibrary APIs: * LibCURL_jll * LibSSH2_jll * MbedTLS_jll * OpenBLAS_jll * libblastrampoline_jll * nghttp2_jll It also introduces a new precompilation statement for a basic matrix multiplciation to ensure that TTFMM (time to first matmul) remains excellent.
1 parent 943db02 commit 7df0d9c

File tree

10 files changed

+80
-234
lines changed

10 files changed

+80
-234
lines changed

contrib/generate_precompile.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@ f(x) = x03
8888
f(1,2)
8989
[][1]
9090
cd("complet_path\t\t$CTRL_C
91+
randn(10, 10) * randn(10, 10)
9192
"""
9293

9394
precompile_script = """

pkgimage.mk

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -81,11 +81,11 @@ $(eval $(call pkgimg_builder,GMP_jll,Artifacts Libdl))
8181
$(eval $(call pkgimg_builder,LLVMLibUnwind_jll,Artifacts Libdl))
8282
$(eval $(call pkgimg_builder,LibUV_jll,Artifacts Libdl))
8383
$(eval $(call pkgimg_builder,LibUnwind_jll,Artifacts Libdl))
84-
$(eval $(call pkgimg_builder,MbedTLS_jll,Artifacts Libdl))
85-
$(eval $(call pkgimg_builder,nghttp2_jll,Artifacts Libdl))
84+
$(eval $(call sysimg_builder,MbedTLS_jll,Artifacts Libdl))
85+
$(eval $(call sysimg_builder,nghttp2_jll,Artifacts Libdl))
8686
$(eval $(call pkgimg_builder,OpenLibm_jll,Artifacts Libdl))
8787
$(eval $(call pkgimg_builder,PCRE2_jll,Artifacts Libdl))
88-
$(eval $(call pkgimg_builder,Zlib_jll,Artifacts Libdl))
88+
$(eval $(call sysimg_builder,Zlib_jll,Artifacts Libdl))
8989
$(eval $(call pkgimg_builder,dSFMT_jll,Artifacts Libdl))
9090
$(eval $(call pkgimg_builder,libLLVM_jll,Artifacts Libdl))
9191
$(eval $(call sysimg_builder,libblastrampoline_jll,Artifacts Libdl))
@@ -98,7 +98,7 @@ $(eval $(call pkgimg_builder,DelimitedFiles,Mmap))
9898

9999
# 2-depth packages
100100
$(eval $(call pkgimg_builder,LLD_jll,Zlib_jll libLLVM_jll Artifacts Libdl))
101-
$(eval $(call pkgimg_builder,LibSSH2_jll,Artifacts Libdl MbedTLS_jll))
101+
$(eval $(call sysimg_builder,LibSSH2_jll,Artifacts Libdl MbedTLS_jll))
102102
$(eval $(call pkgimg_builder,MPFR_jll,Artifacts Libdl GMP_jll))
103103
$(eval $(call sysimg_builder,LinearAlgebra,Libdl libblastrampoline_jll OpenBLAS_jll))
104104
$(eval $(call sysimg_builder,Dates,Printf))
@@ -110,7 +110,7 @@ $(eval $(call sysimg_builder,UUIDs,Random SHA))
110110

111111
# 3-depth packages
112112
# LibGit2_jll
113-
$(eval $(call pkgimg_builder,LibCURL_jll,LibSSH2_jll nghttp2_jll MbedTLS_jll Zlib_jll Artifacts Libdl))
113+
$(eval $(call sysimg_builder,LibCURL_jll,LibSSH2_jll nghttp2_jll MbedTLS_jll Zlib_jll Artifacts Libdl))
114114
$(eval $(call sysimg_builder,REPL,InteractiveUtils Markdown Sockets Unicode))
115115
$(eval $(call pkgimg_builder,SharedArrays,Distributed Mmap Random Serialization))
116116
$(eval $(call sysimg_builder,TOML,Dates))

stdlib/LibCURL_jll/src/LibCURL_jll.jl

Lines changed: 9 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -3,44 +3,21 @@
33
## dummy stub for https://github.com/JuliaBinaryWrappers/LibCURL_jll.jl
44

55
baremodule LibCURL_jll
6-
using Base, Libdl, nghttp2_jll
7-
Base.Experimental.@compiler_options compile=min optimize=0 infer=false
8-
9-
const PATH_list = String[]
10-
const LIBPATH_list = String[]
11-
6+
using Base, Libdl, nghttp2_jll, LibSSH2_jll
127
export libcurl
138

14-
# These get calculated in __init__()
15-
const PATH = Ref("")
16-
const LIBPATH = Ref("")
17-
artifact_dir::String = ""
18-
libcurl_handle::Ptr{Cvoid} = C_NULL
19-
libcurl_path::String = ""
20-
219
if Sys.iswindows()
22-
const libcurl = "libcurl-4.dll"
10+
const libcurl_name = "bin/libcurl-4.dll"
2311
elseif Sys.isapple()
24-
const libcurl = "@rpath/libcurl.4.dylib"
12+
const libcurl_name = "lib/libcurl.4.dylib"
2513
else
26-
const libcurl = "libcurl.so.4"
27-
end
28-
29-
function __init__()
30-
global libcurl_handle = dlopen(libcurl)
31-
global libcurl_path = dlpath(libcurl_handle)
32-
global artifact_dir = dirname(Sys.BINDIR)
33-
LIBPATH[] = dirname(libcurl_path)
34-
push!(LIBPATH_list, LIBPATH[])
14+
const libcurl_name = "lib/libcurl.so.4"
3515
end
3616

37-
# JLLWrappers API compatibility shims. Note that not all of these will really make sense.
38-
# For instance, `find_artifact_dir()` won't actually be the artifact directory, because
39-
# there isn't one. It instead returns the overall Julia prefix.
40-
is_available() = true
41-
find_artifact_dir() = artifact_dir
42-
dev_jll() = error("stdlib JLLs cannot be dev'ed")
43-
best_wrapper = nothing
44-
get_libcurl_path() = libcurl_path
17+
const libcurl_path = BundledLazyLibraryPath(libcurl_name)
18+
const libcurl = LazyLibrary(libcurl_path; dependencies=[libssh2, libnghttp2],
19+
on_load_callback = () -> begin
20+
@assert ccall(dlsym(libcurl, :curl_global_init), UInt32, (Clong,), 0x03) == 0
21+
end) # eventually add libz
4522

4623
end # module LibCURL_jll

stdlib/LibSSH2_jll/src/LibSSH2_jll.jl

Lines changed: 5 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -4,44 +4,17 @@
44

55
baremodule LibSSH2_jll
66
using Base, Libdl, MbedTLS_jll
7-
Base.Experimental.@compiler_options compile=min optimize=0 infer=false
8-
9-
const PATH_list = String[]
10-
const LIBPATH_list = String[]
11-
127
export libssh2
138

14-
# These get calculated in __init__()
15-
const PATH = Ref("")
16-
const LIBPATH = Ref("")
17-
artifact_dir::String = ""
18-
libssh2_handle::Ptr{Cvoid} = C_NULL
19-
libssh2_path::String = ""
20-
219
if Sys.iswindows()
22-
const libssh2 = "libssh2.dll"
10+
const libssh2_name = "bin/libssh2.dll"
2311
elseif Sys.isapple()
24-
const libssh2 = "@rpath/libssh2.1.dylib"
12+
const libssh2_name = "lib/libssh2.1.dylib"
2513
else
26-
const libssh2 = "libssh2.so.1"
14+
const libssh2_name = "lib/libssh2.so.1"
2715
end
2816

29-
function __init__()
30-
global libssh2_handle = dlopen(libssh2)
31-
global libssh2_path = dlpath(libssh2_handle)
32-
global artifact_dir = dirname(Sys.BINDIR)
33-
LIBPATH[] = dirname(libssh2_path)
34-
push!(LIBPATH_list, LIBPATH[])
35-
end
36-
37-
38-
# JLLWrappers API compatibility shims. Note that not all of these will really make sense.
39-
# For instance, `find_artifact_dir()` won't actually be the artifact directory, because
40-
# there isn't one. It instead returns the overall Julia prefix.
41-
is_available() = true
42-
find_artifact_dir() = artifact_dir
43-
dev_jll() = error("stdlib JLLs cannot be dev'ed")
44-
best_wrapper = nothing
45-
get_libssh2_path() = libssh2_path
17+
const libssh2_path = BundledLazyLibraryPath(libssh2_name)
18+
const libssh2 = LazyLibrary(libssh2_path; dependencies=[libmbedx509, libmbedcrypto, libmbedtls])
4619

4720
end # module LibSSH2_jll

stdlib/LinearAlgebra/src/LinearAlgebra.jl

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -676,24 +676,29 @@ function versioninfo(io::IO=stdout)
676676
return nothing
677677
end
678678

679-
function __init__()
680-
try
681-
BLAS.lbt_forward(OpenBLAS_jll.libopenblas_path; clear=true)
682-
BLAS.check()
683-
catch ex
684-
Base.showerror_nostdio(ex, "WARNING: Error during initialization of module LinearAlgebra")
685-
end
679+
680+
function lbt_onload_callback()
681+
# We don't use `BLAS.lbt_forward()` here because we explicitly don't
682+
# want to take a lock on the config cache.
683+
ccall(Libdl.dlsym(libblastrampoline, :lbt_forward), Int32, (Cstring, Int32, Int32, Cstring),
684+
OpenBLAS_jll.libopenblas_path, 1, 0, C_NULL)
685+
686686
# register a hook to disable BLAS threading
687687
Base.at_disable_library_threading(() -> BLAS.set_num_threads(1))
688688

689689
# https://github.com/xianyi/OpenBLAS/blob/c43ec53bdd00d9423fc609d7b7ecb35e7bf41b85/README.md#setting-the-number-of-threads-using-environment-variables
690690
if !haskey(ENV, "OPENBLAS_NUM_THREADS") && !haskey(ENV, "GOTO_NUM_THREADS") && !haskey(ENV, "OMP_NUM_THREADS")
691691
@static if Sys.isapple() && Base.BinaryPlatforms.arch(Base.BinaryPlatforms.HostPlatform()) == "aarch64"
692-
BLAS.set_num_threads(max(1, Sys.CPU_THREADS))
692+
nthreads = max(1, Sys.CPU_THREADS)
693693
else
694-
BLAS.set_num_threads(max(1, Sys.CPU_THREADS ÷ 2))
694+
nthreads = max(1, Sys.CPU_THREADS ÷ 2)
695695
end
696+
ccall(Libdl.dlsym(libblastrampoline, :lbt_set_num_threads), Cvoid, (Int32,), nthreads)
696697
end
697698
end
698699

700+
# Force `libblastrampoline` to forward to `libopenblas` once it's loaded
701+
Libdl.add_dependency!(libblastrampoline, libopenblas)
702+
push!(libblastrampoline_jll.on_load_callbacks, lbt_onload_callback)
703+
699704
end # module LinearAlgebra

stdlib/MbedTLS_jll/src/MbedTLS_jll.jl

Lines changed: 16 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -4,59 +4,29 @@
44

55
baremodule MbedTLS_jll
66
using Base, Libdl
7-
Base.Experimental.@compiler_options compile=min optimize=0 infer=false
8-
9-
const PATH_list = String[]
10-
const LIBPATH_list = String[]
11-
127
export libmbedcrypto, libmbedtls, libmbedx509
138

14-
# These get calculated in __init__()
15-
const PATH = Ref("")
16-
const LIBPATH = Ref("")
17-
artifact_dir::String = ""
18-
libmbedcrypto_handle::Ptr{Cvoid} = C_NULL
19-
libmbedcrypto_path::String = ""
20-
libmbedtls_handle::Ptr{Cvoid} = C_NULL
21-
libmbedtls_path::String = ""
22-
libmbedx509_handle::Ptr{Cvoid} = C_NULL
23-
libmbedx509_path::String = ""
24-
259
if Sys.iswindows()
26-
const libmbedcrypto = "libmbedcrypto.dll"
27-
const libmbedtls = "libmbedtls.dll"
28-
const libmbedx509 = "libmbedx509.dll"
10+
const libmbedcrypto_name = "bin/libmbedcrypto.dll"
11+
const libmbedx509_name = "bin/libmbedx509.dll"
12+
const libmbedtls_name = "bin/libmbedtls.dll"
2913
elseif Sys.isapple()
30-
const libmbedcrypto = "@rpath/libmbedcrypto.7.dylib"
31-
const libmbedtls = "@rpath/libmbedtls.14.dylib"
32-
const libmbedx509 = "@rpath/libmbedx509.1.dylib"
14+
const libmbedcrypto_name = "lib/libmbedcrypto.7.dylib"
15+
const libmbedx509_name = "lib/libmbedx509.1.dylib"
16+
const libmbedtls_name = "lib/libmbedtls.14.dylib"
3317
else
34-
const libmbedcrypto = "libmbedcrypto.so.7"
35-
const libmbedtls = "libmbedtls.so.14"
36-
const libmbedx509 = "libmbedx509.so.1"
18+
const libmbedcrypto_name = "lib/libmbedcrypto.so.7"
19+
const libmbedx509_name = "lib/libmbedx509.so.1"
20+
const libmbedtls_name = "lib/libmbedtls.so.14"
3721
end
3822

39-
function __init__()
40-
global libmbedcrypto_handle = dlopen(libmbedcrypto)
41-
global libmbedcrypto_path = dlpath(libmbedcrypto_handle)
42-
global libmbedtls_handle = dlopen(libmbedtls)
43-
global libmbedtls_path = dlpath(libmbedtls_handle)
44-
global libmbedx509_handle = dlopen(libmbedx509)
45-
global libmbedx509_path = dlpath(libmbedx509_handle)
46-
global artifact_dir = dirname(Sys.BINDIR)
47-
LIBPATH[] = dirname(libmbedtls_path)
48-
push!(LIBPATH_list, LIBPATH[])
49-
end
23+
const libmbedcrypto_path = BundledLazyLibraryPath(libmbedcrypto_name)
24+
const libmbedtls_path = BundledLazyLibraryPath(libmbedtls_name)
25+
const libmbedx509_path = BundledLazyLibraryPath(libmbedx509_name)
5026

51-
# JLLWrappers API compatibility shims. Note that not all of these will really make sense.
52-
# For instance, `find_artifact_dir()` won't actually be the artifact directory, because
53-
# there isn't one. It instead returns the overall Julia prefix.
54-
is_available() = true
55-
find_artifact_dir() = artifact_dir
56-
dev_jll() = error("stdlib JLLs cannot be dev'ed")
57-
best_wrapper = nothing
58-
get_libmbedcrypto_path() =libmbedcrypto_path
59-
get_libmbedtls_path() = libmbedtls_path
60-
get_libmbedx509_path() = libmbedx509_path
27+
const libmbedcrypto = LazyLibrary(libmbedcrypto_path)
28+
const libmbedx509 = LazyLibrary(libmbedx509_path; dependencies=[libmbedcrypto])
29+
libmbedtls_stack = Any[]
30+
const libmbedtls = LazyLibrary(libmbedtls_path; dependencies=[libmbedcrypto, libmbedx509], on_load_callback = () -> push!(libmbedtls_stack, stacktrace()))
6131

6232
end # module MbedTLS_jll

stdlib/OpenBLAS_jll/src/OpenBLAS_jll.jl

Lines changed: 12 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -3,47 +3,31 @@
33
## dummy stub for https://github.com/JuliaBinaryWrappers/OpenBLAS_jll.jl
44
baremodule OpenBLAS_jll
55
using Base, Libdl, Base.BinaryPlatforms
6-
7-
# We are explicitly NOT loading this at runtime, as it contains `libgomp`
8-
# which conflicts with `libiomp5`, breaking things like MKL. In the future,
9-
# we hope to transition to a JLL interface that provides a more granular
10-
# interface than eagerly dlopen'ing all libraries provided in the JLL
11-
# which will eliminate issues like this, where we avoid loading a JLL
12-
# because we don't want to load a library that we don't even use yet.
13-
# using CompilerSupportLibraries_jll
14-
# Because of this however, we have to manually load the libraries we
15-
# _do_ care about, namely libgfortran
16-
Base.Experimental.@compiler_options compile=min optimize=0 infer=false
17-
18-
const PATH_list = String[]
19-
const LIBPATH_list = String[]
20-
216
export libopenblas
227

23-
# These get calculated in __init__()
24-
const PATH = Ref("")
25-
const LIBPATH = Ref("")
26-
artifact_dir::String = ""
27-
libopenblas_handle::Ptr{Cvoid} = C_NULL
28-
libopenblas_path::String = ""
29-
308
if Base.USE_BLAS64
319
const libsuffix = "64_"
3210
else
3311
const libsuffix = ""
3412
end
3513

3614
if Sys.iswindows()
37-
const libopenblas = "libopenblas$(libsuffix).dll"
38-
const _libgfortran = string("libgfortran-", libgfortran_version(HostPlatform()).major, ".dll")
15+
const libopenblas_name = "bin/libopenblas$(libsuffix).dll"
16+
const libgfortran_name = string("libgfortran-", libgfortran_version(HostPlatform()).major, ".dll")
3917
elseif Sys.isapple()
40-
const libopenblas = "@rpath/libopenblas$(libsuffix).dylib"
41-
const _libgfortran = string("@rpath/", "libgfortran.", libgfortran_version(HostPlatform()).major, ".dylib")
18+
const libopenblas_name = "lib/libopenblas$(libsuffix).dylib"
19+
const libgfortran_name = string("lib/libgfortran.", libgfortran_version(HostPlatform()).major, ".dylib")
4220
else
43-
const libopenblas = "libopenblas$(libsuffix).so"
44-
const _libgfortran = string("libgfortran.so.", libgfortran_version(HostPlatform()).major)
21+
const libopenblas_name = "lib/libopenblas$(libsuffix).so"
22+
const libgfortran_name = string("lib/libgfortran.so.", libgfortran_version(HostPlatform()).major)
4523
end
4624

25+
const libgfortran_path = BundledLazyLibraryPath(libgfortran_name)
26+
const libgfortran = LazyLibrary(libgfortran_path)
27+
28+
const libopenblas_path = BundledLazyLibraryPath(libopenblas_name)
29+
const libopenblas = LazyLibrary(libopenblas_path; dependencies=[libgfortran])
30+
4731
function __init__()
4832
# make sure OpenBLAS does not set CPU affinity (#1070, #9639)
4933
if !haskey(ENV, "OPENBLAS_MAIN_FREE")
@@ -62,25 +46,6 @@ function __init__()
6246
# to the true value in its `__init__()` function.
6347
ENV["OPENBLAS_DEFAULT_NUM_THREADS"] = "1"
6448
end
65-
66-
# As mentioned above, we are sneaking this in here so that we don't have to
67-
# depend on CSL_jll and load _all_ of its libraries.
68-
dlopen(_libgfortran)
69-
70-
global libopenblas_handle = dlopen(libopenblas)
71-
global libopenblas_path = dlpath(libopenblas_handle)
72-
global artifact_dir = dirname(Sys.BINDIR)
73-
LIBPATH[] = dirname(libopenblas_path)
74-
push!(LIBPATH_list, LIBPATH[])
7549
end
7650

77-
# JLLWrappers API compatibility shims. Note that not all of these will really make sense.
78-
# For instance, `find_artifact_dir()` won't actually be the artifact directory, because
79-
# there isn't one. It instead returns the overall Julia prefix.
80-
is_available() = true
81-
find_artifact_dir() = artifact_dir
82-
dev_jll() = error("stdlib JLLs cannot be dev'ed")
83-
best_wrapper = nothing
84-
get_libopenblas_path() = libopenblas_path
85-
8651
end # module OpenBLAS_jll

0 commit comments

Comments
 (0)