Skip to content

Commit

Permalink
Merge pull request #10 from ASML-Labs/make-linux-compatible
Browse files Browse the repository at this point in the history
Add Linux support
  • Loading branch information
jorisbelierasml authored Jul 26, 2024
2 parents 26dd9fa + b722aa6 commit 9ed53e7
Show file tree
Hide file tree
Showing 9 changed files with 74 additions and 71 deletions.
28 changes: 12 additions & 16 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
![MATLAB versions](https://img.shields.io/badge/MATLAB-R2022b-blue.svg)
![Julia support](https://img.shields.io/badge/Julia%20-v1.7+-purple)
![Windows support](https://img.shields.io/badge/Windows-yellow)
![Windows](https://img.shields.io/badge/Windows-0078D6?style=for-the-badge&logo=windows&logoColor=white)
![Linux](https://img.shields.io/badge/Linux-FCC624?style=for-the-badge&logo=linux&logoColor=black)

# MATFrost.jl - Embedding Julia in MATLAB

Expand All @@ -13,15 +14,20 @@ Characteristics:
3. Leveraging Julia environments for reproducible builds.
4. Julia runs in its own mexhost process.

# Prerequisites: MATLAB MEX C++ compiler configured
1. Windows: MinGW-w64 installed: https://mathworks.com/support/requirements/supported-compilers.html
```matlab
% Install official MATLAB MinGW-w64 add-on.
websave("mingw.mlpkginstall", "https://mathworks.com/matlabcentral/mlc-downloads/downloads/submissions/52848/versions/22/download/mlpkginstall")
uiopen("mingw.mlpkginstall",1)
```
Alternatively, install MinGW-w64 manually and link with MATLAB using `MW_MINGW64_LOC` environment variable.
2. Linux: GCC installed: https://mathworks.com/support/requirements/supported-compilers-linux.html


# Quick start 🚀

```matlab
% MATLAB
websave("mingw.mlpkginstall", "https://mathworks.com/matlabcentral/mlc-downloads/downloads/submissions/52848/versions/22/download/mlpkginstall")
uiopen("mingw.mlpkginstall",1)
% Install official MATLAB MinGW-w64 addon for MEX compilation. (Different installation options exist.)
system('julia --project="@MATFrostEnvironment" -e "import Pkg ; Pkg.add(name=""MATFrost"")"');
% Install MATFrost
Expand All @@ -41,16 +47,6 @@ mjl.MATFrostHelloWorld.matfrost_hello_world() % 'Hello Julia! :)'

For more examples see the examples folder.

# Prerequisites
1. MinGW-w64 configured in MATLAB (needed for MEX compilation): https://mathworks.com/help/matlab/matlab_external/install-mingw-support-package.html
1. Option 1: Install the official MATLAB MinGW-w64 addon: https://mathworks.com/matlabcentral/fileexchange/52848-matlab-support-for-mingw-w64-c-c-fortran-compiler
```matlab
websave("mingw.mlpkginstall", "https://mathworks.com/matlabcentral/mlc-downloads/downloads/submissions/52848/versions/22/download/mlpkginstall")
uiopen("mingw.mlpkginstall",1)
```
2. Option 2: Install MinGW-w64 manually and link with MATLAB using `MW_MINGW64_LOC` environment variable.
# Workflow
To call a Julia function using MATFrost, the Julia function needs to satisfy some conditions. These conditions include fully typed input signatures and single method implementation (see later section for more info on these conditions). These conditions have been set to remove interface ambiguities but goes against the overall Julia vision of aiming for high extensibility. What this means in practice is that to be able to call Julia functions from MATLAB, very likely, a MATFrost Julia interface function needs to be created that wraps around Julia functionality.

Expand Down
4 changes: 0 additions & 4 deletions src/matfrostjuliacall/matfrostjuliacall.cpp
Original file line number Diff line number Diff line change
@@ -1,10 +1,6 @@
#include "mex.hpp"
#include "mexAdapter.hpp"

// windows
#define WIN32_LEAN_AND_MEAN
#include <windows.h>

#include <tuple>
// stdc++ lib
#include <string>
Expand Down
34 changes: 25 additions & 9 deletions src/matlab/matfrostjulia.m
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
properties (Access=private)
environment (1,1) string
namespace (:,1) string = []
bindir (1,1) string
juliaexe (1,1) string
matfrostjuliacall (1,1) string
mh matlab.mex.MexHost
end
Expand Down Expand Up @@ -48,23 +48,39 @@
obj.environment = argstruct.environment;

if isfield(argstruct, 'bindir')
obj.bindir = argstruct.bindir;
bindir = argstruct.bindir;
elseif isfield(argstruct, 'version')
obj.bindir = juliaup(argstruct.version);
bindir = juliaup(argstruct.version);
else
[status, obj.bindir] = system('julia -e "print(Sys.BINDIR)"');
[status, bindir] = system('julia -e "print(Sys.BINDIR)"');
assert(~status, "matfrostjulia:julia", ...
"Julia not found on PATH")
end

obj.matfrostjuliacall = getmatfrostjuliacall(obj.bindir);
if ispc
obj.juliaexe = fullfile(bindir, "julia.exe");
elseif isunix
obj.juliaexe = fullfile(bindir, "julia");
else
error("matfrostjulia:osNotSupported", "MacOS not supported yet.");
end


obj.mh = mexhost("EnvironmentVariables", [...
"JULIA_PROJECT", obj.environment;
"PATH", obj.bindir]);
obj.matfrostjuliacall = getmatfrostjuliacall(obj.juliaexe);

if ispc
obj.mh = mexhost("EnvironmentVariables", [...
"JULIA_PROJECT", obj.environment;
"PATH", fileparts(obj.juliaexe)]);
elseif isunix
obj.mh = mexhost("EnvironmentVariables", [...
"JULIA_PROJECT", obj.environment;
"PATH", fileparts(obj.juliaexe);
"LD_LIBRARY_PATH", fullfile(fileparts(fileparts(obj.juliaexe)), "lib")]);
end

if argstruct.instantiate
environmentinstantiate(obj.bindir, obj.environment);
environmentinstantiate(obj.juliaexe, obj.environment);
end
end
end
Expand Down
11 changes: 3 additions & 8 deletions src/matlab/private/environmentinstantiate.m
Original file line number Diff line number Diff line change
@@ -1,15 +1,10 @@
function environmentinstantiate(bindir, environment)
function environmentinstantiate(juliaexe, environment)
arguments
bindir (1,1) string
juliaexe (1,1) string
environment (1,1) string
end
if ispc
julia_exe = "julia.exe";
else
julia_exe = "julia";
end

[status, output] = system(fullfile(bindir, julia_exe) + " --project=""" + environment + """ -e ""import Pkg; Pkg.instantiate()""");
[status, output] = system(juliaexe + " --project=""" + environment + """ -e ""import Pkg; Pkg.instantiate()""");

assert(~status, "matfrostjulia:exe", "Could not instantiate environment: " + environment);

Expand Down
10 changes: 5 additions & 5 deletions src/matlab/private/getmatfrostjuliacall.m
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
function bjlname = getmatfrostjuliacall(bindir)
function mjlname = getmatfrostjuliacall(juliaexe)
% Get the name of the compiled mex file. If MEX file does not exist, it will compile.
%
bjlname = matfrostjuliacallname(bindir);
if ~exist(bjlname, "file")
mexdir = matfrostmexdir();
matfrostmake(bindir, fullfile(mexdir, bjlname + ".mexw64"));
mjlname = matfrostjuliacallname(juliaexe);
if ~exist(mjlname, "file")
mexdir = matfrostmexdir(juliaexe);
matfrostmake(juliaexe, mexdir, mjlname);
end

end
16 changes: 4 additions & 12 deletions src/matlab/private/juliaversion.m
Original file line number Diff line number Diff line change
@@ -1,19 +1,11 @@
function version = juliaversion(bindir)
function version = juliaversion(juliaexe)
%% Returns the version of the current or proivded julia executable
arguments
bindir (1,1) string = ""
end
if ispc
julia_exe = "julia.exe";
else
julia_exe = "julia";
end
if bindir ~= "" && isfolder(bindir)
julia_exe = fullfile(bindir, julia_exe);
juliaexe (1,1) string = ""
end

[status, output] = system(sprintf('%s --version', julia_exe));
assert(~status, "matfrostjulia:exe", "Could not run Julia in %s", bindir)
[status, output] = system(sprintf('%s --version', juliaexe));
assert(~status, "matfrostjulia:exe", "Could not run Julia in %s", juliaexe)
versionCell = regexp(output, 'julia version ([\d\.]+)', 'tokens');
version = versionCell{1}{1};

Expand Down
4 changes: 2 additions & 2 deletions src/matlab/private/matfrostjuliacallname.m
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
function bjlname = matfrostjuliacallname(bindir)
function bjlname = matfrostjuliacallname(juliaexe)

bjlname = "matfrostjuliacall_" + mlreportgen.utils.hash(string(version) + juliaversion(bindir) + matfrostversion());
bjlname = "matfrostjuliacall_" + mlreportgen.utils.hash(string(version) + juliaversion(juliaexe) + matfrostversion());

end
34 changes: 21 additions & 13 deletions src/matlab/private/matfrostmake.m
Original file line number Diff line number Diff line change
@@ -1,24 +1,32 @@
function matfrostmake(bindir, outmex)
function matfrostmake(juliaexe, mexdir, mjlname)
% NOTE: MinGW-w64 should be installed separately.
% Environment variable: "MW_MINGW64_LOC" should point to the MinGW installation directory.
%
% Or see https://mathworks.com/help/matlab/matlab_external/install-mingw-support-package.html

arguments
bindir (1,1) string {mustBeFolder}
outmex (1,1) string %= fullfile(fileparts(mfilename('fullpath')), 'bin');
juliaexe (1,1) string
mexdir (1,1) string
mjlname (1,1) string
end
if ~isfolder(fileparts(outmex))
mkdir(fileparts(outmex))
if ~isfolder(mexdir)
mkdir(mexdir)
end

julia_dir = fileparts(bindir);

mex('-v', ...
strcat('-I"', fullfile(julia_dir, 'include', 'julia'), '"'), ...
strcat('"', fullfile(julia_dir, 'lib', 'libjulia.dll.a'), '"'), ...
strcat('"', fullfile(julia_dir, 'lib', 'libopenlibm.dll.a'), '"'), ...
'-output', outmex, ...
fullfile(fileparts(fileparts(fileparts(mfilename('fullpath')))), 'matfrostjuliacall', 'matfrostjuliacall.cpp'));
julia_dir = fileparts(fileparts(juliaexe));
if ispc
mex('-v', ...
strcat('-I"', fullfile(julia_dir, 'include', 'julia'), '"'), ...
strcat('"', fullfile(julia_dir, 'lib', 'libjulia.dll.a'), '"'), ...
strcat('"', fullfile(julia_dir, 'lib', 'libopenlibm.dll.a'), '"'), ...
'-output', fullfile(mexdir, mjlname + ".mexw64"), ...
fullfile(fileparts(fileparts(fileparts(mfilename('fullpath')))), 'matfrostjuliacall', 'matfrostjuliacall.cpp'));
elseif isunix
mex('-v', ...
strcat('-I"', fullfile(julia_dir, 'include', 'julia'), '"'), ...
strcat('"', fullfile(julia_dir, 'lib', 'libjulia.so'), '"'), ...
'-output', fullfile(mexdir, mjlname + ".mexa64"), ...
fullfile(fileparts(fileparts(fileparts(mfilename('fullpath')))), 'matfrostjuliacall', 'matfrostjuliacall.cpp'));
end

end
4 changes: 2 additions & 2 deletions src/matlab/private/matfrostmexdir.m
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
function mexdir = matfrostmexdir()
function mexdir = matfrostmexdir(juliaexe)

[~, juliaroot] = system('julia -e "print(Base.DEPOT_PATH[1])"');
[~, juliaroot] = system(juliaexe + " -e ""print(Base.DEPOT_PATH[1])""");

mexdir = fullfile(juliaroot, "scratchspaces", "406cae98-a0f7-4766-b83f-8510a556e0e7", "mexbin");

Expand Down

0 comments on commit 9ed53e7

Please sign in to comment.