Add Meson build system

Mostly working. The testsuite remains to be done.

In particular, windows/deps/mkoctfile64 is modified to print its version number
on stdout instead of stderr. This is what the standard mkoctfile does, and it
allows easier capture of the output from Meson.
kalman-mex
Sébastien Villemot 2023-07-24 17:30:03 +02:00
parent b0503ce994
commit 16f921ed62
No known key found for this signature in database
GPG Key ID: 2CECE9350ECEBE4A
12 changed files with 827 additions and 4 deletions

View File

@ -21,7 +21,14 @@ if nargin<2
modifypath = true;
end
if isoctave
build_dir = get_build_dir(dynareroot);
if ~isempty(build_dir)
% If a Meson build directory is found, use it preferably
mexpath = { build_dir };
if modifypath
addpath(build_dir)
end
elseif isoctave
% Add specific paths for Dynare Windows package
if ispc
if strcmpi(computer(), 'i686-w64-mingw32')

View File

@ -201,7 +201,15 @@ if preprocessoroutput
end
end
command = ['"' dynareroot '..' filesep 'preprocessor' filesep 'dynare-preprocessor" ' fname] ;
build_dir = get_build_dir(dynareroot);
if isempty(build_dir)
preprocessor_dir = [ dynareroot '..' filesep 'preprocessor' ];
else
disp(['Using build directory: ' build_dir ])
preprocessor_dir = [ build_dir filesep 'preprocessor' filesep 'src' ];
end
command = ['"' preprocessor_dir filesep 'dynare-preprocessor" ' fname];
command = [ command ' mexext=' mexext ' "matlabroot=' matlabroot '"'];
% Properly quote arguments before passing them to the shell
if ~isempty(varargin)

36
matlab/get_build_dir.m Normal file
View File

@ -0,0 +1,36 @@
function p = get_build_dir(dynareroot)
% Returns a Meson build directory if one is found.
% Otherwise returns an empty value.
% Copyright © 2023 Dynare Team
%
% This file is part of Dynare.
%
% Dynare is free software: you can redistribute it and/or modify
% it under the terms of the GNU General Public License as published by
% the Free Software Foundation, either version 3 of the License, or
% (at your option) any later version.
%
% Dynare is distributed in the hope that it will be useful,
% but WITHOUT ANY WARRANTY; without even the implied warranty of
% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
% GNU General Public License for more details.
%
% You should have received a copy of the GNU General Public License
% along with Dynare. If not, see <https://www.gnu.org/licenses/>.
envvar = getenv('DYNARE_BUILD_DIR');
default_matlab = [ dynareroot '..' filesep 'build-matlab' ];
default_octave = [ dynareroot '..' filesep 'build-octave' ];
if ~isempty(envvar)
p = envvar;
elseif ~isoctave && exist(default_matlab, 'dir')
p = default_matlab;
elseif isoctave && exist(default_octave, 'dir')
p = default_octave;
else
p = [];
end
end

500
meson.build Normal file
View File

