From b6964b3eb1d40c7dd13b3170fb7aa2e242a107d6 Mon Sep 17 00:00:00 2001 From: Alex Ames Date: Mon, 18 Jan 2021 21:28:56 -0700 Subject: [PATCH 1/6] Update Windows installation instructions (#184) R2020a+ should automatically register a COM server upon installation. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index b3363fa..af2cbdf 100644 --- a/README.md +++ b/README.md @@ -27,7 +27,7 @@ By default, `MATLAB.jl` uses the MATLAB installation with the greatest version n ### Windows -1. Start a Command Prompt as an Administrator and enter `matlab /regserver`. +1. For Matlab R2020a onwards, you should be able to go directly to step 2. If you encounter issues, run `matlab -batch "comserver('register')"` in the command prompt. For earlier versions of Matlab, start a command prompt as an administrator and enter `matlab /regserver`. 2. From Julia run: `Pkg.add("MATLAB")` From a18362986bace432f3de9a2c9936e06144974ce8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?P=C3=A1ll=20Haraldsson?= Date: Sat, 27 Feb 2021 18:21:29 +0000 Subject: [PATCH 2/6] Registered trademark (#187) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Registered trademark [skip ci] They also use TM (i.e not registered), but tat seems wrong in this case. FYI: I thought conversely TM right for Python.jl, but that's for the logo only. There's a discussion about registering that package, pointing to this one as precedent (while also thought the name here not ideal...). ® is maybe not required for Python (in that case), but might be best to be on the safe side, here I think we really want o be right: https://se.mathworks.com/company/aboutus/policies_statements/trademarks.html * Fix --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index af2cbdf..87ca89f 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,8 @@ ## MATLAB -The `MATLAB.jl` package provides an interface for using [MATLAB™](http://www.mathworks.com/products/matlab/) from [Julia](http://julialang.org) using the MATLAB C api. In other words, this package allows users to call MATLAB functions within Julia, thus making it easy to interoperate with MATLAB from the Julia language. +The `MATLAB.jl` package provides an interface for using [MATLAB®](http://www.mathworks.com/products/matlab/) from [Julia](http://julialang.org) using the MATLAB C api. In other words, this package allows users to call MATLAB functions within Julia, thus making it easy to interoperate with MATLAB from the Julia language. -You cannot use `MATLAB.jl` without having purchased and installed a copy of MATLAB™ from [MathWorks](http://www.mathworks.com/). This package is available free of charge and in no way replaces or alters any functionality of MathWorks's MATLAB product. +You cannot use `MATLAB.jl` without having purchased and installed a copy of MATLAB® from [MathWorks](http://www.mathworks.com/). This package is available free of charge and in no way replaces or alters any functionality of MathWorks's MATLAB product. ## Overview From bd3b4f11e41f068dbe89997f5237c2d06a77ce01 Mon Sep 17 00:00:00 2001 From: Mustafa Mohamad Date: Wed, 28 Apr 2021 14:36:33 -0400 Subject: [PATCH 3/6] Add CI --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index e63f75c..de76143 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ *.mat +Manifest.toml From 71826c537688beeab8595304b840a77ee24b7513 Mon Sep 17 00:00:00 2001 From: Mustafa Mohamad Date: Wed, 28 Apr 2021 14:37:23 -0400 Subject: [PATCH 4/6] Add CI through gh actions --- .github/workflows/CI.yml | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 .github/workflows/CI.yml diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml new file mode 100644 index 0000000..c4ec1a7 --- /dev/null +++ b/.github/workflows/CI.yml @@ -0,0 +1,37 @@ +name: CI + +on: + push: + branches: + - master + - release-* + tags: '*' + pull_request: + +jobs: + name: julia ${{ matrix.version }} - ${{ matrix.os }} - ${{ matrix.arch }} + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + version: + - '1.3' + - '1' + - 'nightly' + os: + - ubuntu-latest + - macOS-latest + - windows-latest + arch: + - x64 + - x86 + steps: + - uses: actions/checkout@v2 + - uses: julia-actions/setup-julia@latest + with: + version: ${{ matrix.version }} + arch: ${{ matrix.arch }} + - uses: julia-actions/julia-buildpkg@latest + - uses: julia-actions/julia-runtest@latest + with: + file: lcov.info From c54efa9bdccb2b8276e8b8e622a249d536281656 Mon Sep 17 00:00:00 2001 From: Mustafa M Date: Thu, 29 Apr 2021 00:09:14 -0400 Subject: [PATCH 5/6] 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. --- .github/workflows/CI.yml | 48 ++++++++-------- .gitignore | 2 + Project.toml | 2 +- deps/build.jl | 119 +++++++++++++++++++++++++++++++++++++++ src/MATLAB.jl | 27 ++++++--- src/engine.jl | 16 +++--- src/mxbase.jl | 74 ------------------------ test/runtests.jl | 4 ++ 8 files changed, 176 insertions(+), 116 deletions(-) create mode 100644 deps/build.jl delete mode 100644 src/mxbase.jl diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index c4ec1a7..3d9a89b 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -9,29 +9,25 @@ on: pull_request: jobs: - name: julia ${{ matrix.version }} - ${{ matrix.os }} - ${{ matrix.arch }} - runs-on: ${{ matrix.os }} - strategy: - fail-fast: false - matrix: - version: - - '1.3' - - '1' - - 'nightly' - os: - - ubuntu-latest - - macOS-latest - - windows-latest - arch: - - x64 - - x86 - steps: - - uses: actions/checkout@v2 - - uses: julia-actions/setup-julia@latest - with: - version: ${{ matrix.version }} - arch: ${{ matrix.arch }} - - uses: julia-actions/julia-buildpkg@latest - - uses: julia-actions/julia-runtest@latest - with: - file: lcov.info + test: + name: julia ${{ matrix.version }} - ${{ matrix.os }} - ${{ matrix.arch }} + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + version: + - '1.3' + - '1' + - 'nightly' + os: + - ubuntu-latest + arch: + - x64 + steps: + - uses: actions/checkout@v2 + - uses: julia-actions/setup-julia@latest + with: + version: ${{ matrix.version }} + arch: ${{ matrix.arch }} + - uses: julia-actions/julia-buildpkg@latest + - uses: julia-actions/julia-runtest@latest diff --git a/.gitignore b/.gitignore index de76143..7fd2562 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,4 @@ +deps/build.log +deps/deps.jl *.mat Manifest.toml diff --git a/Project.toml b/Project.toml index 2ffb4ad..bd9b77d 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "MATLAB" uuid = "10e44e05-a98a-55b3-a45b-ba969058deb6" -repo = "https://github.com/JuliaInterop/MATLAB.jl.git" license = "MIT" +repo = "https://github.com/JuliaInterop/MATLAB.jl.git" version = "0.8.0" [deps] diff --git a/deps/build.jl b/deps/build.jl new file mode 100644 index 0000000..b778398 --- /dev/null +++ b/deps/build.jl @@ -0,0 +1,119 @@ +import Libdl + +const depsfile = joinpath(@__DIR__, "deps.jl") + +# Determine MATLAB library path and provide facilities to load libraries with +# this path + +function find_matlab_homepath() + matlab_home = get(ENV, "MATLAB_HOME", nothing) + if isnothing(matlab_home) + matlab_exe = Sys.which("matlab") + matlab_home = !isnothing(matlab_exe) ? dirname(dirname(matlab_exe)) : nothing + if isnothing(matlab_home) + if Sys.isapple() + default_dir = "/Applications" + if isdir(default_dir) + dirs = readdir(default_dir) + filter!(app -> occursin(r"^MATLAB_R[0-9]+[ab]\.app$", app), dirs) + if !isempty(dirs) + matlab_home = joinpath(default_dir, maximum(dirs)) + end + end + elseif Sys.iswindows() + default_dir = Sys.WORD_SIZE == 32 ? "C:\\Program Files (x86)\\MATLAB" : "C:\\Program Files\\MATLAB" + if isdir(default_dir) + dirs = readdir(default_dir) + filter!(dir -> occursin(r"^R[0-9]+[ab]$", dir), dirs) + if !isempty(dirs) + matlab_home = joinpath(default_dir, maximum(dirs)) + end + end + end + end + end + if isnothing(matlab_home) + return nothing + else + @info("Found MATLAB home path at $matlab_home") + return matlab_home + end +end + +function find_matlab_libpath(matlab_home) + # get path to MATLAB libraries + matlab_lib_dir = if Sys.islinux() + Sys.WORD_SIZE == 32 ? "glnx86" : "glnxa64" + elseif Sys.isapple() + Sys.WORD_SIZE == 32 ? "maci" : "maci64" + elseif Sys.iswindows() + Sys.WORD_SIZE == 32 ? "win32" : "win64" + end + matlab_libpath = joinpath(matlab_home, "bin", matlab_lib_dir) + if !isdir(matlab_libpath) + @warn("The MATLAB library path could not be found.") + end + return matlab_libpath +end + +function find_matlab_cmd(matlab_home) + if !Sys.iswindows() + matlab_cmd = joinpath(matlab_home, "bin", "matlab") + if !isfile(matlab_cmd) + @warn("The MATLAB path is invalid. Ensure the \"MATLAB_HOME\" evironmental variable to the MATLAB root directory.") + end + matlab_cmd = "exec $(Base.shell_escape(matlab_cmd))" + elseif Sys.iswindows() + matlab_cmd = joinpath(matlab_home, "bin", (Sys.WORD_SIZE == 32 ? "win32" : "win64"), "MATLAB.exe") + if !isfile(matlab_cmd) + error("The MATLAB path is invalid. Ensure the \"MATLAB_HOME\" evironmental variable to the MATLAB root directory.") + end + end + return matlab_cmd +end + +matlab_homepath = find_matlab_homepath() + +if !isnothing(matlab_homepath) + matlab_libpath = find_matlab_libpath(matlab_homepath) + matlab_cmd = find_matlab_cmd(matlab_homepath) + libmx_size = filesize(Libdl.dlpath(joinpath(matlab_libpath, "libmx"))) + open(depsfile, "w") do io + println(io, + """ + # This file is automatically generated, do not edit. + + function check_deps() + if libmx_size != filesize(Libdl.dlpath(joinpath(matlab_libpath, "libmx"))) + error("MATLAB library has changed, re-run Pkg.build(\\\"MATLAB\\\")") + end + end + """ + ) + println(io, "const matlab_libpath = \"$(escape_string(matlab_libpath))\"") + println(io, "const matlab_cmd = \"$(escape_string(matlab_cmd))\"") + println(io, "const libmx_size = $libmx_size") + end +elseif get(ENV, "JULIA_REGISTRYCI_AUTOMERGE", nothing) == "true" || get(ENV, "CI", nothing) == "true" + # We need to be able to install and load this package without error for + # Julia's registry AutoMerge to work, so we just use dummy values. + # Similarly we want to also be able to install and load this package for CI. + matlab_libpath = "" + matlab_cmd = "" + libmx_size = 0 + + open(depsfile, "w") do io + println(io, + """ + # This file is automatically generated, do not edit. + + check_deps() = nothing + """ + ) + println(io, "const matlab_libpath = \"$(escape_string(matlab_libpath))\"") + println(io, "const matlab_cmd = \"$(escape_string(matlab_cmd))\"") + println(io, "const libmx_size = $libmx_size") + end +else + error("MATLAB cannot be found. Set the \"MATLAB_HOME\" environment variable to the MATLAB root directory and re-run Pkg.build(\"MATLAB\").") +end diff --git a/src/MATLAB.jl b/src/MATLAB.jl index 68046e5..bf4e278 100644 --- a/src/MATLAB.jl +++ b/src/MATLAB.jl @@ -1,6 +1,5 @@ module MATLAB -using Base.Sys: islinux, iswindows, isapple using Libdl using SparseArrays @@ -30,23 +29,29 @@ export MSession, MatFile, mxcall, @mput, @mget, @mat_str -if iswindows() +if Sys.iswindows() export show_msession, hide_msession, get_msession_visiblity end +const depsfile = joinpath(dirname(@__DIR__), "deps", "deps.jl") +if isfile(depsfile) + include(depsfile) +else + error("MATLAB is not properly installed. Please run Pkg.build(\"MATLAB\") and restart Julia.") +end + # exceptions struct MEngineError <: Exception message::String end include("init.jl") # initialize Refs -include("mxbase.jl") include("mxarray.jl") include("matfile.jl") include("engine.jl") include("matstr.jl") -if iswindows() +if Sys.iswindows() # workaround "primary message table for module 77" error # creates a dummy Engine session and keeps it open so the libraries used by all other # Engine clients are not loaded and unloaded repeatedly @@ -64,14 +69,21 @@ if iswindows() end end +# helper library access function +engfunc(fun::Symbol) = Libdl.dlsym(libeng[], fun) +mxfunc(fun::Symbol) = Libdl.dlsym(libmx[], fun) +matfunc(fun::Symbol) = Libdl.dlsym(libmat[], fun) + function __init__() + check_deps() + if libmx_size > 0 # non-zero size library path # load libraries - libmx[] = Libdl.dlopen(joinpath(matlab_libpath(), "libmx"), Libdl.RTLD_GLOBAL) - libmat[] = Libdl.dlopen(joinpath(matlab_libpath(), "libmat"), Libdl.RTLD_GLOBAL) - libeng[] = Libdl.dlopen(joinpath(matlab_libpath(), "libeng"), Libdl.RTLD_GLOBAL) + libmx[] = Libdl.dlopen(joinpath(matlab_libpath, "libmx"), Libdl.RTLD_GLOBAL) + libmat[] = Libdl.dlopen(joinpath(matlab_libpath, "libmat"), Libdl.RTLD_GLOBAL) + libeng[] = Libdl.dlopen(joinpath(matlab_libpath, "libeng"), Libdl.RTLD_GLOBAL) # engine functions @@ -166,6 +178,7 @@ function __init__() mat_put_variable[] = matfunc(:matPutVariable) mat_get_dir[] = matfunc(:matGetDir) + end end diff --git a/src/engine.jl b/src/engine.jl index ec78c74..16371db 100644 --- a/src/engine.jl +++ b/src/engine.jl @@ -6,10 +6,10 @@ # ########################################################### const default_startflag = "" # no additional flags -default_matlabcmd() = matlab_cmd() * " -nosplash" +const default_matlabcmd = matlab_cmd * " -nosplash" # pass matlab flags directly or as a Vector of flags, i.e. "-a" or ["-a", "-b", "-c"] -startcmd(flag::AbstractString = default_startflag) = isempty(flag) ? default_matlabcmd() : default_matlabcmd() * " " * flag -startcmd(flags::AbstractVector{T}) where {T<:AbstractString} = isempty(flags) ? default_matlabcmd() : default_matlabcmd() * " " * join(flags, " ") +startcmd(flag::AbstractString = default_startflag) = isempty(flag) ? default_matlabcmd : default_matlabcmd * " " * flag +startcmd(flags::AbstractVector{T}) where {T<:AbstractString} = isempty(flags) ? default_matlabcmd : default_matlabcmd * " " * join(flags, " ") # 64 K buffer should be sufficient to store the output text in most cases const default_output_buffer_size = 64 * 1024 @@ -20,20 +20,20 @@ mutable struct MSession bufptr::Ptr{UInt8} function MSession(bufsize::Integer = default_output_buffer_size; flags=default_startflag) - if iswindows() + if Sys.iswindows() assign_persistent_msession() end ep = ccall(eng_open[], Ptr{Cvoid}, (Ptr{UInt8},), startcmd(flags)) if ep == C_NULL @warn("Confirm MATLAB is installed and discoverable.", maxlog=1) - if iswindows() + if Sys.iswindows() @warn("Ensure `matlab -regserver` has been run in a Command Prompt as Administrator.", maxlog=1) - elseif islinux() + elseif Sys.islinux() @warn("Ensure `csh` is installed; this may require running `sudo apt-get install csh`.", maxlog=1) end throw(MEngineError("failed to open MATLAB engine session")) end - if iswindows() + if Sys.iswindows() # hide the MATLAB command window on Windows and change to current directory ccall(eng_set_visible[], Cint, (Ptr{Cvoid}, Cint), ep, 0) ccall(eng_eval_string[], Cint, (Ptr{Cvoid}, Ptr{UInt8}), @@ -103,7 +103,7 @@ function close_default_msession() return nothing end -if iswindows() +if Sys.iswindows() function show_msession(m::MSession = get_default_msession()) ret = ccall(eng_set_visible[], Cint, (Ptr{Cvoid}, Cint), m, 1) ret != 0 && throw(MEngineError("failed to show MATLAB engine session (err = $ret)")) diff --git a/src/mxbase.jl b/src/mxbase.jl deleted file mode 100644 index 2855e63..0000000 --- a/src/mxbase.jl +++ /dev/null @@ -1,74 +0,0 @@ -# Determine MATLAB library path and provide facilities to load libraries with -# this path - -function matlab_homepath() - matlab_home = get(ENV, "MATLAB_HOME", "") - if isempty(matlab_home) - if islinux() - matlab_home = dirname(dirname(realpath(chomp(read(`which matlab`, String))))) - elseif isapple() - default_dir = "/Applications" - if isdir(default_dir) - dirs = readdir(default_dir) - filter!(app -> occursin(r"^MATLAB_R[0-9]+[ab]\.app$", app), dirs) - if !isempty(dirs) - matlab_home = joinpath(default_dir, maximum(dirs)) - end - end - elseif iswindows() - default_dir = Sys.WORD_SIZE == 32 ? "C:\\Program Files (x86)\\MATLAB" : "C:\\Program Files\\MATLAB" - if isdir(default_dir) - dirs = readdir(default_dir) - filter!(dir -> occursin(r"^R[0-9]+[ab]$", dir), dirs) - if !isempty(dirs) - matlab_home = joinpath(default_dir, maximum(dirs)) - end - end - end - end - if isempty(matlab_home) - error("The MATLAB path could not be found. Set the MATLAB_HOME environmental variable to specify the MATLAB path.") - end - return matlab_home -end - -function matlab_libpath() - # get path to MATLAB libraries - matlab_home = matlab_homepath() - matlab_lib_dir = if islinux() - Sys.WORD_SIZE == 32 ? "glnx86" : "glnxa64" - elseif isapple() - Sys.WORD_SIZE == 32 ? "maci" : "maci64" - elseif iswindows() - Sys.WORD_SIZE == 32 ? "win32" : "win64" - end - matlab_libpath = joinpath(matlab_home, "bin", matlab_lib_dir) - if !isdir(matlab_libpath) - error("The MATLAB library path could not be found.") - end - return matlab_libpath -end - -function matlab_cmd() - matlab_home = matlab_homepath() - if !iswindows() - matlab_cmd = joinpath(matlab_home, "bin", "matlab") - if !isfile(matlab_cmd) - error("The MATLAB path is invalid. Set the MATLAB_HOME evironmental variable to the MATLAB root.") - end - matlab_cmd = "exec $(Base.shell_escape(matlab_cmd))" - elseif iswindows() - matlab_cmd = joinpath(matlab_home, "bin", (Sys.WORD_SIZE == 32 ? "win32" : "win64"), "MATLAB.exe") - if !isfile(matlab_cmd) - error("The MATLAB path is invalid. Set the MATLAB_HOME evironmental variable to the MATLAB root.") - end - end - return matlab_cmd -end - - -# helper library access function - -engfunc(fun::Symbol) = Libdl.dlsym(libeng[], fun) -mxfunc(fun::Symbol) = Libdl.dlsym(libmx[], fun) -matfunc(fun::Symbol) = Libdl.dlsym(libmat[], fun) diff --git a/test/runtests.jl b/test/runtests.jl index 67398b7..7ef05bb 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -1,7 +1,11 @@ using MATLAB using Test +is_ci() = get(ENV, "CI", nothing) == "true" + +if !is_ci() # only test if not CI include("engine.jl") include("matfile.jl") include("matstr.jl") include("mxarray.jl") +end From 783a527ee9469e633199a7210ff7dfb2bf17b9aa Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 29 Apr 2021 04:48:18 +0000 Subject: [PATCH 6/6] Set version to 0.8.1 --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index bd9b77d..2a17753 100644 --- a/Project.toml +++ b/Project.toml @@ -2,7 +2,7 @@ name = "MATLAB" uuid = "10e44e05-a98a-55b3-a45b-ba969058deb6" license = "MIT" repo = "https://github.com/JuliaInterop/MATLAB.jl.git" -version = "0.8.0" +version = "0.8.1" [deps] Libdl = "8f399da3-3557-5675-b5ff-fb832c97cbdb"