Skip to content

Commit c54efa9

Browse files
authored
Overhaul library and engine detection (#188)
Move code the determines the engine paths to deps/build.jl file. This has the benefit to allow us to handle and load the package in cases MATLAB doesn't exist on the CI.
1 parent 71826c5 commit c54efa9

File tree

8 files changed

+176
-116
lines changed

8 files changed

+176
-116
lines changed

.github/workflows/CI.yml

Lines changed: 22 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -9,29 +9,25 @@ on:
99
pull_request:
1010

1111
jobs:
12-
name: julia ${{ matrix.version }} - ${{ matrix.os }} - ${{ matrix.arch }}
13-
runs-on: ${{ matrix.os }}
14-
strategy:
15-
fail-fast: false
16-
matrix:
17-
version:
18-
- '1.3'
19-
- '1'
20-
- 'nightly'
21-
os:
22-
- ubuntu-latest
23-
- macOS-latest
24-
- windows-latest
25-
arch:
26-
- x64
27-
- x86
28-
steps:
29-
- uses: actions/checkout@v2
30-
- uses: julia-actions/setup-julia@latest
31-
with:
32-
version: ${{ matrix.version }}
33-
arch: ${{ matrix.arch }}
34-
- uses: julia-actions/julia-buildpkg@latest
35-
- uses: julia-actions/julia-runtest@latest
36-
with:
37-
file: lcov.info
12+
test:
13+
name: julia ${{ matrix.version }} - ${{ matrix.os }} - ${{ matrix.arch }}
14+
runs-on: ${{ matrix.os }}
15+
strategy:
16+
fail-fast: false
17+
matrix:
18+
version:
19+
- '1.3'
20+
- '1'
21+
- 'nightly'
22+
os:
23+
- ubuntu-latest
24+
arch:
25+
- x64
26+
steps:
27+
- uses: actions/checkout@v2
28+
- uses: julia-actions/setup-julia@latest
29+
with:
30+
version: ${{ matrix.version }}
31+
arch: ${{ matrix.arch }}
32+
- uses: julia-actions/julia-buildpkg@latest
33+
- uses: julia-actions/julia-runtest@latest

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,4 @@
1+
deps/build.log
2+
deps/deps.jl
13
*.mat
24
Manifest.toml

Project.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
name = "MATLAB"
22
uuid = "10e44e05-a98a-55b3-a45b-ba969058deb6"
3-
repo = "https://github.com/JuliaInterop/MATLAB.jl.git"
43
license = "MIT"
4+
repo = "https://github.com/JuliaInterop/MATLAB.jl.git"
55
version = "0.8.0"
66

77
[deps]

deps/build.jl

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
import Libdl
2+
3+
const depsfile = joinpath(@__DIR__, "deps.jl")
4+
5+
# Determine MATLAB library path and provide facilities to load libraries with
6+
# this path
7+
8+
function find_matlab_homepath()
9+
matlab_home = get(ENV, "MATLAB_HOME", nothing)
10+
if isnothing(matlab_home)
11+
matlab_exe = Sys.which("matlab")
12+
matlab_home = !isnothing(matlab_exe) ? dirname(dirname(matlab_exe)) : nothing
13+
if isnothing(matlab_home)
14+
if Sys.isapple()
15+
default_dir = "/Applications"
16+
if isdir(default_dir)
17+
dirs = readdir(default_dir)
18+
filter!(app -> occursin(r"^MATLAB_R[0-9]+[ab]\.app$", app), dirs)
19+
if !isempty(dirs)
20+
matlab_home = joinpath(default_dir, maximum(dirs))
21+
end
22+
end
23+
elseif Sys.iswindows()
24+
default_dir = Sys.WORD_SIZE == 32 ? "C:\\Program Files (x86)\\MATLAB" : "C:\\Program Files\\MATLAB"
25+
if isdir(default_dir)
26+
dirs = readdir(default_dir)
27+
filter!(dir -> occursin(r"^R[0-9]+[ab]$", dir), dirs)
28+
if !isempty(dirs)
29+
matlab_home = joinpath(default_dir, maximum(dirs))
30+
end
31+
end
32+
end
33+
end
34+
end
35+
if isnothing(matlab_home)
36+
return nothing
37+
else
38+
@info("Found MATLAB home path at $matlab_home")
39+
return matlab_home
40+
end
41+
end
42+
43+
function find_matlab_libpath(matlab_home)
44+
# get path to MATLAB libraries
45+
matlab_lib_dir = if Sys.islinux()
46+
Sys.WORD_SIZE == 32 ? "glnx86" : "glnxa64"
47+
elseif Sys.isapple()
48+
Sys.WORD_SIZE == 32 ? "maci" : "maci64"
49+
elseif Sys.iswindows()
50+
Sys.WORD_SIZE == 32 ? "win32" : "win64"
51+
end
52+
matlab_libpath = joinpath(matlab_home, "bin", matlab_lib_dir)
53+
if !isdir(matlab_libpath)
54+
@warn("The MATLAB library path could not be found.")
55+
end
56+
return matlab_libpath
57+
end
58+
59+
function find_matlab_cmd(matlab_home)
60+
if !Sys.iswindows()
61+
matlab_cmd = joinpath(matlab_home, "bin", "matlab")
62+
if !isfile(matlab_cmd)
63+
@warn("The MATLAB path is invalid. Ensure the \"MATLAB_HOME\" evironmental variable to the MATLAB root directory.")
64+
end
65+
matlab_cmd = "exec $(Base.shell_escape(matlab_cmd))"
66+
elseif Sys.iswindows()
67+
matlab_cmd = joinpath(matlab_home, "bin", (Sys.WORD_SIZE == 32 ? "win32" : "win64"), "MATLAB.exe")
68+
if !isfile(matlab_cmd)
69+
error("The MATLAB path is invalid. Ensure the \"MATLAB_HOME\" evironmental variable to the MATLAB root directory.")
70+
end
71+
end
72+
return matlab_cmd
73+
end
74+
75+
matlab_homepath = find_matlab_homepath()
76+
77+
if !isnothing(matlab_homepath)
78+
matlab_libpath = find_matlab_libpath(matlab_homepath)
79+
matlab_cmd = find_matlab_cmd(matlab_homepath)
80+
libmx_size = filesize(Libdl.dlpath(joinpath(matlab_libpath, "libmx")))
81+
open(depsfile, "w") do io
82+
println(io,
83+
"""
84+
# This file is automatically generated, do not edit.
85+
86+
function check_deps()
87+
if libmx_size != filesize(Libdl.dlpath(joinpath(matlab_libpath, "libmx")))
88+
error("MATLAB library has changed, re-run Pkg.build(\\\"MATLAB\\\")")
89+
end
90+
end
91+
"""
92+
)
93+
println(io, "const matlab_libpath = \"$(escape_string(matlab_libpath))\"")
94+
println(io, "const matlab_cmd = \"$(escape_string(matlab_cmd))\"")
95+
println(io, "const libmx_size = $libmx_size")
96+
end
97+
elseif get(ENV, "JULIA_REGISTRYCI_AUTOMERGE", nothing) == "true" || get(ENV, "CI", nothing) == "true"
98+
# We need to be able to install and load this package without error for
99+
# Julia's registry AutoMerge to work, so we just use dummy values.
100+
# Similarly we want to also be able to install and load this package for CI.
101+
matlab_libpath = ""
102+
matlab_cmd = ""
103+
libmx_size = 0
104+
105+
open(depsfile, "w") do io
106+
println(io,
107+
"""
108+
# This file is automatically generated, do not edit.
109+
110+
check_deps() = nothing
111+
"""
112+
)
113+
println(io, "const matlab_libpath = \"$(escape_string(matlab_libpath))\"")
114+
println(io, "const matlab_cmd = \"$(escape_string(matlab_cmd))\"")
115+
println(io, "const libmx_size = $libmx_size")
116+
end
117+
else
118+
error("MATLAB cannot be found. Set the \"MATLAB_HOME\" environment variable to the MATLAB root directory and re-run Pkg.build(\"MATLAB\").")
119+
end

src/MATLAB.jl

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
module MATLAB
22

3-
using Base.Sys: islinux, iswindows, isapple
43
using Libdl
54
using SparseArrays
65

@@ -30,23 +29,29 @@ export MSession, MatFile,
3029
mxcall,
3130
@mput, @mget, @mat_str
3231

33-
if iswindows()
32+
if Sys.iswindows()
3433
export show_msession, hide_msession, get_msession_visiblity
3534
end
3635

36+
const depsfile = joinpath(dirname(@__DIR__), "deps", "deps.jl")
37+
if isfile(depsfile)
38+
include(depsfile)
39+
else
40+
error("MATLAB is not properly installed. Please run Pkg.build(\"MATLAB\") and restart Julia.")
41+
end
42+
3743
# exceptions
3844
struct MEngineError <: Exception
3945
message::String
4046
end
4147

4248
include("init.jl") # initialize Refs
43-
include("mxbase.jl")
4449
include("mxarray.jl")
4550
include("matfile.jl")
4651
include("engine.jl")
4752
include("matstr.jl")
4853

49-
if iswindows()
54+
if Sys.iswindows()
5055
# workaround "primary message table for module 77" error
5156
# creates a dummy Engine session and keeps it open so the libraries used by all other
5257
# Engine clients are not loaded and unloaded repeatedly
@@ -64,14 +69,21 @@ if iswindows()
6469
end
6570
end
6671

72+
# helper library access function
73+
engfunc(fun::Symbol) = Libdl.dlsym(libeng[], fun)
74+
mxfunc(fun::Symbol) = Libdl.dlsym(libmx[], fun)
75+
matfunc(fun::Symbol) = Libdl.dlsym(libmat[], fun)
76+
6777
function __init__()
78+
check_deps()
6879

80+
if libmx_size > 0 # non-zero size library path
6981

7082
# load libraries
7183

72-
libmx[] = Libdl.dlopen(joinpath(matlab_libpath(), "libmx"), Libdl.RTLD_GLOBAL)
73-
libmat[] = Libdl.dlopen(joinpath(matlab_libpath(), "libmat"), Libdl.RTLD_GLOBAL)
74-
libeng[] = Libdl.dlopen(joinpath(matlab_libpath(), "libeng"), Libdl.RTLD_GLOBAL)
84+
libmx[] = Libdl.dlopen(joinpath(matlab_libpath, "libmx"), Libdl.RTLD_GLOBAL)
85+
libmat[] = Libdl.dlopen(joinpath(matlab_libpath, "libmat"), Libdl.RTLD_GLOBAL)
86+
libeng[] = Libdl.dlopen(joinpath(matlab_libpath, "libeng"), Libdl.RTLD_GLOBAL)
7587

7688
# engine functions
7789

@@ -166,6 +178,7 @@ function __init__()
166178
mat_put_variable[] = matfunc(:matPutVariable)
167179
mat_get_dir[] = matfunc(:matGetDir)
168180

181+
end
169182
end
170183

171184

src/engine.jl

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,10 @@
66
#
77
###########################################################
88
const default_startflag = "" # no additional flags
9-
default_matlabcmd() = matlab_cmd() * " -nosplash"
9+
const default_matlabcmd = matlab_cmd * " -nosplash"
1010
# pass matlab flags directly or as a Vector of flags, i.e. "-a" or ["-a", "-b", "-c"]
11-
startcmd(flag::AbstractString = default_startflag) = isempty(flag) ? default_matlabcmd() : default_matlabcmd() * " " * flag
12-
startcmd(flags::AbstractVector{T}) where {T<:AbstractString} = isempty(flags) ? default_matlabcmd() : default_matlabcmd() * " " * join(flags, " ")
11+
startcmd(flag::AbstractString = default_startflag) = isempty(flag) ? default_matlabcmd : default_matlabcmd * " " * flag
12+
startcmd(flags::AbstractVector{T}) where {T<:AbstractString} = isempty(flags) ? default_matlabcmd : default_matlabcmd * " " * join(flags, " ")
1313

1414
# 64 K buffer should be sufficient to store the output text in most cases
1515
const default_output_buffer_size = 64 * 1024
@@ -20,20 +20,20 @@ mutable struct MSession
2020
bufptr::Ptr{UInt8}
2121

2222
function MSession(bufsize::Integer = default_output_buffer_size; flags=default_startflag)
23-
if iswindows()
23+
if Sys.iswindows()
2424
assign_persistent_msession()
2525
end
2626
ep = ccall(eng_open[], Ptr{Cvoid}, (Ptr{UInt8},), startcmd(flags))
2727
if ep == C_NULL
2828
@warn("Confirm MATLAB is installed and discoverable.", maxlog=1)
29-
if iswindows()
29+
if Sys.iswindows()
3030
@warn("Ensure `matlab -regserver` has been run in a Command Prompt as Administrator.", maxlog=1)
31-
elseif islinux()
31+
elseif Sys.islinux()
3232
@warn("Ensure `csh` is installed; this may require running `sudo apt-get install csh`.", maxlog=1)
3333
end
3434
throw(MEngineError("failed to open MATLAB engine session"))
3535
end
36-
if iswindows()
36+
if Sys.iswindows()
3737
# hide the MATLAB command window on Windows and change to current directory
3838
ccall(eng_set_visible[], Cint, (Ptr{Cvoid}, Cint), ep, 0)
3939
ccall(eng_eval_string[], Cint, (Ptr{Cvoid}, Ptr{UInt8}),
@@ -103,7 +103,7 @@ function close_default_msession()
103103
return nothing
104104
end
105105

106-
if iswindows()
106+
if Sys.iswindows()
107107
function show_msession(m::MSession = get_default_msession())
108108
ret = ccall(eng_set_visible[], Cint, (Ptr{Cvoid}, Cint), m, 1)
109109
ret != 0 && throw(MEngineError("failed to show MATLAB engine session (err = $ret)"))

src/mxbase.jl

Lines changed: 0 additions & 74 deletions
This file was deleted.

test/runtests.jl

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
11
using MATLAB
22
using Test
33

4+
is_ci() = get(ENV, "CI", nothing) == "true"
5+
6+
if !is_ci() # only test if not CI
47
include("engine.jl")
58
include("matfile.jl")
69
include("matstr.jl")
710
include("mxarray.jl")
11+
end

0 commit comments

Comments
 (0)