diff --git a/src/ComputingTasks.cc b/src/ComputingTasks.cc index e3969e30a4bdb58703a6acc05655a58fb464e154..f9fb98abfc9d91b04f8e81d31578d9f9c2f87ef6 100644 --- a/src/ComputingTasks.cc +++ b/src/ComputingTasks.cc @@ -2458,7 +2458,7 @@ PlannerObjectiveStatement::writeOutput(ostream &output, const string &basename, for (const auto &temporary_terms_derivative : model_tree.getTemporaryTermsDerivatives()) output << temporary_terms_derivative.size() << "; "; output << "];" << endl; - model_tree.writeStaticFile(basename + ".objective", false, "", {}, {}, false); + model_tree.writeStaticFile(basename + ".objective", false, "", {}, false); } void diff --git a/src/DynamicModel.cc b/src/DynamicModel.cc index c874ef2c2f1125765e35896798351557225a8c61..ba1cebb99cfa4e7223988ea0b13925bca847a1c2 100644 --- a/src/DynamicModel.cc +++ b/src/DynamicModel.cc @@ -2412,7 +2412,7 @@ DynamicModel::computeBlockDynJacobianCols() } void -DynamicModel::writeDynamicFile(const string &basename, bool use_dll, const string &mexext, const filesystem::path &matlabroot, const filesystem::path &dynareroot, bool julia) const +DynamicModel::writeDynamicFile(const string &basename, bool use_dll, const string &mexext, const filesystem::path &matlabroot, bool julia) const { filesystem::path model_dir{basename}; model_dir /= "model"; @@ -2443,7 +2443,7 @@ DynamicModel::writeDynamicFile(const string &basename, bool use_dll, const strin // Legacy representation if (use_dll) - writeModelCFile<true>(basename, mexext, matlabroot, dynareroot); + writeModelCFile<true>(basename, mexext, matlabroot); else if (!julia) // M-files writeDynamicMFile(basename); // The legacy representation is no longer produced for Julia @@ -2454,7 +2454,7 @@ DynamicModel::writeDynamicFile(const string &basename, bool use_dll, const strin // Sparse representation if (use_dll) - writeSparseModelCFiles<true>(basename, mexext, matlabroot, dynareroot); + writeSparseModelCFiles<true>(basename, mexext, matlabroot); else if (julia) writeSparseModelJuliaFiles<true>(basename); else // MATLAB/Octave diff --git a/src/DynamicModel.hh b/src/DynamicModel.hh index a1f4b98d3cfeccb339b70960af81a7d687b5753f..eb6c6f14a0a82840301e3b811d32ad24655cbd0c 100644 --- a/src/DynamicModel.hh +++ b/src/DynamicModel.hh @@ -367,7 +367,7 @@ public: void updateVarAndTrendModel() const; //! Writes dynamic model file (+ bytecode) - void writeDynamicFile(const string &basename, bool use_dll, const string &mexext, const filesystem::path &matlabroot, const filesystem::path &dynareroot, bool julia) const; + void writeDynamicFile(const string &basename, bool use_dll, const string &mexext, const filesystem::path &matlabroot, bool julia) const; //! Writes file containing parameters derivatives template<bool julia> diff --git a/src/DynareMain.cc b/src/DynareMain.cc index e4bd0efdea658cc820417399b70c89f6c782f568..e16c7697e72699b8ba60ebe97d26b21ac7f79379 100644 --- a/src/DynareMain.cc +++ b/src/DynareMain.cc @@ -503,7 +503,8 @@ main(int argc, char **argv) } if (mod_file->use_dll) - ModelTree::initializeMEXCompilationWorkers(max(jthread::hardware_concurrency(), 1U)); + ModelTree::initializeMEXCompilationWorkers(max(jthread::hardware_concurrency(), 1U), + dynareroot, mexext); if (json == JsonOutputPointType::parsing) mod_file->writeJsonOutput(basename, json, json_output_mode, onlyjson); @@ -533,7 +534,7 @@ main(int argc, char **argv) else mod_file->writeMOutput(basename, clear_all, clear_global, no_warn, console, nograph, nointeractive, config_file, check_model_changes, minimal_workspace, compute_xrefs, - mexext, matlabroot, dynareroot, onlymodel, gui, notime); + mexext, matlabroot, onlymodel, gui, notime); /* Ensures that workers are not destroyed before they finish compiling. Also ensures that the preprocessor final message is printed after the end of diff --git a/src/ModFile.cc b/src/ModFile.cc index 9c34b4702c0f7a48f5107a0536438872e26a19df..17ef006ce20d0df17d24a57c935402f484906628 100644 --- a/src/ModFile.cc +++ b/src/ModFile.cc @@ -770,8 +770,7 @@ ModFile::writeMOutput(const string &basename, bool clear_all, bool clear_global, bool console, bool nograph, bool nointeractive, const ConfigFile &config_file, bool check_model_changes, bool minimal_workspace, bool compute_xrefs, const string &mexext, - const filesystem::path &matlabroot, - const filesystem::path &dynareroot, bool onlymodel, bool gui, bool notime) const + const filesystem::path &matlabroot, bool onlymodel, bool gui, bool notime) const { if (basename.empty()) { @@ -1043,11 +1042,11 @@ ModFile::writeMOutput(const string &basename, bool clear_all, bool clear_global, { if (!no_static) { - static_model.writeStaticFile(basename, use_dll, mexext, matlabroot, dynareroot, false); + static_model.writeStaticFile(basename, use_dll, mexext, matlabroot, false); static_model.writeParamsDerivativesFile<false>(basename); } - dynamic_model.writeDynamicFile(basename, use_dll, mexext, matlabroot, dynareroot, false); + dynamic_model.writeDynamicFile(basename, use_dll, mexext, matlabroot, false); dynamic_model.writeParamsDerivativesFile<false>(basename); } @@ -1069,10 +1068,10 @@ ModFile::writeJuliaOutput(const string &basename) const { if (!no_static) { - static_model.writeStaticFile(basename, false, "", {}, {}, true); + static_model.writeStaticFile(basename, false, "", {}, true); static_model.writeParamsDerivativesFile<true>(basename); } - dynamic_model.writeDynamicFile(basename, use_dll, "", {}, {}, true); + dynamic_model.writeDynamicFile(basename, use_dll, "", {}, true); dynamic_model.writeParamsDerivativesFile<true>(basename); } steady_state_model.writeSteadyStateFile(basename, true); diff --git a/src/ModFile.hh b/src/ModFile.hh index a4e69d4d5c521c043f220c8fd033831786d2278d..5e141d5005dbd54e1dceeb096ac992de7b4fa576 100644 --- a/src/ModFile.hh +++ b/src/ModFile.hh @@ -1,5 +1,5 @@ /* - * Copyright © 2006-2022 Dynare Team + * Copyright © 2006-2023 Dynare Team * * This file is part of Dynare. * @@ -173,7 +173,7 @@ public: bool console, bool nograph, bool nointeractive, const ConfigFile &config_file, bool check_model_changes, bool minimal_workspace, bool compute_xrefs, const string &mexext, const filesystem::path &matlabroot, - const filesystem::path &dynareroot, bool onlymodel, bool gui, bool notime) const; + bool onlymodel, bool gui, bool notime) const; void writeJuliaOutput(const string &basename) const; diff --git a/src/ModelTree.cc b/src/ModelTree.cc index 9e780a58beb5c82adaeceaa2e1be0437f3e745fc..02147440749cdacd959f834881dd8e0f7f7efe93 100644 --- a/src/ModelTree.cc +++ b/src/ModelTree.cc @@ -1616,7 +1616,7 @@ ModelTree::findGccOnMacos(const string &mexext) #endif filesystem::path -ModelTree::compileMEX(const filesystem::path &output_dir, const string &output_basename, const string &mexext, const vector<filesystem::path> &input_files, const filesystem::path &matlabroot, const filesystem::path &dynareroot, bool link) const +ModelTree::compileMEX(const filesystem::path &output_dir, const string &output_basename, const string &mexext, const vector<filesystem::path> &input_files, const filesystem::path &matlabroot, bool link) const { assert(!mex_compilation_workers.empty()); @@ -1637,22 +1637,6 @@ ModelTree::compileMEX(const filesystem::path &output_dir, const string &output_b // Octave compiler = matlabroot / "bin" / "mkoctfile"; flags << "--mex"; -#ifdef __APPLE__ - /* On macOS, 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) - { - 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) - { - cerr << "Can't set CXX environment variable" << endl; - exit(EXIT_FAILURE); - } -#endif } else { @@ -1672,22 +1656,8 @@ ModelTree::compileMEX(const filesystem::path &output_dir, const string &output_b << " -shared -Wl,--no-undefined -Wl,-rpath-link," << bin_dir; libs += " -lm"; } - else if (mexext == "mexw64") - { - // Windows - flags << " -static-libgcc -shared"; - // Put the MinGW environment shipped with Dynare in the path - auto mingwpath = dynareroot / "mingw64" / "bin"; - string newpath = "PATH=" + mingwpath.string() + ';' + getenv("PATH"); - /* We can’t use setenv() since it is not available on MinGW. Note - that putenv() seems to make a copy of the string on MinGW, contrary - to what is done on GNU/Linux and macOS. */ - if (putenv(const_cast<char *>(newpath.c_str())) != 0) - { - cerr << "Can't set PATH" << endl; - exit(EXIT_FAILURE); - } - } + else if (mexext == "mexw64") // Windows + flags << " -static-libgcc -shared"; #ifdef __APPLE__ else if (mexext == "mexmaci64" || mexext == "mexmaca64") { @@ -1866,7 +1836,8 @@ ModelTree::getRHSFromLHS(expr_t lhs) const } void -ModelTree::initializeMEXCompilationWorkers(int numworkers) +ModelTree::initializeMEXCompilationWorkers(int numworkers, const filesystem::path &dynareroot, + const string &mexext) { assert(numworkers > 0); assert(mex_compilation_workers.empty()); @@ -1917,6 +1888,44 @@ ModelTree::initializeMEXCompilationWorkers(int numworkers) mex_compilation_cv.notify_all(); } }); + + /* Set some environment variables needed for compilation on Windows/MATLAB + and macOS/Octave. + For Windows/MATLAB, this should be done only once, because otherwise + the PATH variable can become too long and GCC will not be found. */ + if (mexext == "mexw64") + { + // Put the MinGW environment shipped with Dynare in the path + auto mingwpath = dynareroot / "mingw64" / "bin"; + string newpath = "PATH=" + mingwpath.string() + ';' + getenv("PATH"); + /* We can’t use setenv() since it is not available on MinGW. Note that + putenv() seems to make an internal copy of the string on MinGW, + contrary to what is done on GNU/Linux and macOS. */ + if (putenv(const_cast<char *>(newpath.c_str())) != 0) + { + cerr << "Can't set PATH" << endl; + exit(EXIT_FAILURE); + } + } +#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) + { + 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) + { + cerr << "Can't set CXX environment variable" << endl; + exit(EXIT_FAILURE); + } + } +#endif } void diff --git a/src/ModelTree.hh b/src/ModelTree.hh index 551ab744749f949606735d29761c20a8ab382336..5682cf08ade2fd9fb6c813f5142878963417df72 100644 --- a/src/ModelTree.hh +++ b/src/ModelTree.hh @@ -309,7 +309,7 @@ protected: // Writes and compiles dynamic/static file (C version, legacy representation) template<bool dynamic> - void writeModelCFile(const string &basename, const string &mexext, const filesystem::path &matlabroot, const filesystem::path &dynareroot) const; + void writeModelCFile(const string &basename, const string &mexext, const filesystem::path &matlabroot) const; // Writes per-block residuals and temporary terms (incl. for derivatives) template<ExprNodeOutputType output_type> @@ -377,7 +377,7 @@ protected: // Writes and compiles the sparse representation of the model in C template<bool dynamic> - void writeSparseModelCFiles(const string &basename, const string &mexext, const filesystem::path &matlabroot, const filesystem::path &dynareroot) const; + void writeSparseModelCFiles(const string &basename, const string &mexext, const filesystem::path &matlabroot) const; // Writes the sparse representation of the model in Julia // Assumes that the directory <MODFILE>/model/julia/ already exists @@ -583,7 +583,7 @@ private: blocking. The dependency of a linked MEX file upon intermediary objects is nicely handled. Returns the name of the output file (to be reused later as input file if link=false). */ - filesystem::path compileMEX(const filesystem::path &output_dir, const string &output_basename, const string &mexext, const vector<filesystem::path> &input_files, const filesystem::path &matlabroot, const filesystem::path &dynareroot, bool link = true) const; + filesystem::path compileMEX(const filesystem::path &output_dir, const string &output_basename, const string &mexext, const vector<filesystem::path> &input_files, const filesystem::path &matlabroot, bool link = true) const; public: ModelTree(SymbolTable &symbol_table_arg, @@ -625,8 +625,10 @@ public: If no such equation can be found, throws an ExprNode::MatchFailureExpression */ expr_t getRHSFromLHS(expr_t lhs) const; - // Initialize the MEX compilation workers - static void initializeMEXCompilationWorkers(int numworkers); + /* Initialize the MEX compilation workers (and some environment variables + needed for finding GCC) */ + static void initializeMEXCompilationWorkers(int numworkers, const filesystem::path &dynareroot, + const string &mexext); // Waits until the MEX compilation queue is empty static void waitForMEXCompilationWorkers(); @@ -922,8 +924,7 @@ ModelTree::writeModelFileHelper() const template<bool dynamic> void ModelTree::writeModelCFile(const string &basename, const string &mexext, - const filesystem::path &matlabroot, - const filesystem::path &dynareroot) const + const filesystem::path &matlabroot) const { ofstream output; auto open_file = [&output](const filesystem::path &p) @@ -974,7 +975,7 @@ ModelTree::writeModelCFile(const string &basename, const string &mexext, << endl; output.close(); object_files.push_back(compileMEX(model_src_dir, funcname + "_tt" , mexext, { source_tt }, - matlabroot, dynareroot, false)); + matlabroot, false)); const string prototype_main { @@ -1013,7 +1014,7 @@ ModelTree::writeModelCFile(const string &basename, const string &mexext, << endl; output.close(); object_files.push_back(compileMEX(model_src_dir, funcname, mexext, { source_main }, - matlabroot, dynareroot, false)); + matlabroot, false)); } const filesystem::path filename { model_src_dir / (dynamic ? "dynamic.c" : "static.c") }; @@ -1123,8 +1124,7 @@ ModelTree::writeModelCFile(const string &basename, const string &mexext, output.close(); object_files.push_back(filename); - compileMEX(packageDir(basename), dynamic ? "dynamic" : "static", mexext, object_files, matlabroot, - dynareroot); + compileMEX(packageDir(basename), dynamic ? "dynamic" : "static", mexext, object_files, matlabroot); } template<ExprNodeOutputType output_type> @@ -2611,8 +2611,7 @@ ModelTree::writeSparseModelMFiles(const string &basename) const template<bool dynamic> void ModelTree::writeSparseModelCFiles(const string &basename, const string &mexext, - const filesystem::path &matlabroot, - const filesystem::path &dynareroot) const + const filesystem::path &matlabroot) const { constexpr ExprNodeOutputType output_type {dynamic ? ExprNodeOutputType::CSparseDynamicModel : ExprNodeOutputType::CSparseStaticModel}; auto [d_sparse_output, tt_sparse_output] = writeModelFileHelper<output_type>(); @@ -2654,7 +2653,7 @@ ModelTree::writeSparseModelCFiles(const string &basename, const string &mexext, output.close(); auto power_deriv_object {compileMEX(model_src_dir, (prefix + "power_deriv"), mexext, { power_deriv_src }, - matlabroot, dynareroot, false)}; + matlabroot, false)}; size_t ttlen {0}; @@ -2731,7 +2730,7 @@ ModelTree::writeSparseModelCFiles(const string &basename, const string &mexext, << endl; output.close(); tt_object_files.push_back(compileMEX(model_src_dir, funcname + "_tt", mexext, { source_tt }, - matlabroot, dynareroot, false)); + matlabroot, false)); const string prototype_main {"void " + funcname + "(const double *restrict y, const double *restrict x, const double *restrict params" + ss_argin + ", const double *restrict T, double *restrict " + (i == 0 ? "residual" : "g" + to_string(i) + "_v") + ")"}; @@ -2754,7 +2753,7 @@ ModelTree::writeSparseModelCFiles(const string &basename, const string &mexext, << endl; output.close(); auto main_object_file {compileMEX(model_src_dir, funcname, mexext, { source_main }, - matlabroot, dynareroot, false)}; + matlabroot, false)}; const filesystem::path source_mex { model_src_dir / (funcname + "_mex.c")}; int nargin {5+static_cast<int>(dynamic)+3*static_cast<int>(i == 1)}; @@ -2837,7 +2836,7 @@ ModelTree::writeSparseModelCFiles(const string &basename, const string &mexext, vector<filesystem::path> mex_input_files { power_deriv_object, main_object_file, source_mex }; for (int j {0}; j <= i; j++) mex_input_files.push_back(tt_object_files[j]); - compileMEX(mex_dir, funcname, mexext, mex_input_files, matlabroot, dynareroot); + compileMEX(mex_dir, funcname, mexext, mex_input_files, matlabroot); } if (block_decomposed) @@ -2926,7 +2925,7 @@ ModelTree::writeSparseModelCFiles(const string &basename, const string &mexext, } output << "}" << endl; output.close(); - compileMEX(block_dir, funcname, mexext, { source_mex, power_deriv_object }, matlabroot, dynareroot); + compileMEX(block_dir, funcname, mexext, { source_mex, power_deriv_object }, matlabroot); } } } diff --git a/src/StaticModel.cc b/src/StaticModel.cc index 92e5f2606942840a5e6b95820580625c1e2b132c..a6d87a9f7e036202c1b829ea30158f8c37c53b03 100644 --- a/src/StaticModel.cc +++ b/src/StaticModel.cc @@ -456,7 +456,7 @@ StaticModel::writeStaticMCompatFile(const string &basename) const } void -StaticModel::writeStaticFile(const string &basename, bool use_dll, const string &mexext, const filesystem::path &matlabroot, const filesystem::path &dynareroot, bool julia) const +StaticModel::writeStaticFile(const string &basename, bool use_dll, const string &mexext, const filesystem::path &matlabroot, bool julia) const { filesystem::path model_dir{basename}; model_dir /= "model"; @@ -487,7 +487,7 @@ StaticModel::writeStaticFile(const string &basename, bool use_dll, const string // Legacy representation if (use_dll) - writeModelCFile<false>(basename, mexext, matlabroot, dynareroot); + writeModelCFile<false>(basename, mexext, matlabroot); else if (!julia) // M-files writeStaticMFile(basename); // The legacy representation is no longer produced for Julia @@ -498,7 +498,7 @@ StaticModel::writeStaticFile(const string &basename, bool use_dll, const string // Sparse representation if (use_dll) - writeSparseModelCFiles<false>(basename, mexext, matlabroot, dynareroot); + writeSparseModelCFiles<false>(basename, mexext, matlabroot); else if (julia) writeSparseModelJuliaFiles<false>(basename); else // MATLAB/Octave diff --git a/src/StaticModel.hh b/src/StaticModel.hh index 8f02c0047373fb20b521ec4a7c392d45009ddda8..6971f12ae8e6d293037b506666c6ab57552aa030 100644 --- a/src/StaticModel.hh +++ b/src/StaticModel.hh @@ -125,7 +125,7 @@ public: void computingPass(int derivsOrder, int paramsDerivsOrder, const eval_context_t &eval_context, bool no_tmp_terms, bool block); //! Writes static model file (+ bytecode) - void writeStaticFile(const string &basename, bool use_dll, const string &mexext, const filesystem::path &matlabroot, const filesystem::path &dynareroot, bool julia) const; + void writeStaticFile(const string &basename, bool use_dll, const string &mexext, const filesystem::path &matlabroot, bool julia) const; //! Write JSON Output (used by PlannerObjectiveStatement) void writeJsonOutput(ostream &output) const;