diff --git a/src/ModelTree.cc b/src/ModelTree.cc index 681d8a37bdfa733e2ebe483c74a59017ea139e33..675a6e435db06fc9bb2e22893691dfad63ffd89f 100644 --- a/src/ModelTree.cc +++ b/src/ModelTree.cc @@ -1613,34 +1613,36 @@ ModelTree::matlab_arch(const string &mexext) } #ifdef __APPLE__ -filesystem::path -ModelTree::findGccOnMacos(const string &mexext) + +pair<filesystem::path, bool> +ModelTree::findCompilerOnMacos(const string &mexext) { + /* Try to find gcc, otherwise use Apple’s clang compiler. + Homebrew binaries are located in /usr/local/bin/ on x86_64 systems and in + /opt/homebrew/bin/ on arm64 systems. + Apple’s clang is located both in /usr/bin/gcc and /usr/bin/clang, it + automatically selects x86_64 or arm64 depending on the compile-time + environment. */ const string macos_gcc_version {"13"}; - char dynare_preprocessor_path[PATH_MAX]; - uint32_t size = PATH_MAX; - filesystem::path local_gcc_path; - if (_NSGetExecutablePath(dynare_preprocessor_path, &size) == 0) - { - string s = dynare_preprocessor_path; - local_gcc_path = s.substr(0, s.find_last_of("/")) + "/../.brew/bin/gcc-" + macos_gcc_version; - } - // if user did not choose to install gcc locally via the pkg-installer then we need to find GNU gcc - // homebrew binaries are located in /usr/local/bin/ on x86_64 systems and in /opt/homebrew/bin/ on arm64 systems - if (exists(local_gcc_path)) - return local_gcc_path; - else if (filesystem::path global_gcc_path {"/usr/local/bin/gcc-" + macos_gcc_version}; - exists(global_gcc_path) && mexext == "mexmaci64") - return global_gcc_path; + if (filesystem::path global_gcc_path {"/usr/local/bin/gcc-" + macos_gcc_version}; + exists(global_gcc_path) && mexext == "mexmaci64") + return { global_gcc_path, false }; else if (filesystem::path global_gcc_path {"/opt/homebrew/bin/gcc-" + macos_gcc_version}; exists(global_gcc_path) && mexext == "mexmaca64") - return global_gcc_path; + return { global_gcc_path, false }; + else if (filesystem::path global_clang_path {"/usr/bin/clang"}; exists(global_clang_path)) + return { global_clang_path, true }; else { cerr << "ERROR: You must install gcc-" << macos_gcc_version << " on your system before using the `use_dll` option of Dynare. " - << "If using MATLAB, you can do this via the Dynare installation package. If using Octave, you should run `brew install gcc-" << macos_gcc_version << "` in a terminal." << endl; + << "You should install Homebrew"; + if (mexext == "mexmaca64") + cerr << " for arm64"; + else if (mexext == "mexmaci64") + cerr << " for x86_64"; + cerr << " and run `brew install gcc-" << macos_gcc_version << "` in a terminal." << endl; exit(EXIT_FAILURE); } } @@ -1651,11 +1653,13 @@ ModelTree::compileMEX(const filesystem::path &output_dir, const string &output_b { assert(!mex_compilation_workers.empty()); - const string opt_flags = "-O3 -g0 --param ira-max-conflict-table-size=1 -fno-forward-propagate -fno-gcse -fno-dce -fno-dse -fno-tree-fre -fno-tree-pre -fno-tree-cselim -fno-tree-dse -fno-tree-dce -fno-tree-pta -fno-gcse-after-reload"; + const string gcc_opt_flags { "-O3 -g0 --param ira-max-conflict-table-size=1 -fno-forward-propagate -fno-gcse -fno-dce -fno-dse -fno-tree-fre -fno-tree-pre -fno-tree-cselim -fno-tree-dse -fno-tree-dce -fno-tree-pta -fno-gcse-after-reload" }; + const string clang_opt_flags { "-O3 -g0 --param ira-max-conflict-table-size=1 -Wno-unused-command-line-argument" }; filesystem::path compiler; ostringstream flags; string libs; + bool is_clang {false}; if (matlabroot.empty()) { @@ -1692,7 +1696,7 @@ ModelTree::compileMEX(const filesystem::path &output_dir, const string &output_b #ifdef __APPLE__ else if (mexext == "mexmaci64" || mexext == "mexmaca64") { - compiler = findGccOnMacos(mexext); + tie(compiler, is_clang) = findCompilerOnMacos(mexext); flags << " -fno-common -Wl,-twolevel_namespace -undefined error -bundle"; libs += " -lm"; } @@ -1727,7 +1731,7 @@ ModelTree::compileMEX(const filesystem::path &output_dir, const string &output_b cmd << user_set_compiler << " "; if (user_set_subst_flags.empty()) - cmd << opt_flags << " " << flags.str() << " "; + cmd << (is_clang ? clang_opt_flags : gcc_opt_flags) << " " << flags.str() << " "; else cmd << user_set_subst_flags << " "; @@ -1970,16 +1974,17 @@ ModelTree::initializeMEXCompilationWorkers(int numworkers, const filesystem::pat #ifdef __APPLE__ else if (mexext == "mex") { - /* On macOS, with Octave, enforce GCC, otherwise Clang will be used, and - it does not accept our custom optimization flags (see dynare#1797) */ - filesystem::path gcc_path {findGccOnMacos(mexext)}; - if (setenv("CC", gcc_path.c_str(), 1) != 0) + /* On macOS, with Octave, enforce our compiler. In particular this is + necessary if we’ve selected GCC; otherwise Clang will be used, and + it does not accept the same optimization flags (see dynare#1797) */ + auto [compiler_path, is_clang] { findCompilerOnMacos(mexext) }; + if (setenv("CC", compiler_path.c_str(), 1) != 0) { cerr << "Can't set CC environment variable" << endl; exit(EXIT_FAILURE); } // We also define CXX, because that is used for linking - if (setenv("CXX", gcc_path.c_str(), 1) != 0) + if (setenv("CXX", compiler_path.c_str(), 1) != 0) { cerr << "Can't set CXX environment variable" << endl; exit(EXIT_FAILURE); diff --git a/src/ModelTree.hh b/src/ModelTree.hh index 0c0044a4b8167cd6c2b5a749068e446561cd8525..60192407e24622cb80c78e9fb19b493b5baae8a9 100644 --- a/src/ModelTree.hh +++ b/src/ModelTree.hh @@ -594,8 +594,9 @@ private: //! Returns the name of the MATLAB architecture given the extension used for MEX files static string matlab_arch(const string &mexext); #ifdef __APPLE__ - //! Finds a suitable GCC compiler on macOS - static filesystem::path findGccOnMacos(const string &mexext); + /* Finds a suitable compiler on macOS. + The boolean is false if this is GCC and true if this is Clang */ + static pair<filesystem::path, bool> findCompilerOnMacos(const string &mexext); #endif /* Compiles a MEX file (if link=true) or an object file to be linked later into a MEX file (if link=false). The compilation is done in separate