-
Notifications
You must be signed in to change notification settings - Fork 32
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
First draft of improved installation experience
* Sneaks an `ENV["PATH"] = dirname(libmklrt)` into `Base.__init__()` to fix #6 * Adds an `MKL.enable_*_startup()` API, to enable re-setting to OpenBLAS
- Loading branch information
1 parent
76ab0ac
commit cb6b0ca
Showing
3 changed files
with
154 additions
and
30 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,148 @@ | ||
using PackageCompiler | ||
|
||
""" | ||
lineedit(editor::Function, filename::String) | ||
Easily edit a file, line by line. If your `editor` function returns `nothing` the | ||
file is not modified. Usage example: | ||
lineedit("foo.jl") do lines | ||
map(lines) do l | ||
if occursin(r"libblas_name", l) | ||
return "const libblas_name = \"libfoo.so\"\n" | ||
end | ||
return l | ||
end | ||
end | ||
""" | ||
function lineedit(editor::Function, filename::String) | ||
lines = open(filename) do io | ||
readlines(io, keep=true) | ||
end | ||
|
||
# Run user editor; if something goes wrong, just quit out | ||
try | ||
lines = editor(lines) | ||
catch e | ||
@error("Error occured while running user line editor:", e) | ||
return nothing | ||
end | ||
|
||
# Write it out, if the editor decides something needs to change | ||
if lines != nothing | ||
open(filename, "w") do io | ||
for l in lines | ||
write(io, l) | ||
end | ||
end | ||
end | ||
|
||
# Return the lines, just for fun | ||
return lines | ||
end | ||
|
||
function replace_libblas(base_dir, name) | ||
# Replace `libblas` and `liblapack` in build_h.jl | ||
lineedit(joinpath(base_dir, "build_h.jl")) do lines | ||
return map(lines) do l | ||
if occursin(r"libblas_name", l) | ||
return "const libblas_name = $(repr(name))\n" | ||
elseif occursin(r"liblapack_name", l) | ||
return "const liblapack_name = $(repr(name))\n" | ||
else | ||
return l | ||
end | ||
end | ||
end | ||
end | ||
|
||
""" | ||
find_init_func_bounds(lines) | ||
Given a list of `lines`, find the beginning and ending indicies within `lines` that | ||
contains the `__init__()` function, if any. If none can be found, returns `(nothing, nothing)`. | ||
""" | ||
function find_init_func_bounds(lines) | ||
start_idx = findfirst(match.(r"^function\s+__init__\(\)\s*$", lines) .!= nothing) | ||
if start_idx === nothing | ||
return (nothing, nothing) | ||
end | ||
|
||
func_len = findfirst(match.(r"^end\s*$", lines[start_idx+1:end]) .!= nothing) | ||
if func_len === nothing | ||
return (nothing, nothing) | ||
end | ||
return (start_idx, start_idx + func_len) | ||
end | ||
|
||
function force_proper_PATH(base_dir, dir_path) | ||
# We need to be compatible with Julia 1.0+, so we need to search for `__init__()` | ||
# within both `sysimg.jl` (for Julia 1.0 and 1.1) as well as `Base.jl` (for 1.2+) | ||
|
||
for file in ("sysimg.jl", "Base.jl") | ||
lineedit(joinpath(base_dir, file)) do lines | ||
# Find the start/end of `__init__()` within this file, if possible: | ||
init_start, init_end = find_init_func_bounds(lines) | ||
if init_start === nothing | ||
@info("Found init function in $(file)") | ||
return nothing | ||
end | ||
|
||
# Scan the function for mentions of our `dir_path`; if it already exists, | ||
# then call it good, returning `nothing` so the file is not modified. | ||
dir_path_regex = Regex(string("\\s+ENV\\[\"PATH\"\\] = .*$(dir_path).*")) | ||
if any(match.(dir_path_regex, lines[init_start+1:init_end-1])) | ||
@info("Found ENV already") | ||
return nothing | ||
end | ||
|
||
# If we found a function, insert our `ENV["PATH"]` mapping: | ||
pathsep = Sys.iswindows() ? ';' : ':' | ||
insert!( | ||
lines, | ||
init_start + 1, | ||
" ENV[\"PATH\"] = string(ENV[\"PATH\"], $(repr(pathsep)), $(repr(dir_path)))", | ||
) | ||
return lines | ||
end | ||
end | ||
end | ||
|
||
function enable_mkl_startup(libmkl_rt) | ||
# First, we need to modify a few files in Julia's base directory | ||
base_dir = joinpath(Sys.BINDIR, Base.DATAROOTDIR, "julia", "base") | ||
|
||
# Replace definitions of `libblas_name`, etc.. to point to MKL | ||
replace_libblas(base_dir, libmkl_rt) | ||
|
||
# Force-setting `ENV["PATH"]` to include the location of MKL libraries | ||
# This is required on Windows, where we can't use RPATH | ||
if Sys.iswindows() | ||
force_proper_PATH(base_dir, dirname(libmkl_rt)) | ||
end | ||
|
||
# Next, build a new system image | ||
sysimgpath = PackageCompiler.sysimgbackup_folder("native") | ||
if ispath(sysimgpath) | ||
rm(sysimgpath, recursive=true) | ||
end | ||
force_native_image!() | ||
end | ||
|
||
function enable_openblas_startup(libopenblas = "libopenblas") | ||
# First, we need to modify a few files in Julia's base directory | ||
base_dir = joinpath(Sys.BINDIR, Base.DATAROOTDIR, "julia", "base") | ||
|
||
# Replace definitions of `libblas_name`, etc.. to point to MKL | ||
if Sys.WORD_SIZE == 64 | ||
libopenblas = "$(libopenblas)64_" | ||
end | ||
replace_libblas(base_dir, libopenblas) | ||
|
||
# Next, build a new system image | ||
sysimgpath = PackageCompiler.sysimgbackup_folder("native") | ||
if ispath(sysimgpath) | ||
rm(sysimgpath, recursive=true) | ||
end | ||
force_native_image!() | ||
end |