From ae49cddd1dc82a74e02e477d9e41b4bcbf8f228b Mon Sep 17 00:00:00 2001
From: Houtan Bastani <houtan@dynare.org>
Date: Fri, 28 Aug 2015 17:58:43 +0200
Subject: [PATCH] preprocessor: allow for [paths] block in dynare config file.
 closes #1039

---
 doc/dynare.texi                    |  29 ++++++++
 preprocessor/ConfigFile.cc         | 109 +++++++++++++++++++++++++----
 preprocessor/ConfigFile.hh         |  19 ++++-
 preprocessor/DynareMain.cc         |   7 ++
 tests/parallel/dynare.ini          |   3 +
 tests/parallel/folder/endovars.mod |   1 +
 tests/parallel/ls2003.mod          |   3 +-
 7 files changed, 155 insertions(+), 16 deletions(-)
 create mode 100644 tests/parallel/folder/endovars.mod

diff --git a/doc/dynare.texi b/doc/dynare.texi
index 3b1519d22d..9f6fd2117b 100644
--- a/doc/dynare.texi
+++ b/doc/dynare.texi
@@ -9571,6 +9571,35 @@ GlobalInitFile = /home/usern/dynare/myInitFile.m
 
 @end deffn
 
+@deffn {Configuration block} [paths]
+
+@descriptionhead
+
+The @code{[paths]} block can be used to specify paths that will be
+used when running dynare.
+
+@optionshead
+
+@table @code
+
+@item Include = @var{PATH}
+A colon-separated path to use when searching for files to include via
+@ref{@@#include}. Paths specified via @ref{-I} take priority over
+paths specified here, while these paths take priority over those
+specified by @ref{@@#includepath}.
+
+@end table
+
+@examplehead
+
+@example
+[paths]
+Include = /path/to/folder/containing/modfiles:/path/to/another/folder
+
+@end example
+
+@end deffn
+
 @node Parallel Configuration
 @section Parallel Configuration
 
diff --git a/preprocessor/ConfigFile.cc b/preprocessor/ConfigFile.cc
index 0a3ef5eed0..7186adc0df 100644
--- a/preprocessor/ConfigFile.cc
+++ b/preprocessor/ConfigFile.cc
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2010-2013 Dynare Team
+ * Copyright (C) 2010-2015 Dynare Team
  *
  * This file is part of Dynare.
  *
@@ -40,6 +40,16 @@ Hook::Hook(string &global_init_file_arg)
   hooks["global_init_file"] = global_init_file_arg;
 }
 
+Path::Path(vector<string> &includepath_arg)
+{
+  if (includepath_arg.empty())
+    {
+      cerr << "ERROR: The Path must have an Include argument." << endl;
+      exit(EXIT_FAILURE);
+    }
+  paths["include"] = includepath_arg;
+}
+
 SlaveNode::SlaveNode(string &computerName_arg, string port_arg, int minCpuNbr_arg, int maxCpuNbr_arg, string &userName_arg,
                      string &password_arg, string &remoteDrive_arg, string &remoteDirectory_arg,
                      string &dynarePath_arg, string &matlabOctavePath_arg, bool singleCompThread_arg,
@@ -151,6 +161,7 @@ ConfigFile::getConfigFileInfo(const string &config_file)
   string name, computerName, port, userName, password, remoteDrive,
     remoteDirectory, dynarePath, matlabOctavePath, operatingSystem,
     global_init_file;
+  vector<string> includepath;
   int minCpuNbr = 0, maxCpuNbr = 0;
   bool singleCompThread = true;
   member_nodes_t member_nodes;
@@ -158,6 +169,7 @@ ConfigFile::getConfigFileInfo(const string &config_file)
   bool inHooks = false;
   bool inNode = false;
   bool inCluster = false;
+  bool inPaths = false;
   while (configFile->good())
     {
       string line;
@@ -166,11 +178,17 @@ ConfigFile::getConfigFileInfo(const string &config_file)
       if (line.empty())
         continue;
 
-      if (!line.compare("[node]") || !line.compare("[cluster]") || !line.compare("[hooks]"))
+      if (!line.compare("[node]")
+          || !line.compare("[cluster]")
+          || !line.compare("[hooks]")
+          || !line.compare("[paths]"))
         {
           if (!global_init_file.empty())
             // we were just in [hooks]
             addHooksConfFileElement(global_init_file);
+          else if (!includepath.empty())
+            // we were just in [paths]
+            addPathsConfFileElement(includepath);
           else
             // we were just in [node] or [cluster]
             addParallelConfFileElement(inNode, inCluster, member_nodes, name,
@@ -185,24 +203,34 @@ ConfigFile::getConfigFileInfo(const string &config_file)
               inHooks = true;
               inNode = false;
               inCluster = false;
+              inPaths = false;
+            }
+          else if (!line.compare("[node]"))
+            {
+              inHooks = false;
+              inNode = true;
+              inCluster = false;
+              inPaths = false;
+            }
+          else if (!line.compare("[paths]"))
+            {
+              inHooks = false;
+              inNode = false;
+              inCluster = false;
+              inPaths = true;
             }
           else
-            if (!line.compare("[node]"))
-              {
-                inHooks = false;
-                inNode = true;
-                inCluster = false;
-              }
-            else
-              {
-                inHooks = false;
-                inNode = false;
-                inCluster = true;
-              }
+            {
+              inHooks = false;
+              inNode = false;
+              inCluster = true;
+              inPaths = false;
+            }
 
           name = userName = computerName = port = password = remoteDrive
             = remoteDirectory = dynarePath = matlabOctavePath
             = operatingSystem = global_init_file = "";
+          includepath.clear();
           minCpuNbr = maxCpuNbr = 0;
           singleCompThread = true;
           member_nodes.clear();
@@ -233,6 +261,30 @@ ConfigFile::getConfigFileInfo(const string &config_file)
                 cerr << "ERROR: Unrecognized option " << tokenizedLine.front() << " in [hooks] block." << endl;
                 exit(EXIT_FAILURE);
               }
+          else if (inPaths)
+            if (!tokenizedLine.front().compare("Include"))
+              if (includepath.empty())
+                {
+                  vector<string> tokenizedPath;
+                  split(tokenizedPath, tokenizedLine.back(), is_any_of(":"), token_compress_on);
+                  for (vector<string>::iterator it = tokenizedPath.begin();
+                       it != tokenizedPath.end(); it++ )
+                    if (!it->empty())
+                      {
+                        trim(*it);
+                        includepath.push_back(*it);
+                      }
+                }
+              else
+                {
+                  cerr << "ERROR: May not have more than one Include option in [paths] block." << endl;
+                  exit(EXIT_FAILURE);
+                }
+            else
+              {
+                cerr << "ERROR: Unrecognized option " << tokenizedLine.front() << " in [paths] block." << endl;
+                exit(EXIT_FAILURE);
+              }
           else
             if (!tokenizedLine.front().compare("Name"))
               name = tokenizedLine.back();
@@ -379,12 +431,15 @@ ConfigFile::getConfigFileInfo(const string &config_file)
 
   if (!global_init_file.empty())
     addHooksConfFileElement(global_init_file);
+  else if (!includepath.empty())
+    addPathsConfFileElement(includepath);
   else
     addParallelConfFileElement(inNode, inCluster, member_nodes, name,
                                computerName, port, minCpuNbr, maxCpuNbr, userName,
                                password, remoteDrive, remoteDirectory,
                                dynarePath, matlabOctavePath, singleCompThread,
                                operatingSystem);
+
   configFile->close();
   delete configFile;
 }
@@ -401,6 +456,18 @@ ConfigFile::addHooksConfFileElement(string &global_init_file)
     hooks.push_back(new Hook(global_init_file));
 }
 
+void
+ConfigFile::addPathsConfFileElement(vector<string> &includepath)
+{
+  if (includepath.empty())
+    {
+      cerr << "ERROR: The path to be included must be passed to the Include option." << endl;
+      exit(EXIT_FAILURE);
+    }
+  else
+    paths.push_back(new Path(includepath));
+}
+
 void
 ConfigFile::addParallelConfFileElement(bool inNode, bool inCluster, member_nodes_t member_nodes,
                                        string &name, string &computerName, string port, int minCpuNbr, int maxCpuNbr, string &userName,
@@ -609,6 +676,20 @@ ConfigFile::transformPass()
     it->second /= weight_denominator;
 }
 
+vector<string>
+ConfigFile::getIncludePaths() const
+{
+  vector<string> include_paths;
+  for (vector<Path *>::const_iterator it = paths.begin() ; it != paths.end(); it++)
+    {
+      map <string, vector<string> > pathmap = (*it)->get_paths();
+      for (map <string, vector<string> >::const_iterator mapit = pathmap.begin() ; mapit != pathmap.end(); mapit++)
+        for (vector<string>::const_iterator vecit = mapit->second.begin(); vecit != mapit->second.end(); vecit++)
+          include_paths.push_back(*vecit);
+    }
+  return include_paths;
+}
+
 void
 ConfigFile::writeHooks(ostream &output) const
 {
diff --git a/preprocessor/ConfigFile.hh b/preprocessor/ConfigFile.hh
index c7dd85ef51..0a6d2e6c03 100644
--- a/preprocessor/ConfigFile.hh
+++ b/preprocessor/ConfigFile.hh
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2010-2012 Dynare Team
+ * Copyright (C) 2010-2015 Dynare Team
  *
  * This file is part of Dynare.
  *
@@ -40,6 +40,17 @@ public:
   inline map<string, string>get_hooks() { return hooks; };
 };
 
+class Path
+{
+public:
+    Path(vector<string> &includepath_arg);
+  ~Path();
+private:
+  map<string, vector<string> > paths;
+public:
+  inline map<string, vector<string> >get_paths() { return paths; };
+};
+
 class SlaveNode
 {
   friend class ConfigFile;
@@ -91,12 +102,16 @@ private:
   string firstClusterName;
   //! Hooks
   vector<Hook *> hooks;
+  //! Paths
+  vector<Path *> paths;
   //! Cluster Table
   map<string, Cluster *> clusters;
   //! Node Map
   map<string, SlaveNode *> slave_nodes;
   //! Add Hooks
   void addHooksConfFileElement(string &global_init_file);
+  //! Add Paths
+  void addPathsConfFileElement(vector<string> &includepath);
   //! Add a SlaveNode or a Cluster object
   void addParallelConfFileElement(bool inNode, bool inCluster, member_nodes_t member_nodes, string &name,
                                   string &computerName, string port, int minCpuNbr, int maxCpuNbr, string &userName,
@@ -110,6 +125,8 @@ public:
   void checkPass(WarningConsolidation &warnings) const;
   //! Check Pass
   void transformPass();
+  //! Get Path Info
+  vector<string> getIncludePaths() const;
   //! Write any hooks
   void writeHooks(ostream &output) const;
   //! Create options_.parallel structure, write options
diff --git a/preprocessor/DynareMain.cc b/preprocessor/DynareMain.cc
index 666e43e96a..e1804b334a 100644
--- a/preprocessor/DynareMain.cc
+++ b/preprocessor/DynareMain.cc
@@ -288,6 +288,13 @@ main(int argc, char **argv)
   config_file.checkPass(warnings);
   config_file.transformPass();
 
+  // If Include option was passed to the [paths] block of the config file, add
+  // it to paths before macroprocessing
+  vector<string> config_include_paths = config_file.getIncludePaths();
+  for (vector<string>::const_iterator it = config_include_paths.begin();
+       it != config_include_paths.end(); it++)
+    path.push_back(*it);
+
   // Do macro processing
   MacroDriver m;
 
diff --git a/tests/parallel/dynare.ini b/tests/parallel/dynare.ini
index 2132a28cbe..0d400f636c 100644
--- a/tests/parallel/dynare.ini
+++ b/tests/parallel/dynare.ini
@@ -9,3 +9,6 @@ GlobalInitFile = ./init.m
 Name = n1
 ComputerName = localhost
 CPUnbr = [1:8]
+
+[paths]
+Include = folder
\ No newline at end of file
diff --git a/tests/parallel/folder/endovars.mod b/tests/parallel/folder/endovars.mod
new file mode 100644
index 0000000000..2ee76dfb79
--- /dev/null
+++ b/tests/parallel/folder/endovars.mod
@@ -0,0 +1 @@
+var y y_s R pie dq pie_s de A y_obs pie_obs R_obs;
diff --git a/tests/parallel/ls2003.mod b/tests/parallel/ls2003.mod
index 20f4d6856d..6c8043358f 100644
--- a/tests/parallel/ls2003.mod
+++ b/tests/parallel/ls2003.mod
@@ -1,4 +1,5 @@
-var y y_s R pie dq pie_s de A y_obs pie_obs R_obs;
+@#include "endovars.mod"
+
 varexo e_R e_q e_ys e_pies e_A;
 
 parameters psi1 psi2 psi3 rho_R tau alpha rr k rho_q rho_A rho_ys rho_pies;
-- 
GitLab