mex_halide.m
function mex_halide( generator_filename, varargin )
%mex_halide - Create a mex library from a Halide generator source
%file.
%
% generator_filename identifies a C++ source file containing a generator.
% The remaining arguments are a list of name-value pairs of the form
% 'generator_param=value' used to assign the generator params, or
% additional flags:
% -e <assembly,bitcode,stmt,html>: Which outputs to emit from the
% generator, multiply outputs can be specified with a comma
% delimited list.
% -c <compiler>: Which C++ compiler to use to build the
% generator. Default is 'c++'.
% -g <generator>: Which generator to build. If only one generator
% is registered, it will be used by default.
%
% If a target is specified by a generator param with target=..., the
% 'matlab' feature flag must be present.
%
% This script uses two environment variables that can optionally be
% set or changed:
% - HALIDE_DISTRIB_PATH: The path to the distrib directory of Halide. If
% unspecified, this defaults to '../../distrib' relative to mex_halide.m.
% - HALIDE_CXX: The C++ compiler to use to build generators. The
% default is 'c++'.
gengen_cpp = ['#include "Halide.h"', sprintf('\n'), ...
'int main(int argc, char **argv) {', ...
' return Halide::Internal::generate_filter_main(argc, argv, std::cerr);', ...
'}'];
% Make a temporary directory for our intermediates.
temp = fullfile(tempdir, 'mex_halide');
if ~exist(temp, 'dir')
mkdir(temp);
end
% Write the generator main program to a temporary file.
gengen_filename = fullfile(temp, 'GenGen.cpp');
gengen_file = fopen(gengen_filename, 'w');
fprintf(gengen_file, '%s', gengen_cpp);
fclose(gengen_file);
% Build the filenames of the intermediate object we will generate.
[path, filename] = fileparts(generator_filename);
object_file = fullfile(temp, [filename, '.o']);
function_name = filename;
% Concatenate the generator args into a single string.
generator_args = strjoin(varargin);
target = 'host-matlab';
if isempty(getenv('HALIDE_DISTRIB_PATH'))
% If the user has not set the halide path, get the path of
% this file (presumably in $HALIDE_DISTRIB_PATH/tools/) and use
% that.
[path, ~] = fileparts(mfilename('fullpath'));
halide_distrib_path = fullfile(path, '..');
setenv('HALIDE_DISTRIB_PATH', halide_distrib_path);
end
halide_distrib_path = getenv('HALIDE_DISTRIB_PATH');
if ismac
libhalide = fullfile(halide_distrib_path, 'lib', 'libHalide.dylib');
else
libhalide = fullfile(halide_distrib_path, 'lib', 'libHalide.so');
end
halide_include = fullfile(halide_distrib_path, 'include');
if isempty(getenv('HALIDE_CXX'))
% If the user has not set a compiler for Halide, use c++.
setenv('HALIDE_CXX', 'c++');
end
halide_cxx = getenv('HALIDE_CXX');
ld_library_path = fullfile(halide_distrib_path, 'lib');
% Build the command to build the generator.
gen_bin = fullfile(temp, [function_name, '.generator']);
build_generator = ...
[halide_cxx, ...
' -g -Wall -std=c++17 -fno-rtti -I', halide_include, ' ', ...
gengen_filename, ' ', ...
generator_filename, ' ', ...
libhalide, ' ', ...
' -lz -lpthread -ldl ', ...
'-o ', gen_bin];
status = system(build_generator);
if status ~= 0
error('mex_halide:build_failed', 'Generator build failed.');
return;
end
% Run the generator to build the object file.
build_object = ...
['LD_LIBRARY_PATH=', ld_library_path, ' ', ...
'DYLD_LIBRARY_PATH=', ld_library_path, ' ', ...
gen_bin, ' ', ...
'-f ', function_name, ' ', ...
'-o ', temp, ' ', ...
'-e o,h ', ...
'target=', target, ' ', ...
generator_args];
status = system(build_object);
if status ~= 0
error('mex_halide:build_failed', ['Generator failed to build ' ...
'pipeline.']);
return;
end
% Run mex on the resulting object file.
mex(object_file, '-ldl');
end