From bff80c0eaf39ba5c05be92900bbf09b16c7f9452 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Villemot?= <sebastien@dynare.org> Date: Mon, 20 Mar 2023 17:54:32 +0100 Subject: [PATCH] use_dll: under Windows, append MinGW location to the PATH variable only once Previously, the MinGW location was appended multiple times to the PATH variable, which in some cases would make the variable too long and thus dysfunctional. The variable is now initialized once when the worker threads are created. By the way, move the macOS+Octave environment variable initializations to the same place, for consistency. --- src/ComputingTasks.cc | 2 +- src/DynamicModel.cc | 6 ++-- src/DynamicModel.hh | 2 +- src/DynareMain.cc | 5 +-- src/ModFile.cc | 11 +++---- src/ModFile.hh | 4 +-- src/ModelTree.cc | 77 ++++++++++++++++++++++++------------------- src/ModelTree.hh | 35 ++++++++++---------- src/StaticModel.cc | 6 ++-- src/StaticModel.hh | 2 +- 10 files changed, 79 insertions(+), 71 deletions(-) diff --git a/src/ComputingTasks.cc b/src/ComputingTasks.cc index e3969e30..f9fb98ab 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 c874ef2c..ba1cebb9 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 a1f4b98d..eb6c6f14 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 e4bd0efd..e16c7697 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 9c34b470..17ef006c 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 a4e69d4d..5e141d50 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 9e780a58..02147440 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 551ab744..5682cf08 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 92e5f260..a6d87a9f 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 8f02c004..6971f12a 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; -- GitLab