@ -0,0 +1,500 @@
# TODO:
# - move to fortran_std=f2018; this requires changes in libkordersim, and also replacing isnan() by ieee_is_nan() in various MEX (or use -fall-intrinsics)
# - use buildtype=debugoptimized by default? (and in the preprocessor too)
# - with -Dprefer_static=true, under Octave/Windows, we are now linking the compiler libs (libgcc, libstdc++, libgfortran, libquadmath, libssp, libgomp) statically (contrary to what we were doing with autotools). In theory this is better, because the compiler used for creating the Octave binary may be different from the one used for creating our MEX. Check that this is ok
# - configuration option to disable documentation
# - configuration option to disable preprocessor build
# - add -Wold-style-cast C++ flag except when building flex-generated files
# - determine minimal meson version required, and declare it in the project() function (here and the preprocessor); if possible at low cost, diminish the requirement. NB: Ubuntu “jammy” 22.04 has 0.61, Debian “bullseye” 11 has 0.56 (but bullseye-backports has 1.0), Ubuntu ”focal” 20.04 has 0.53
project('dynare',
'cpp', 'fortran', 'c',
version : '6-unstable',
default_options : [ 'cpp_std=gnu++20', 'fortran_std=none', 'c_std=gnu17', 'warning_level=2' ])
add_global_arguments('-Wimplicit-interface', '-Wno-compare-reals', language : 'fortran')
add_global_arguments('-DPACKAGE_VERSION="' + meson.project_version() + '"', language : 'cpp')
cpp_compiler = meson.get_compiler('cpp')
fortran_compiler = meson.get_compiler('fortran')
c_compiler = meson.get_compiler('c')
### Preprocessor
subdir('preprocessor/src')
subdir('preprocessor/doc')
### Generated M-file
sed_exe = find_program('sed')
custom_target(output : 'dynare_version.m', input : 'matlab/dynare_version.m.in',
command : [ sed_exe, 's/@PACKAGE_VERSION@/' + meson.project_version() + '/', '@INPUT@' ],
capture : true,
build_by_default : true) # FIXME: This option can be removed when “install” is set to true
### MEX files
mex_incdir = include_directories('mex/sources')
## Various dependencies
if host_machine.system() != 'windows'
dl_dep = dependency('dl')
else
# Under Windows, we dont use dlopen but rely on LoadLibrary
dl_dep = []
endif
openmp_dep = dependency('openmp')
gsl_dep = dependency('gsl')
if get_option('build_for') == 'octave'
matio_dep = dependency('matio')
else
# We dont use MatIO under MATLAB
matio_dep = []
endif
pthread_t_sizeof = c_compiler.sizeof('pthread_t', prefix : '#include <pthread.h>')
# TODO: when requiring meson ⩾ 1.2, incorporate pthread.F08 into the dependency object with “extra_files” option
# (and thus remove the pthread_fortran_iface variable)
pthread_fortran_dep = declare_dependency(compile_args : '-DSIZEOF_PTHREAD_T=' + pthread_t_sizeof.to_string())
## Determine MEX compilation options
if get_option('build_for') == 'matlab'
matlab_path = get_option('matlab_path')
if matlab_path == ''
error('The “matlab_path” option must be provided when doing a MATLAB build')
endif
matlab_version = run_command('scripts/get-matlab-version', matlab_path, check : true).stdout().strip()
matlab_minimal_version = [ '8.3', 'R2014a' ]
if matlab_version.version_compare('<' + matlab_minimal_version[0])
error('MATLAB is too old (version ' + matlab_version + '), please upgrade to version ' + matlab_minimal_version[0] + ' (' + matlab_minimal_version[1] + ') at least.')
endif
matlab_version_hex = run_command('scripts/get-matlab-version', '--hex', matlab_path, check : true).stdout().strip()
matlab_exe = find_program(matlab_path / 'bin' / 'matlab', required : not meson.is_cross_build())
if host_machine.system() == 'linux' and host_machine.cpu_family() == 'x86_64'
mexext = 'mexa64'
matlab_arch = 'glnxa64'
export_file = matlab_path / 'extern/lib/glnxa64/mexFunction.map'
export_link_arg = '-Wl,--version-script,' + export_file
elif host_machine.system() == 'windows' and host_machine.cpu_family() == 'x86_64'
mexext = 'mexw64'
matlab_arch = 'win64'
export_file = meson.current_source_dir() / 'mex/build/matlab/mex.def'
export_link_arg = export_file
if get_option('build_for') == 'matlab'
arch_fortran_args = [ '-fno-underscoring' ]
endif
elif host_machine.system() == 'darwin'
if host_machine.cpu_family() == 'x86_64'
mexext = 'mexmaci64'
matlab_arch = 'maci64'
elif host_machine.cpu_family() == 'aarch64'
mexext = 'mexmaca64'
matlab_arch = 'maca64'
else
error('Unsupported platform')
endif
export_file = meson.current_source_dir() / 'mex/build/matlab/mexFunction-MacOSX.map'
export_link_arg = '-Wl,-exported_symbols_list,' + export_file
else
error('Unsupported platform')
endif
matlab_defs = [ '-DMATLAB_MEX_FILE', '-DMATLAB_VERSION=' + matlab_version_hex, '-DMEXEXT="' + mexext + '"' ]
matlab_incdir = include_directories(matlab_path / 'extern/include')
mex_kwargs = { 'name_prefix' : '',
'name_suffix' : mexext,
'include_directories' : [ mex_incdir, matlab_incdir ],
'cpp_args' : matlab_defs,
'fortran_args' : matlab_defs + [ '-fexceptions' ] + get_variable('arch_fortran_args', []),
'c_args' : matlab_defs + [ '-fexceptions' ],
'link_args' : [ export_link_arg, '-L' + (matlab_path / 'bin' / matlab_arch), '-lmx', '-lmex', '-lmat' ],
'link_depends' : export_file }
# For unit tests
exe_rpath = matlab_path / 'bin' / matlab_arch
exe_link_args = [ '-L' + exe_rpath, '-lmx', '-lmex', '-lmat' ]
# No need to use find_library() for the following libraries, since they are always shipped with MATLAB
blas_dep = declare_dependency(link_args : '-lmwblas')
lapack_dep = declare_dependency(link_args : '-lmwlapack', dependencies : blas_dep)
umfpack_dep = declare_dependency(link_args : '-lmwumfpack', dependencies : blas_dep)
ut_dep = declare_dependency(link_args : '-lut')
slicot_dep = declare_dependency(dependencies : [ fortran_compiler.find_library('slicot64_pic'), blas_dep, lapack_dep ])
else # Octave build
octave_exe = find_program('octave', required : not meson.is_cross_build())
mkoctfile_exe = find_program('mkoctfile')
octave_minimal_version = '6.2.0'
octave_version = run_command(mkoctfile_exe, '-v', check : true).stdout().replace('mkoctfile, version ', '')
if octave_version.version_compare('<' + octave_minimal_version)
error('Octave is too old (version ' + octave_version + '), please upgrade to version ' + octave_minimal_version + ' at least.')
endif
octave_incflags = run_command(mkoctfile_exe, '-p', 'INCFLAGS', check : true).stdout().split()
octlibdir = run_command(mkoctfile_exe, '-p', 'OCTLIBDIR', check : true).stdout().strip()
# Determine whether to link MEX files against the Octave libraries. mkoctfile
# no longer does this by default but in practice it is needed for Windows and
# macOS.
octave_libs = run_command(mkoctfile_exe, '-p', 'OCTAVE_LIBS', check : true).stdout().split()
if host_machine.system() == 'windows' or host_machine.system() == 'darwin'
# Under Windows, --enable-link-all-dependencies is hardcoded in src/mkoctfile.cc.in.
# Under macOS, the Homebrew formula passes --enable-link-all-dependencies
# to the configure script.
octave_link_args = [ '-L' + octlibdir ] + octave_libs
else
octave_link_args = []
endif
# For unit tests
exe_rpath = octlibdir
exe_link_args = [ '-L' + octlibdir ] + octave_libs
octave_defs = [ '-DOCTAVE_MEX_FILE', '-DMEXEXT="mex"' ]
mex_kwargs = { 'name_prefix' : '',
'name_suffix' : 'mex',
'include_directories' : [ mex_incdir ],
'cpp_args' : octave_incflags + octave_defs,
'fortran_args' : octave_incflags + octave_defs,
'c_args' : octave_incflags + octave_defs,
'link_args' : octave_link_args}
# The -L argument is useful when cross-compiling.
blas_dep = declare_dependency(link_args : [ '-L' + (octlibdir / '../..') ] + run_command(mkoctfile_exe, '-p', 'BLAS_LIBS', check : true).stdout().split())
lapack_dep = declare_dependency(link_args : run_command(mkoctfile_exe, '-p', 'LAPACK_LIBS', check : true).stdout().split(),
dependencies : blas_dep)
# Create a dependency object for UMFPACK.
# The dependency returned by find_library('umfpack') is not enough, because we also want the define
# that indicates the location of umfpack.h, so we construct a new dependency object.
if cpp_compiler.has_header('suitesparse/umfpack.h', args : octave_incflags)
umfpack_def = '-DHAVE_SUITESPARSE_UMFPACK_H'
elif cpp_compiler.has_header('umfpack.h', args : octave_incflags)
umfpack_def = '-DHAVE_UMFPACK_H'
else
error('Cant find umfpack.h')
endif
# Do not enforce static linking even if prefer_static is true, since that library is shipped
# with Octave.
# The “dirs” argument is useful when cross-compiling.
umfpack_dep_tmp = cpp_compiler.find_library('umfpack', dirs : octlibdir / '../..', static : false)
umfpack_dep = declare_dependency(compile_args : umfpack_def, dependencies : [ umfpack_dep_tmp, blas_dep ])
# This library does not exist under Octave
ut_dep = []
# First look for libslicot, then if needed fallback on libslicot_pic
slicot_dep_tmp = fortran_compiler.find_library('slicot', required : false)
if not slicot_dep_tmp.found()
slicot_dep_tmp = fortran_compiler.find_library('slicot_pic')
endif
slicot_dep = declare_dependency(dependencies : [ slicot_dep_tmp, blas_dep, lapack_dep ])
endif
# When static linking is preferred, try to statically link the compiler libraries
if get_option('prefer_static')
static_flags_pre = [ '-static-libgcc', '-static-libstdc++' ]
static_flags_post = []
if host_machine.system() == 'windows'
# Under Debian 12, libgfortran.a and libquadmath.a are not compiled with -fPIC, so cant be linked in a MEX.
# Under macOS, -static-libgcc implies -static-libgfortran (see gfortran -dumpspecs), and for libquadmath
# we use a hack with a local copy of libquadmath.a.
# -static-libquadmath was introduced in GCC 13. Until we require the latter, use a hack.
static_flags_pre += [ '-static-libgfortran', '-Wl,-Bstatic,--whole-archive', '-lquadmath', '-Wl,-Bdynamic,--no-whole-archive' ]
# Hack to avoid dynamically linking against libwinpthread DLL (which is
# pulled in by libstdc++, even without using threads, since we are using
# the POSIX threads version of MinGW).
static_flags_pre += [ '-Wl,-Bstatic,--whole-archive', '-lwinpthread', '-Wl,-Bdynamic,--no-whole-archive' ]
# Hack for libssp, which is pulled in by -fstack-protector (curiously only
# on some MEX files), see windows/build.sh. Note that the link against
# libssp should not happen with compilers from MSYS2, see:
# https://www.msys2.org/news/#2022-10-10-libssp-is-no-longer-required
# But it happens with Debians cross compilers (as of Debian “bookworm”
# 12). Also note that the -lssp must come by the end of the link command
# (otherwise it will have to be enclosed within --whole-archive).
static_flags_post = [ '-Wl,-Bstatic', '-lssp', '-Wl,-Bdynamic' ]
endif
mex_kwargs += { 'link_args' : static_flags_pre + mex_kwargs.get('link_args', []) + static_flags_post }
# NB: constructing a dependency object with link_args : ['-Wl,-Bstatic', '-lgomp', '-Wl,-Bdynamic'] does not work,
# because it reorders the three arguments and puts -lgomp at the end
openmp_dep_tmp = cpp_compiler.find_library('gomp', static : true)
openmp_dep = declare_dependency(dependencies : [ openmp_dep, openmp_dep_tmp ])
endif
# For use when creating intermediate static libraries to be incorporated in MEX files
static_library_kwargs = mex_kwargs + { 'name_prefix' : [], 'name_suffix' : [], 'link_args' : [], 'pic' : true }
mex_blas_fortran_iface = [ 'mex/sources/matlab_mex.F08', 'mex/sources/blas_lapack.F08' ]
pthread_fortran_iface = [ 'mex/sources/pthread.F08' ]
## Various core MEX
shared_module('mjdgges', [ 'mex/sources/mjdgges/mjdgges.F08' ] + mex_blas_fortran_iface, kwargs : mex_kwargs, dependencies : lapack_dep)
shared_module('num_procs', 'mex/sources/num_procs/num_procs.cc', kwargs : mex_kwargs)
perfect_foresight_problem_src = [ 'mex/sources/perfect_foresight_problem/perfect_foresight_problem.cc',
'mex/sources/perfect_foresight_problem/DynamicModelCaller.cc' ]
shared_module('perfect_foresight_problem', perfect_foresight_problem_src, kwargs : mex_kwargs, dependencies : openmp_dep)
block_trust_region_src = [ 'mex/sources/block_trust_region/dulmage_mendelsohn.f08',
'mex/sources/block_trust_region/matlab_fcn_closure.F08',
'mex/sources/block_trust_region/trust_region.f08',
'mex/sources/block_trust_region/mexFunction.f08' ] + mex_blas_fortran_iface
shared_module('block_trust_region', block_trust_region_src, kwargs : mex_kwargs, dependencies : lapack_dep)
bytecode_src = [ 'mex/sources/bytecode/bytecode.cc',
'mex/sources/bytecode/Interpreter.cc',
'mex/sources/bytecode/Mem_Mngr.cc',
'mex/sources/bytecode/SparseMatrix.cc',
'mex/sources/bytecode/Evaluate.cc',
'mex/sources/bytecode/BasicSymbolTable.cc' ]
preprocessor_headers_dep = declare_dependency(include_directories : include_directories('preprocessor/src'))
shared_module('bytecode', bytecode_src, kwargs : mex_kwargs, dependencies : [ umfpack_dep, ut_dep, preprocessor_headers_dep ])
shared_module('sparse_hessian_times_B_kronecker_C', 'mex/sources/kronecker/sparse_hessian_times_B_kronecker_C.cc',
kwargs : mex_kwargs, dependencies : openmp_dep)
# TODO: A_times_B_kronecker_C does not depend on LAPACK, but since the
# interfaces to both BLAS and LAPACK are in the same source file, the
# dependency must be added. Think about splitting into two files.
# NB: The problem does not appear with libkordersim because since it is a library,
# the LAPACK stuff is never pulled in.
shared_module('A_times_B_kronecker_C', [ 'mex/sources/kronecker/A_times_B_kronecker_C.f08' ] + mex_blas_fortran_iface,
kwargs : mex_kwargs, dependencies : [ blas_dep, lapack_dep ])
shared_module('cycle_reduction', [ 'mex/sources/cycle_reduction/mexFunction.f08' ] + mex_blas_fortran_iface,
kwargs : mex_kwargs, dependencies : [ blas_dep, lapack_dep ])
shared_module('logarithmic_reduction', [ 'mex/sources/logarithmic_reduction/mexFunction.f08' ] + mex_blas_fortran_iface,
kwargs : mex_kwargs, dependencies : [ blas_dep, lapack_dep ])
shared_module('disclyap_fast', [ 'mex/sources/disclyap_fast/disclyap_fast.f08' ] + mex_blas_fortran_iface,
kwargs : mex_kwargs, dependencies : [ blas_dep, lapack_dep ])
# TODO: Same remark as A_times_B_kronecker_C
shared_module('riccati_update', [ 'mex/sources/riccati_update/mexFunction.f08' ] + mex_blas_fortran_iface,
kwargs : mex_kwargs, dependencies : [ blas_dep, lapack_dep ])
qmc_sequence_src = [ 'mex/sources/sobol/qmc_sequence.cc',
'mex/sources/sobol/sobol.f08' ]
# Hack for statically linking libgfortran
# Since qmc_sequence is a mix of C++ and Fortran, the linker invoked is the C++ one.
# Meson then rightly adds -lgfortran, but the -static-libgfortran flag does not work.
qmc_sequence_mex_kwargs = mex_kwargs
if get_option('prefer_static') and host_machine.system() == 'windows'
qmc_sequence_mex_kwargs += { 'link_args' : qmc_sequence_mex_kwargs.get('link_args') + [ '-Wl,-Bstatic', '-lgfortran', '-Wl,-Bdynamic' ] }
endif
shared_module('qmc_sequence', qmc_sequence_src, kwargs : qmc_sequence_mex_kwargs, dependencies : [ blas_dep, openmp_dep ])
shared_module('kalman_steady_state', 'mex/sources/kalman_steady_state/kalman_steady_state.cc', kwargs : mex_kwargs, dependencies : slicot_dep)
## k-order simulation stuff
kordersim_src = [ 'mex/sources/libkordersim/pascal.f08',
'mex/sources/libkordersim/sort.f08',
'mex/sources/libkordersim/partitions.f08',
'mex/sources/libkordersim/tensors.f08',
'mex/sources/libkordersim/simulation.f08',
'mex/sources/libkordersim/struct.f08' ] + mex_blas_fortran_iface + pthread_fortran_iface
kordersim_lib = static_library('kordersim', kordersim_src, kwargs : static_library_kwargs, dependencies : [ blas_dep, pthread_fortran_dep ])
shared_module('folded_to_unfolded_dr', 'mex/sources/folded_to_unfolded_dr/mexFunction.f08', kwargs : mex_kwargs, link_with : kordersim_lib)
shared_module('k_order_mean', 'mex/sources/k_order_mean/mexFunction.f08', kwargs : mex_kwargs, link_with : kordersim_lib)
shared_module('k_order_simul', 'mex/sources/k_order_simul/mexFunction.f08', kwargs : mex_kwargs, link_with : kordersim_lib)
shared_module('local_state_space_iteration_2', 'mex/sources/local_state_space_iterations/local_state_space_iteration_2.cc', kwargs : mex_kwargs, dependencies : openmp_dep)
shared_module('local_state_space_iteration_3', 'mex/sources/local_state_space_iterations/local_state_space_iteration_3.f08', kwargs : mex_kwargs, link_with : kordersim_lib)
shared_module('local_state_space_iteration_k', 'mex/sources/local_state_space_iterations/local_state_space_iteration_k.f08', kwargs : mex_kwargs, link_with : kordersim_lib)
## k-order resolution stuff
korder_src = [ 'mex/sources/libkorder/kord/approximation.cc',
'mex/sources/libkorder/kord/decision_rule.cc',
'mex/sources/libkorder/kord/dynamic_model.cc',
'mex/sources/libkorder/kord/faa_di_bruno.cc',
'mex/sources/libkorder/kord/first_order.cc',
'mex/sources/libkorder/kord/korder.cc',
'mex/sources/libkorder/kord/korder_stoch.cc',
'mex/sources/libkorder/kord/journal.cc',
'mex/sources/libkorder/sylv/BlockDiagonal.cc',
'mex/sources/libkorder/sylv/GeneralMatrix.cc',
'mex/sources/libkorder/sylv/GeneralSylvester.cc',
'mex/sources/libkorder/sylv/IterativeSylvester.cc',
'mex/sources/libkorder/sylv/KronUtils.cc',
'mex/sources/libkorder/sylv/KronVector.cc',
'mex/sources/libkorder/sylv/QuasiTriangular.cc',
'mex/sources/libkorder/sylv/QuasiTriangularZero.cc',
'mex/sources/libkorder/sylv/SchurDecomp.cc',
'mex/sources/libkorder/sylv/SchurDecompEig.cc',
'mex/sources/libkorder/sylv/SimilarityDecomp.cc',
'mex/sources/libkorder/sylv/SylvException.cc',
'mex/sources/libkorder/sylv/SylvMatrix.cc',
'mex/sources/libkorder/sylv/SylvParams.cc',
'mex/sources/libkorder/sylv/SymSchurDecomp.cc',
'mex/sources/libkorder/sylv/TriangularSylvester.cc',
'mex/sources/libkorder/sylv/Vector.cc',
'mex/sources/libkorder/tl/equivalence.cc',
'mex/sources/libkorder/tl/fine_container.cc',
'mex/sources/libkorder/tl/fs_tensor.cc',
'mex/sources/libkorder/tl/gs_tensor.cc',
'mex/sources/libkorder/tl/int_sequence.cc',
'mex/sources/libkorder/tl/kron_prod.cc',
'mex/sources/libkorder/tl/normal_moments.cc',
'mex/sources/libkorder/tl/permutation.cc',
'mex/sources/libkorder/tl/ps_tensor.cc',
'mex/sources/libkorder/tl/pyramid_prod.cc',
'mex/sources/libkorder/tl/pyramid_prod2.cc',
'mex/sources/libkorder/tl/rfs_tensor.cc',
'mex/sources/libkorder/tl/sparse_tensor.cc',
'mex/sources/libkorder/tl/stack_container.cc',
'mex/sources/libkorder/tl/symmetry.cc',
'mex/sources/libkorder/tl/t_container.cc',
'mex/sources/libkorder/tl/t_polynomial.cc',
'mex/sources/libkorder/tl/tensor.cc',
'mex/sources/libkorder/tl/tl_static.cc',
'mex/sources/libkorder/tl/twod_matrix.cc',
'mex/sources/libkorder/utils/pascal_triangle.cc',
'mex/sources/libkorder/utils/int_power.cc',
'mex/sources/libkorder/utils/sthread.cc',
'mex/sources/libkorder/k_ord_dynare.cc',
'mex/sources/libkorder/dynamic_dll.cc',
'mex/sources/libkorder/dynamic_m.cc' ]
korder_incdir = include_directories('mex/sources/libkorder', 'mex/sources/libkorder/tl', 'mex/sources/libkorder/sylv',
'mex/sources/libkorder/kord', 'mex/sources/libkorder/utils')
korder_lib = static_library('korder', korder_src,
kwargs : static_library_kwargs + { 'include_directories' : static_library_kwargs.get('include_directories') + korder_incdir},
dependencies : [ blas_dep, lapack_dep, dl_dep ])
korder_mex_kwargs = mex_kwargs + { 'include_directories' : mex_kwargs.get('include_directories') + korder_incdir}
shared_module('gensylv', 'mex/sources/gensylv/gensylv.cc', kwargs : korder_mex_kwargs, link_with : korder_lib)
shared_module('k_order_perturbation', 'mex/sources/gensylv/gensylv.cc', kwargs : korder_mex_kwargs, link_with : korder_lib)
k_order_welfare_src = [ 'mex/sources/k_order_welfare/k_order_welfare.cc',
'mex/sources/k_order_welfare/approximation_welfare.cc',
'mex/sources/k_order_welfare/k_ord_objective.cc',
'mex/sources/k_order_welfare/objective_m.cc' ]
shared_module('k_order_welfare', k_order_welfare_src, kwargs : korder_mex_kwargs, link_with : korder_lib)
# Unit tests
korder_test_kwargs = { 'include_directories' : [ mex_incdir, korder_incdir ],
'link_args' : exe_link_args,
'build_rpath' : exe_rpath,
'link_with' : korder_lib }
korder_sylv_test_exe = executable('korder_sylv_test', [ 'mex/sources/libkorder/sylv/tests/MMMatrix.cc',
'mex/sources/libkorder/sylv/tests/tests.cc'],
kwargs : korder_test_kwargs)
test('korder_sylv', korder_sylv_test_exe, workdir : meson.current_source_dir() / 'mex/sources/libkorder/sylv/tests', timeout : 300)
korder_tl_test_exe = executable('korder_tl_test', [ 'mex/sources/libkorder/tl/tests/factory.cc',
'mex/sources/libkorder/tl/tests/monoms.cc',
'mex/sources/libkorder/tl/tests/tests.cc'],
kwargs : korder_test_kwargs)
test('korder_tl', korder_tl_test_exe, timeout : 300)
korder_kord_test_exe = executable('korder_kord_test', 'mex/sources/libkorder/kord/tests/tests.cc', kwargs : korder_test_kwargs)
test('korder_kord', korder_kord_test_exe, timeout : 1500)
## MS-SBVAR stuff
ms_sbvar_src = [ 'contrib/ms-sbvar/utilities_dw/arrays/dw_array.c',
'contrib/ms-sbvar/utilities_dw/arrays/dw_matrix_array.c',
'contrib/ms-sbvar/utilities_dw/ascii/dw_ascii.c',
'contrib/ms-sbvar/utilities_dw/ascii/dw_parse_cmd.c',
'contrib/ms-sbvar/utilities_dw/elliptical/dw_elliptical.c',
'contrib/ms-sbvar/utilities_dw/error/dw_error.c',
'contrib/ms-sbvar/utilities_dw/histogram/dw_histogram.c',
'contrib/ms-sbvar/utilities_dw/math/dw_math.c',
'contrib/ms-sbvar/utilities_dw/matrix/dw_matrix.c',
'contrib/ms-sbvar/utilities_dw/matrix/bmatrix.c',
'contrib/ms-sbvar/utilities_dw/sort/dw_matrix_sort.c',
'contrib/ms-sbvar/utilities_dw/stat/dw_rand_gsl.c',
'contrib/ms-sbvar/utilities_dw/stat/dw_matrix_rand.c',
'contrib/ms-sbvar/switch_dw/switching/dw_switch.c',
'contrib/ms-sbvar/switch_dw/switching/dw_switchio.c',
'contrib/ms-sbvar/switch_dw/switching/dw_dirichlet_restrictions.c',
'contrib/ms-sbvar/switch_dw/switching/dw_metropolis_theta.c',
'contrib/ms-sbvar/switch_dw/state_space/sbvar/VARbase.c',
'contrib/ms-sbvar/switch_dw/state_space/sbvar/VARio.c',
'mex/sources/ms-sbvar/mex_top_level.cc',
'mex/sources/ms-sbvar/modify_for_mex.cc' ]
ms_sbvar_defs = [ '-DSTRUCTURED_COLUMN_MAJOR' ]
ms_sbvar_incdir = include_directories('contrib/ms-sbvar/utilities_dw/include', 'contrib/ms-sbvar/switch_dw/switching', 'mex/sources/ms-sbvar')
ms_sbvar_lib = static_library('ms_sbvar', ms_sbvar_src,
kwargs : static_library_kwargs + { 'c_args' : static_library_kwargs.get('c_args') + ms_sbvar_defs,
'cpp_args' : static_library_kwargs.get('cpp_args') + ms_sbvar_defs,
'include_directories' : static_library_kwargs.get('include_directories') + ms_sbvar_incdir },
dependencies : [ blas_dep, lapack_dep, gsl_dep, matio_dep, ut_dep ])
mex_ms_sbvar_kwargs = mex_kwargs + { 'c_args' : mex_kwargs.get('c_args') + ms_sbvar_defs,
'cpp_args' : mex_kwargs.get('cpp_args') + ms_sbvar_defs,
'include_directories' : mex_kwargs.get('include_directories') + ms_sbvar_incdir }
ms_sbvar_create_init_file_src = [ 'contrib/ms-sbvar/switch_dw/state_space/sbvar/create_init_file.c',
'contrib/ms-sbvar/switch_dw/state_space/sbvar/VARio_matlab.c' ]
shared_module('ms_sbvar_create_init_file', ms_sbvar_create_init_file_src, kwargs : mex_ms_sbvar_kwargs, link_with : ms_sbvar_lib, dependencies : matio_dep)
ms_sbvar_command_line_src = [ 'contrib/ms-sbvar/switch_dw/switching/dw_switch_opt.c',
'contrib/ms-sbvar/switch_dw/switching/dw_mdd_switch.c',
'contrib/ms-sbvar/switch_dw/state_space/sbvar/dw_sbvar_command_line.c',
'contrib/ms-sbvar/switch_dw/state_space/sbvar/sbvar_estimate.c',
'contrib/ms-sbvar/switch_dw/state_space/sbvar/sbvar_simulate.c',
'contrib/ms-sbvar/switch_dw/state_space/sbvar/sbvar_probabilities.c',
'contrib/ms-sbvar/switch_dw/state_space/sbvar/sbvar_mdd.c',
'contrib/ms-sbvar/switch_dw/state_space/sbvar/sbvar_forecast.c',
'contrib/ms-sbvar/switch_dw/state_space/sbvar/sbvar_variance_decomposition.c',
'contrib/ms-sbvar/switch_dw/state_space/sbvar/sbvar_impulse_responses.c',
'contrib/ms-sbvar/switch_dw/state_space/sbvar/dw_csminwel.c' ]
shared_module('ms_sbvar_command_line', ms_sbvar_command_line_src, kwargs : mex_ms_sbvar_kwargs, link_with : ms_sbvar_lib, dependencies : [ gsl_dep, matio_dep ])
### Integration tests
# Create a test driver (in bash) that takes as arguments:
# - unique name of the test
# - get_option('build_for')
# - the path to the MATLAB/Octave executable: get_option('build_for') == 'matlab' ? matlab_exe.full_path() : octave_exe.full_path()
# - the version of MATLAB/Octave (needed under MATLAB for determining the batch options)
# - a list of .mod files that must be executed sequentially (i.e. a flattened dependency tree)
# - a separator (e.g. "--")
# - a list of extra files on which the .mod files depend (.inc, .mat, …)
#
# The test driver would do the following:
# - create a temporary directory (with mktemp -d or its equivalent on Windows/macOS), of the form dynare-$(testname).XXXXXX
# - copy the .mod files and the extra files into that temporary directory, keeping the original directory structure (a test dependency may be in another directory)
# - set the environment variable DYNARE_BUILD_DIR
# - run MATLAB/Octave on the .mod files sequentially (using a thin .m wrapper for printing the stack trace in case of error)
# - return the correct exitcode
#
# In the present file, the tests list could be organized in a array of arrays. Each inner array would correspond to a test, and contain:
# - the unique name of the test
# - an array containing the list of .mod files
# - an array containing the list of extra files
# - an array indicating the suite(s) to which this test belongs
# - an optional timeout value?
#
# This tests list would be used in a foreach loop around the test() command.
#
# Other items:
# - Decide whether to automatically delete the temporary directories created by the test driver. Cons: removes test data useful for debugging. Pro: avoid excessive disk filling.
# - See what to do with xvfb-run (see #1892). Maybe try to detect it from meson.build, and pass it optionally to the test driver script
# - Include the k-order unit tests in some suite(s)
# - See if the tests can be automatically disabled if the MATLAB/Octave executable is not found (using the disabler option of find_program?)
# - Deal with other types of tests (integration tests implemented as pure .m scripts, unit tests for .m files)

