diff --git a/src/ModelTree.cc b/src/ModelTree.cc index 681d8a37bdfa733e2ebe483c74a59017ea139e33..651a276fa61338c0dd4c9d733e565e66ae2b469d 100644 --- a/src/ModelTree.cc +++ b/src/ModelTree.cc @@ -1613,34 +1613,40 @@ 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 switches between x86_64 and arm64 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 { + const string brew_message { + [&mexext]() + { + if (mexext == "mexmaca64") + return "Homebrew for arm64"; + else if (mexext == "mexmaci64") + return "Homebrew for x86_64"; + else + return "Homebrew"; // Fallback message if mexext doesn't match + }() + }; + 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 " << brew_message << " and run `brew install gcc-" << macos_gcc_version << "` in a terminal." << endl; exit(EXIT_FAILURE); } } @@ -1651,7 +1657,9 @@ 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" }; + string opt_flags { gcc_opt_flags }; // Will be modified if we end up using Clang filesystem::path compiler; ostringstream flags; @@ -1692,7 +1700,9 @@ ModelTree::compileMEX(const filesystem::path &output_dir, const string &output_b #ifdef __APPLE__ else if (mexext == "mexmaci64" || mexext == "mexmaca64") { - compiler = findGccOnMacos(mexext); + auto [compiler, is_clang] = findCompilerOnMacos(mexext); + if (is_clang) + opt_flags = clang_opts_flags; flags << " -fno-common -Wl,-twolevel_namespace -undefined error -bundle"; libs += " -lm"; } @@ -1970,9 +1980,10 @@ 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 + /* 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 our custom optimization flags (see dynare#1797) */ - filesystem::path gcc_path {findGccOnMacos(mexext)}; + auto [compiler_path, is_clang] { findCompilerOnMacos(mexext) }; if (setenv("CC", gcc_path.c_str(), 1) != 0) { cerr << "Can't set CC environment variable" << endl; 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