-
Notifications
You must be signed in to change notification settings - Fork 2
/
try_mex_setup.m
240 lines (209 loc) · 9.97 KB
/
try_mex_setup.m
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
function success = try_mex_setup(language, verbose)
%TRY_MEX_SETUP tries running MEX setup for compiling language.
% At return,
% success = 1 means MEX is correctly set up.
% success = 0 means MEX setup fails.
% success = -1 means MEX setup runs successfully, but either we cannot try MEX on the example
% file because such a file is not found, or the MEX file of the example file works but the result
% is incorrect.
verbose = (nargin >=2 && verbose);
% Return if MEX is already well configured. This is important, because MEX is usable if it was set
% up before, and because MEX setup may fail even if it succeeded before due to change of environment.
success = mex_well_configured(language); % verbose = false
if success == 1
return
end
orig_warning_state = warning;
warning('off','all'); % We do not want to see warnings
% Try `mex('-setup', language)`
mex_setup = -1;
exception = [];
try
%[~, mex_setup] = evalc('mex(''-setup'', language)'); % Use evalc so that no output will be displayed
mex_setup = mex('-setup', language); % mex -setup may be interactive. So it is not good to mute it completely!!!
catch exception
% Do nothing
end
% If MEX setup fails, it is probably because of failing to find a supported compiler. See
% https://www.mathworks.com/support/requirements/supported-compilers.html. On Linux, it is easy to
% fix and the user should be capable of handling it. So we focus on macOS and Windows.
% For C/C++, it suffices to install "Xcode with Clang" on macOS and "Microsoft Visual C++"
% (Microsoft Visual Studio with the "Desktop development with C++" workload) on Windows.
% For Fortran, we need Xcode on macOS or Microsoft Visual Studio (maybe also with "Desktop
% development with C++") on Windows, and additionally the Intel Fortran compiler with the
% environment variables ONEAPI_ROOT or IFORT_COMPILERYEAR set accordingly. In the following,
% for Fortran, we set the environment variables ONEAPI_ROOT and IFORT_COMPILER18/19 ...
% This should make MEX setup work for Fortran on MATLAB 2020a or above if a **default** installation
% of Intel OneAPI (available for free) has been done and Xcode or Microsoft VS is correctly installed.
% Update 20230401: MATLAB R2022b/R2023a seem not checking ONEAPI_ROOT, and IFORT_COMPILER23 is needed.
if strcmpi(language, 'fortran') && (ismac || ispc) && (~isempty(exception) || mex_setup ~= 0)
if ismac
oneapi_root = '/opt/intel/oneapi/';
system_string = 'mac';
elseif ispc % Windows
oneapi_root = 'C:\Program Files (x86)\Intel\oneAPI\';
system_string = 'windows';
end
% Intel oneAPI does not support macOS any more starting from oneAPI 2024.
% For oneAPI 2024 on Windows, the compiler directory is "compiler/latest", and ifort is located
% in "compiler/latest/bin/"; In previous versions, the paths were
% "compiler/latest/<system_string>" and "compiler/latest/<system_string>/bin/intel64".
% As of Dec 2023, oneAPI 2024 is not supported on Windows, because MATLAB cannot locate ifort
% due to the change of the directory structure, even if ONEAPI_ROOT is set correctly.
compiler_dir = fullfile(oneapi_root, 'compiler', 'latest', system_string);
if ~exist(compiler_dir, 'dir')
compiler_dir = fullfile(oneapi_root, 'compiler', 'latest');
end
% Set PATH.
compiler_bin = fullfile(compiler_dir, 'bin');
compiler_bin64 = fullfile(compiler_bin, 'intel64'); % Why not worry about 32-bit case? Since R2016a, MATLAB has been 64-bit only.
setenv('PATH', [getenv('PATH'), pathsep, compiler_bin, pathsep, compiler_bin64]); % Not needed for Windows as of 2023.
% Set IFORT_COMPILER18, IFORT_COMPILER19, ..., IFORT_COMPILERCURRENT, ONEAPI_ROOT.
first_year = 18;
current_year = year(datetime()) - 2000;
nyear = current_year - first_year + 1;
envvars = cell(1, nyear + 1);
envvars_save = cell(1, nyear + 1);
isenvs = false(1, nyear + 1);
for ienvvar = 1 : length(envvars) - 1
envvars{ienvvar} = ['IFORT_COMPILER', int2str(first_year + ienvvar - 1)];
end
envvars{end} = 'ONEAPI_ROOT';
for ienvvar = 1 : length(envvars)
envvar = envvars{ienvvar};
% Test whether the environment variable exists (isenv is available since R2022b).
isenvs(ienvvar) = ~exist('isenv', 'builtin') || isenv(envvar);
% Save the value of the environment variable; the value is empty in case of nonexistence.
envvars_save{ienvvar} = getenv(envvar);
% Set the environment variable.
if strcmp(envvar, 'ONEAPI_ROOT')
setenv(envvar, oneapi_root);
else
setenv(envvar, compiler_dir);
end
end
% Try setting up MEX again.
mex_setup = -1;
exception = [];
try
%[~, mex_setup] = evalc('mex(''-setup'', language)'); % Use evalc so that no output will be displayed
% mex -setup may be interactive. So it is not good to mute it completely!!!
if verbose
mex_setup = mex('-v', '-setup', language);
else
mex_setup = mex('-setup', language);
end
catch exception
% Do nothing
end
% If the setup fails again, give up after restoring the environment variables.
if ~isempty(exception) || mex_setup ~= 0
for ienvvar = 1 : length(envvars)
envvar = envvars{ienvvar};
setenv(envvar, envvars_save{ienvvar});
if exist('unsetenv', 'builtin') && ~isenvs(ienvvar) % unsetenv is available since R2022b.
unsetenv(envvar);
end
end
end
end
if ~isempty(exception) || mex_setup ~= 0
fprintf('\nYour MATLAB failed to run mex(''-setup'', ''%s'').', language);
fprintf('\nTo see the detailed error message, execute the following command:\n');
fprintf('\n mex(''-v'', ''-setup'', ''%s'')\n', language);
success = 0;
else
% Try `mex(example_file)`
success = mex_well_configured(language, true); % verbose = true
end
% Restore the behavior of displaying warnings
warning(orig_warning_state);
return
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
function success = mex_well_configured(language, verbose)
%MEX_WELL_CONFIGURED verifies whether MEX is well configured by testing it on the official example.
% At return,
% success = 1 means MEX compiles the example successfully and the resultant MEX file works well.
% success = 0 means MEX cannot compile the example or the resultant MEX file does not work.
% success = -1 means either we cannot try MEX on the example file because such a file is not found,
% or the MEX file of the example file works but the result is incorrect.
verbose = (nargin >=2 && verbose);
success = 1;
orig_warning_state = warning;
warning('off','all'); % We do not want to see warnings
callstack = dbstack;
funname = callstack(1).name; % Name of the current function
% Locate example_file, which is an example provided by MATLAB for trying MEX.
% NOTE: MATLAB MAY CHANGE THE LOCATION OF THIS FILE IN THE FUTURE.
switch lower(language)
case 'fortran'
example_file_name = 'timestwo.F';
case {'c', 'c++', 'cpp'}
example_file_name = 'timestwo.c';
otherwise
error(sprintf('%s:UnsupportedLang', funname), '%s: Language ''%s'' is not supported by %s.', funname, language, funname);
end
example_file = fullfile(matlabroot, 'extern', 'examples', 'refbook', example_file_name);
% Check whether example_file exists
if ~exist(example_file, 'file')
if verbose
fprintf('\n');
wid = sprintf('%s:ExampleFileNotExist', funname);
warning('on', wid);
warning(wid, 'We cannot find\n%s,\nwhich is a MATLAB built-in example for trying MEX on %s. It will be ignored.\n', example_file, language);
end
success = -1;
return
end
% Try `mex(example_file)`
%!------------------------------------------------------------------------------------------------!%
% In general, we should clear a MEX function before compiling it. Otherwise, it may lead to a
% failure of even crash. See https://github.com/equipez/test_matlab/tree/master/crash
% Without the next line, `mex(example_file)` fails on Windows if we run this script two times.
clear('timestwo');
%!------------------------------------------------------------------------------------------------!%
temp_mexdir = tempdir(); % The directory to output the MEX file of `timestwo`.
mex_status = -1;
exception = [];
try
[~, mex_status] = evalc('mex(example_file, ''-outdir'', temp_mexdir)'); % Use evalc so that no output will be displayed
catch exception
% Do nothing
end
if ~isempty(exception) || mex_status ~= 0
delete(fullfile(temp_mexdir, 'timestwo.*')); % Remove the trash before returning
if verbose
fprintf('\nThe MEX of your MATLAB failed to compile\n%s,\nwhich is a MATLAB built-in example for trying MEX on %s.\n', example_file, language);
fprintf('\nTo see the detailed error message, execute the following command:\n');
fprintf('\n mex(''-v'', fullfile(matlabroot, ''extern'', ''examples'', ''refbook'', ''%s''));\n', example_file_name);
end
success = 0;
return
end
% Check whether the mexified example_file works
addpath(temp_mexdir); % Make `timestwo` available on path
exception = [];
try
[~, timestwo_out] = evalc('timestwo(1)'); % Try whether timestwo works correctly
catch exception
% Do nothing
end
rmpath(temp_mexdir); % Clean up the path before returning.
delete(fullfile(temp_mexdir, 'timestwo.*')); % Remove the trash before returning
if ~isempty(exception)
if verbose
fprintf('\nThe MEX of your MATLAB compiled\n%s,\nbut the resultant MEX file does not work.\n', example_file);
end
success = 0;
elseif abs(timestwo_out - 2) >= 20*eps
if verbose
fprintf('\n');
wid = sprintf('%s:ExampleFileWorksIncorrectly', funname);
warning('on', wid);
warning(wid, 'The MEX of your MATLAB compiled\n%s,\nbut the resultant MEX file returns %.16f when calculating 2 times 1.', example_file, timestwo_out);
end
success = -1;
end
% Restore the behavior of displaying warnings
warning(orig_warning_state);
return