3
meson_options.txt Normal file
View File

@ -0,0 +1,3 @@
# TODO: Rename this file to meson.options when meson ⩾ 1.1 is required
option('build_for', type : 'combo', choices : [ 'matlab', 'octave' ], description : 'Whether to build for MATLAB or Octave')
option('matlab_path', type : 'string', description : 'Absolute directory containing the MATLAB installation')

@ -1 +1 @@
Subproject commit 3a187076859751ebc3f6a4e43c9ffc3149655191
Subproject commit 140c91e91188ee701d4a185d1ad771c23999f491

193
scripts/get-matlab-version Executable file
View File

@ -0,0 +1,193 @@
#!/bin/bash
# Returns the MATLAB version under the form x.y (not Rnnnn)
# Takes as argument the path to the MATLAB installation
#
# Alternatively, if the first argument is “--hex”, returns the MATLAB version
# as a pseudo-hexadecimal constant (0xMMmm) where MM is the major revision
# number (in decimal) and mm is the minor revision number (in decimal).
# E.g. version 9.14 is returned as 0x0914
# Copyright © 2009-2023 Dynare Team
#
# This file is part of Dynare.
#
# Dynare is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Dynare is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Dynare. If not, see <https://www.gnu.org/licenses/>.
if [[ ($# == 0) || ($# == 1 && $1 == --hex) ]]; then
echo "Usage: $0 [--hex] /path/to/matlab" 2>&1
exit 1
fi
if [[ $1 == --hex ]]; then
hex=true
MATLAB=$2
else
hex=false
MATLAB=$1
fi
if [[ -f ${MATLAB}/VersionInfo.xml ]]; then
# The VersionInfo.xml file is present on all versions since R2017a, on all platforms.
# Extract the version number as x.y, since it is our preferred form, and is
# more robust to future versions.
MATLAB_VERSION=$(sed -En '/<version>/s/.*>([0-9]+\.[0-9]+).*/\1/p' "${MATLAB}/VersionInfo.xml")
elif [[ -f ${MATLAB}/bin/util/mex/version.txt ]]; then
# The bin/util/mex/version.txt file is present on Windows and macOS, at least
# since R2009b. It contains the release number (Rnnnnx).
MATLAB_VERSION=$(< "${MATLAB}/bin/util/mex/version.txt")
elif [[ -f ${MATLAB}/bin/mex || -f ${MATLAB}/bin/mexsh ]]; then
# Works on Linux and macOS until R2018a included. Returns the release number (Rnnnnx).
# Older MATLABs have the version in bin/mex, more recent in bin/mexsh
MATLAB_VERSION=$(sed -En "/^.*full_ver=/s/^.*full_ver='(R[^']+)'.*/\1/p" "${MATLAB}"/bin/mex*)
else
echo "Cant determine the MATLAB version" 2>&1
exit 1
fi
# If needed, convert a release number (Rnnnnx) into a version number (x.y)
case ${MATLAB_VERSION} in
*2023[aA])
MATLAB_VERSION="9.14"
;;
*2022[bB])
MATLAB_VERSION="9.13"
;;
*2022[aA])
MATLAB_VERSION="9.12"
;;
*2021[bB])
MATLAB_VERSION="9.11"
;;
*2021[aA])
MATLAB_VERSION="9.10"
;;
*2020[bB])
MATLAB_VERSION="9.9"
;;
*2020[aA])
MATLAB_VERSION="9.8"
;;
*2019[bB])
MATLAB_VERSION="9.7"
;;
*2019[aA])
MATLAB_VERSION="9.6"
;;
*2018[bB])
MATLAB_VERSION="9.5"
;;
*2018[aA])
MATLAB_VERSION="9.4"
;;
*2017[bB])
MATLAB_VERSION="9.3"
;;
*2017[aA])
MATLAB_VERSION="9.2"
;;
*2016[bB])
MATLAB_VERSION="9.1"
;;
*2016[aA])
MATLAB_VERSION="9.0"
;;
*2015[bB])
MATLAB_VERSION="8.6"
;;
*2015[aA])
MATLAB_VERSION="8.5"
;;
*2014[bB])
MATLAB_VERSION="8.4"
;;
*2014[aA])
MATLAB_VERSION="8.3"
;;
*2013[bB])
MATLAB_VERSION="8.2"
;;
*2013[aA])
MATLAB_VERSION="8.1"
;;
*2012[bB])
MATLAB_VERSION="8.0"
;;
*2012[aA])
MATLAB_VERSION="7.14"
;;
*2011[bB])
MATLAB_VERSION="7.13"
;;
*2011[aA])
MATLAB_VERSION="7.12"
;;
*2010[bB])
MATLAB_VERSION="7.11"
;;
*2010[aA])
MATLAB_VERSION="7.10"
;;
*2009[bB])
MATLAB_VERSION="7.9"
;;
*2009[aA])
MATLAB_VERSION="7.8"
;;
*2008[bB])
MATLAB_VERSION="7.7"
;;
*2008[aA])
MATLAB_VERSION="7.6"
;;
*2007[bB])
MATLAB_VERSION="7.5"
;;
*2007[aA])
MATLAB_VERSION="7.4"
;;
*2006[bB])
MATLAB_VERSION="7.3"
;;
*2006[aA])
MATLAB_VERSION="7.2"
;;
*14[sS][pP]3)
MATLAB_VERSION="7.1"
;;
*14[sS][pP]2)
MATLAB_VERSION="7.0.4"
;;
*14[sS][pP]1)
MATLAB_VERSION="7.0.1"
;;
[rR]14)
MATLAB_VERSION="7.0.0"
;;
esac
# Check that we have an x.y version number
if ! grep -qE '^[0-9.]+$' <<< "${MATLAB_VERSION}"; then
echo "Unknown MATLAB version: ${MATLAB_VERSION}" 2>&1
exit 1
fi
if [[ $hex == true ]]; then
echo -n "0x"
sed -e 's/\([0-9]*\)\.\([0-9]*\).*/Z\1ZZ\2Z/' \
-e 's/Z\([0-9]\)Z/Z0\1Z/g' \
-e 's/[^0-9]//g' <<< "${MATLAB_VERSION}"
else
echo "${MATLAB_VERSION}"
fi

