From fcc08f8b7ebdbf15c569b8e89db63ccee8c5e281 Mon Sep 17 00:00:00 2001
From: Giovanni Bussi <bussi@MBP-30680.homenet.telecomitalia.it>
Date: Sun, 13 May 2018 17:21:27 +0200
Subject: [PATCH] Allow direct #include from other modules

I allowed include commands such as #include "../module/file.h",
only in the case where module is an used module (or the current module).

Later I would like to see if it is possible to eliminate
the "make links" stuff completely.
---
 src/maketools/make.module |  4 +++
 src/maketools/plumedcheck | 54 +++++++++++++++++++++++++++++++--------
 2 files changed, 47 insertions(+), 11 deletions(-)

diff --git a/src/maketools/make.module b/src/maketools/make.module
index 6e846e049..22826817e 100644
--- a/src/maketools/make.module
+++ b/src/maketools/make.module
@@ -90,6 +90,10 @@ codecheck:
 astyle:
 	cd ../ ; ./astyle.sh $(CURDIR)
 
+.PHONY: show_used_modules
+show_used_modules:
+	@echo used_modules $(USE)
+
 
 
 # generic makefile rules
diff --git a/src/maketools/plumedcheck b/src/maketools/plumedcheck
index d8f5cb44c..97a068c86 100755
--- a/src/maketools/plumedcheck
+++ b/src/maketools/plumedcheck
@@ -143,6 +143,10 @@ BEGIN{
   core_modules["tools"]=1
   core_modules["reference"]=1
 
+# declare these as empty arrays
+  used_modules[0]=1
+  delete used_modules
+
 # create tmp dir for future usage
   "mktemp -dt plumed.XXXXXX" | getline tmpdir
 
@@ -164,6 +168,12 @@ BEGINFILE{
 #  found_include_system_header=0
 #  report_include_system_headers=0
 
+# guess type of file from extension
+  filetype=""
+  if(match(FILENAME,".*\\.h")) filetype="header"
+  if(match(FILENAME,".*\\.cpp")) filetype="source"
+  if(match(FILENAME,".*\\.ac")) filetype="autoconf"
+
 # guess module name from directory
 # only works when path is specified
   filename=FILENAME
@@ -171,13 +181,15 @@ BEGINFILE{
   gsub("^[.]/","",filename);
   if(!match(filename,".*/.*")) filename=ENVIRON["PWD"] "/" filename
   gsub("/+","/",filename); # fix multiple slashes
+  filepath=filename
   module=""
   nf=split(filename,ff,"/");
   if(nf>1){
     module=ff[nf-1];
     if(!(module in module_type)){
-      path=filename
+      path=filepath
       sub("/.*$","/module.type",path);
+# detect module type
       if((getline < path > 0) ){
         module_type[module]=$0
         information("module_type","module " module " has type '" module_type[module] "'" );
@@ -188,17 +200,28 @@ BEGINFILE{
         if(!(module_type[module] in allowed_module_types)) error("wrong_module_type","module " module " has a wrong module type '" module_type[module] "'");
       }
     }
+# detect used modules
+    if(filetype=="source" || filetype=="header") {
+      if(!(module in used_modules)){
+        tempfile = tmpdir "/used_modules." module
+        path=filepath
+        sub("/[^/]*$","",path);
+# an explicit cd is required since the command might be run from a different directory
+        system("cd " path "; make show_used_modules 2>/dev/null > " tempfile)
+        if(getline < tempfile >0 && $1=="used_modules") for(i=2;i<=NF;i++) used_modules[module][$i]=1
+        system("rm " tempfile)
+        used_modules_here=""
+        if(isarray(used_modules[module])) {
+          for(mod in used_modules[module]) used_modules_here=used_modules_here " " mod
+        }
+        information("used_modules",module " uses:" used_modules_here)
+      }
+    }
   }
   file=ff[nf];
   filebase=file;
   sub("\\..*","",filebase);
 
-# guess type of file from extension
-  filetype=""
-  if(match(FILENAME,".*\\.h")) filetype="header"
-  if(match(FILENAME,".*\\.cpp")) filetype="source"
-  if(match(FILENAME,".*\\.ac")) filetype="autoconf"
-
 # checks that are done only on some modules will be skipped if the module name is not known
   if((filetype=="source" || filetype=="header") && !module) information("missing_module_name","module name is not know, some check will be skipped");
 
@@ -246,13 +269,22 @@ BEGINFILE{
 # detect copyright lines
     if($0=="/* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++") in_copyright=1;
 # check direct inclusion of headers that would bypass module dependencies
+    if(module && match($0,"^# *include +\\\"\\.\\.")) {
+      included_module_name=$0;
+      sub("^# *include +\\\"\\.\\./","",included_module_name);
+      sub("/.*$","",included_module_name);
 # DOC: :include_dots:
-# DOC: It is considered an error to include a file using a path starting with `..`.
-# DOC: The reason is that this would allow to include PLUMED modules that have not been
-# DOC: explicitly declared in the Makefile (e.g. using `#include "../tools/Vector.h"`).
+# DOC: It is considered an error to include a file using a path starting with `../dir`,
+# DOC: where `dir` is not the name of a module explicitly declared as used in the Makefile.
 # DOC: This might result in undetected dependences between the modules, which means that
 # DOC: by enabling some set of modules one would result in a non-linkable library.
-    if(match($0,"^# *include +\\\"\\.\\.")) error("include_dots","cannot include files using .. path");
+# DOC: As an exception, one can include files such as `../dir` where `dir` is the current module
+# DOC: since this would obviously create no problem.
+# DOC: This would simplify life when moving files from a directory to another.
+      if(module != included_module_name) # ignore case where module is the same
+      if(!isarray(used_modules[module]) || (included_module_name in used_modules[module])==0)
+        error("include_dots","wrong include. Module '" included_module_name "' is not used.");
+    }
 
 # check if there is anything behind copyright and preprocessor commands
     if(!in_copyright && !match($0,"^# *include") && !match($0,"^# *ifndef") && !match($0,"^# *endif") && !match($0,"^# *define") && !match($0,"^ *$")) found_non_preprocessor_lines=1
-- 
GitLab