View File

@ -0,0 +1,11 @@
# Meson native file for compiling under Homebrew
[binaries]
cpp = 'g++-13'
c = 'gcc-13'
flex = '/usr/local/opt/flex/bin/flex'
bison = '/usr/local/opt/bison/bin/bison'
[built-in options]
cpp_args = [ '-I/usr/local/include', '-B', '/usr/local/lib' ]
fortran_args = [ '-B', '/Users/sebastien/slicot' ]

View File

@ -0,0 +1,9 @@
# Meson cross file for targeting Windows from Linux
# This is the MATLAB-specific cross file, to be include before the common cross file
[project options]
build_for='matlab'
#matlab_path='/tmp/deps/matlab64/R2018a'
[constants]
#slicot_path='/tmp/deps/lib64/Slicot/without-underscore/lib/'

View File

@ -0,0 +1,11 @@
# Meson cross file for targeting Windows from Linux
# This is the Octave-specific cross file, to be include before the common cross file
[binaries]
#mkoctfile='/tmp/deps/mkoctfile64'
[project options]
build_for='octave'
[constants]
#slicot_path='/tmp/deps/lib64/Slicot/with-underscore/lib/'

45
scripts/windows-cross.ini Normal file
View File

@ -0,0 +1,45 @@
# Meson common cross file for targeting Windows from Linux
# To be included after windows-cross-{matlab,octave}.ini
[constants]
# For the architectural baseline, we follow MSYS2:
# https://www.msys2.org/news/#2022-10-18-new-minimum-hardware-requirements-cpus-from-20067
arch_flags = [ '-march=nocona', '-msahf', '-mtune=generic' ]
[binaries]
cpp = 'x86_64-w64-mingw32-g++-posix'
fortran = 'x86_64-w64-mingw32-gfortran-posix'
c = 'x86_64-w64-mingw32-gcc-posix'
strip = 'x86_64-w64-mingw32-strip'
ar = 'x86_64-w64-mingw32-gcc-ar-posix'
pkgconfig = 'x86_64-w64-mingw32-pkg-config'
[host_machine]
system = 'windows'
cpu_family = 'x86_64'
cpu = 'x86_64'
endian = 'little'
[built-in options]
prefer_static = true
# MSYS2 libraries are now built with -fstack-protector-strong, see:
# https://www.msys2.org/news/#2022-10-23-mingw-packages-now-built-with-d_fortify_source2-and-fstack-protector-strong
# As of 2023-01-03, when linking against HDF5 (and possibly other libraries),
# it is necessary to compile our own code with -fstack-protector to avoid undefined symbols
# at link time.
# Note that specifying -fstack-protector-strong or -fstack-protector-all will lead
# to a dependency on libssp-0.dll (at least when using the MinGW compilers from Debian),
# and there seems to be no easy way of linking it statically.
# Also note that adding this flag is not necessary when building from MSYS2 shell.
# Maybe revisit this once our runners are upgraded to Debian “Bookworm” 12.
cpp_args = [ '-fstack-protector' ] + arch_flags
cpp_link_args = [ '-fstack-protector' ]
fortran_args = arch_flags + [ '-B', slicot_path ]
c_args = arch_flags
[properties]
#sys_root='/tmp/deps/sys_root'
#pkg_config_libdir = '/tmp/deps/sys_root/mingw64/lib/pkgconfig'
#boost_root = '/tmp/deps/sys_root/mingw64'

View File

@ -142,7 +142,7 @@ fi
if [ $# -eq 1 ]; then
case "$1" in
-v | -version | --version)
echo $version_msg 1>&2
echo $version_msg
exit 0
;;
esac