#! /bin/bash # vim: ft=awk # DOC: \page Plumedcheck Validating syntax with plumedcheck # DOC: # DOC: Here is a list of coding rules that are enforced by the plumedcheck script. # DOC: These rules are necessary to be sure that the codebase is consistent. # DOC: Code not satisfying these rules will make the automatic test on travis-ci fail. # DOC: In the following we provide a list of errors that are detected automatically, # DOC: together with a short explanation of the reason we consider these as errors. # DOC: The names used are the same ones reported by plumedcheck. # DOC: The `plumedcheck` script can be used as is, but the simplest way to obtain a report # DOC: is to go to the `buildroot/src` directory and type `make plumedcheck`. # DOC: In case you think you have a valid reason for a code not passing the check # DOC: to be merged, please contact the developers. TEMP=$(mktemp -t plumed.XXXXXX) cat > $TEMP << \EOF # print usage function usage(){ print "Usage:" print " plumedcheck [options] [files]" print print "Possible options are:" print " -h/--help: print help" print " --global-check: run global check; requires all sources and ac files to be passed" print print "Files can be" print " .h files: interpreted as c++ header file" print " .cpp files: interpreted as c++ source file" print " .ac files: interpreted as autoconf files" exit(0) } # print message function message(severity,id,mes){ if(global_check){ printf("[global_check] (%s) :plmd_%s: %s\n",severity,id,mes) > "/dev/stderr"; } else { printf("[%s:%s] (%s) :plmd_%s: %s\n",FILENAME,FNR,severity,id,mes) > "/dev/stderr"; } } # print error message # error messages make codecheck fail function error(id,mes){ message("error",id,mes) } # print information message # information messages are neutral function information(id,mes){ message("information",id,mes) } # print style message # style messages are possibly bad ideas but do not trigger errors function style(id,mes){ message("style",id,mes) } BEGIN{ # interpret command line arguments newargv[0]=ARGV[0] pre="" opt_global_check=0 astyle="astyle" autoconf="autoconf" for(i=1;i<ARGC;i++){ opt=pre ARGV[i] pre="" switch(opt){ case "-h": case "--help": usage(); break; case "--global-check": opt_global_check=1; break; case /--astyle=.*/: sub("^--astyle=","",opt); astyle=opt; case /--astyle-options=.*/: sub("^--astyle-options=","",opt); astyle_options=opt; break; break; case /--autoconf=.*/: sub("^--autoconf=","",opt); autoconf=opt; case /-.*/: print "Unknown option " opt; exit(1) default: newargv[length(newargv)]=opt } } # copy new command arguments ARGC=length(newargv) for(i=1;i<ARGC;i++) ARGV[i]=newargv[i] # no arguments, print help if(ARGC==1) usage() # setup: # list of deprecated c++ headers deprecated_headers["complex.h"]=1 deprecated_headers["ctype.h"]=1 deprecated_headers["errno.h"]=1 deprecated_headers["fenv.h"]=1 deprecated_headers["float.h"]=1 deprecated_headers["inttypes.h"]=1 deprecated_headers["iso646.h"]=1 deprecated_headers["limits.h"]=1 deprecated_headers["locale.h"]=1 deprecated_headers["math.h"]=1 deprecated_headers["setjmp.h"]=1 deprecated_headers["signal.h"]=1 deprecated_headers["stdalign.h"]=1 deprecated_headers["stdarg.h"]=1 deprecated_headers["stdbool.h"]=1 deprecated_headers["stddef.h"]=1 deprecated_headers["stdint.h"]=1 deprecated_headers["stdio.h"]=1 deprecated_headers["stdlib.h"]=1 deprecated_headers["string.h"]=1 deprecated_headers["tgmath.h"]=1 # list of allowed module types allowed_module_types["always"]=1 allowed_module_types["default-on"]=1 allowed_module_types["default-off"]=1 # list of "outer modules", that is those # that are not included in the kernel library outer_modules["wrapper"]=1 outer_modules["main"]=1 # list of "core modules", that is those for which # a specific module namespace is not required core_modules["core"]=1 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 close("mktemp -dt plumed.XXXXXX") # checking astyle presence astyle_available=!system(astyle " --version > /dev/null 2> /dev/null") if(!astyle_available) error("astyle_not_available","astyle not available on this system, skipping astyle checks") } # for each input file BEGINFILE{ # these variables are used to track if a file containes at least one item of a specific kind # as such they should be reset for every new input file found_namespace_plmd=0 found_namespace_module=0 found_guard_ifdef=0 found_non_preprocessor_lines=0 ## not used, see below # 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 gsub("/[.]/","/",filename); 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=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] "'" ); # DOC: :wrong_module_type: # DOC: Module type as indicated in the `module.type` file located in the module directory is not valid. # DOC: The admitted types are: `default-on`, which means the module is on by default, `default-off`, which means that # DOC: they should be explicitly enabled, and `always`, which means they are always required. if(!(module_type[module] in allowed_module_types)) error("wrong_module_type","module " module " has a wrong module type '" module_type[module] "'"); } close(path) } # 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 close(tempfile) 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) # DOC: :used_wrapper_module: # DOC: Wrapper module should not be used in other modules (via `USE=wrapper` in `Makefile`). # DOC: The reason is that this makes `libplumedKernel` dependent on the PLUMED wrappers. # DOC: An exception is the `main` module, which needs to use the `wrapper` module. # DOC: Notice that within the PLUMED library there is no need to use the external `cmd` interface # DOC: since one can directly declare a `PlumedMain` object. if(!(module in outer_modules) && isarray(used_modules[module]) && ("wrapper" in used_modules[module])) error("used_wrapper_module","wrapped module should not be used except in main") } } } file=ff[nf]; filebase=file; sub("\\..*","",filebase); # 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"); if(astyle_available && (filetype=="source" || filetype=="header")){ tempfile = tmpdir "/astyle" system(astyle " --options=" astyle_options " < " FILENAME " > " tempfile) s=system("diff -q " FILENAME " " tempfile ">/dev/null 2>/dev/null") # check if astyle has been applied correctly # DOC: :astyle: # DOC: In order to keep the code readable, we use the astyle program to format # DOC: all source files. This error indicates that the reported file has not been # DOC: formatted correctly. It is important that the version of astyle and the options # DOC: exactly match the ones we use for testing. To this aim, you should use # DOC: a command such as `make astyle` from the plumed root directory. if(s!=0) error("astyle","astyle not satisfied") system("rm " tempfile) } # check if configure.ac is consistent with configure if(filetype=="autoconf"){ tempfile = tmpdir "/configure" configurefile = FILENAME sub("\\.ac","",configurefile) system("autoconf " FILENAME " > " tempfile) s=system("diff -q " configurefile " " tempfile ">/dev/null 2>/dev/null") # check if autoconf has been applied correctly # DOC: :autoconf: # DOC: In our git repository we distribute both `./configure` and `./configure.ac` files. # DOC: When you modify the latter, the former should be regenerated using `autoconf` and # DOC: committed to git. This error indicates that the `./configure.ac` and `./configure` files # DOC: in the repository are not consistent. if(s!=0) error("autoconf","autoconf not satisfied") system("rm " tempfile) } } # line by line analysis { if(filetype=="source" || filetype=="header"){ # detect plumed manual if(match($0,"^//[+]PLUMEDOC ")) in_plumed_doc=$NF # detect doxygen manual - at least with /** and */ on separate lines if($1=="/**") in_doxygen=1 # 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 `../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. # 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 ## check if system headers are included after local headers. ## I am not sure it is a good idea, so I let it commented for now. # if(match($0,"^# *include +<")) found_include_system_header=1 ## it is a bad habit to include local headers after system headers ## notice that I explicitly check for the final "h" in the included file so as to exclude ## included files ending with tmp that are somewhere used in plumed to include automatically generated source code # if(match($0,"^# *include +\".*h\"")){ # if(found_include_system_header && !report_include_system_headers){ # style("include_system_headers","include system headers before local headers is discouraged"); # report_include_system_headers=1 # only report once per file # } # } # check #include <file> if(!in_doxygen && match($0,"^# *include +<[^>]+>.*$")){ h=$0 sub("^# *include +<","",h) sub(">.*$","",h) # check if deprecated headers are included # DOC: :deprecated_headers: # DOC: There is a list of headers that have been deprecated in C++ and should not be # DOC: included. These headers should be replaced with their equivalent from the stdlib. # DOC: E.g., use `#include <cstdlib>` instead of `#include <stdlib.h>`. Notice that # DOC: using the modern header all the functions are declared inside the `std` namespace. # DOC: Also notice that we have a special exception to allow them in Plumed.h. if(h in deprecated_headers && !(module in outer_modules)) error("deprecated_header","including deprecated header " h); # DOC: :unsupported_header_regex: # DOC: Header `<regex>`, although part of the C++11 standard, is not supported by gcc 4.8. # DOC: In order to allow compatibility with old compilers, this header should not be used. if(h == "regex") error("unsupported_header_regex","including unsupported header " h); # DOC: :external_header: (style) # DOC: Header files should possibly not include other header files from external libraries. # DOC: Indeed, once PLUMED is installed, if paths are not set correctly the include files of the other libraries # DOC: might not be reachable anymore. This is only a stylistic warning now. Notice that # DOC: with the current implementation of class Communicator it is necessary to include # DOC: `mpi.h` in a header file. if(filetype=="header" && h~"\\.h") style("external_header","including external header " h " in a header file"); } # check #include "file" if(!in_doxygen && match($0,"^# *include +\"[^\"]+\".*$")){ h=$0 sub("^# *include +\"","",h) sub("\".*$","",h) # check if a .inc file, which is temporary and not installed, is included in a header file. # this might create inconsistencies in the installed plumed header files. # DOC: :non_h_header: # DOC: Files with `.inc` extension are generated by PLUMED while it is compiled and are # DOC: not installed. They should not be directly included in header files, otherwise # DOC: those header files could be not usable once PLUMED is installed. if(filetype=="header" && h!~"\\.h") error("non_h_header","including non '.h' file " h " in a header file"); } # check if namespace PLMD is present if(match($0,"^ *namespace *PLMD")) found_namespace_plmd=1 # check if namespace for module is present if(match($0,"^ *namespace *" module)) found_namespace_module=1 # take note of registered actions if(match($0,"^ *PLUMED_REGISTER_ACTION")){ action=$0 sub("^ *PLUMED_REGISTER_ACTION\\(.*, *[\"]","",action); sub("[\"].*$","",action); information("registered_action","action " action); # DOC: :multi_registered_action: # DOC: Each action should be registered (with `PLUMED_REGISTER_ACTION`) once and only once. if(action in registered_actions) error("multi_registered_action","action " action " already registered at "registered_actions[action]); registered_actions[action]=FILENAME ":" FNR; } # take note of registered cltools if(match($0,"^ *PLUMED_REGISTER_CLTOOL")){ cltool=$0 sub("^ *PLUMED_REGISTER_CLTOOL\\(.*, *[\"]","",cltool); sub("[\"].*$","",cltool); information("registered_cltool","cltool " cltool); # DOC: :multi_registered_cltool: # DOC: Each cltool should be registered (with `PLUMED_REGISTER_CLTOOL`) once and only once. if(cltool in registered_cltools) error("multi_registered_cltool","cltool " cltool " already registered at "registered_cltools[cltool]); registered_cltools[cltool]=FILENAME ":" FNR; } # take note of registered vessels if(match($0,"^ *PLUMED_REGISTER_VESSEL")){ vessel=$0 sub("^ *PLUMED_REGISTER_VESSEL\\(.*, *[\"]","",vessel); sub("[\"].*$","",vessel); information("registered_vessel","vessel " vessel); # DOC: :multi_registered_vessel: # DOC: Each vessel should be registered (with `PLUMED_REGISTER_VESSEL`) once and only once. if(vessel in registered_vessels) error("multi_registered_vessel","vessel " vessel " already registered at "registered_vessels[vessel]); registered_vessels[vessel]=FILENAME ":" FNR; } # take note of registered metrics if(match($0,"^ *PLUMED_REGISTER_METRIC")){ metric=$0 sub("^ *PLUMED_REGISTER_METRIC\\(.*, *[\"]","",metric); sub("[\"].*$","",metric); information("registered_metric","metric " metric); # DOC: :multi_registered_metric: # DOC: Each metric should be registered (with `PLUMED_REGISTER_METRIC`) once and only once. if(metric in registered_metrics) error("multi_registered_metric","metric " metric " already registered at "registered_metrics[metric]); registered_metrics[metric]=FILENAME ":" FNR; } # take note of documented actions or cltools if(match($0,"^//[+]PLUMEDOC ")){ doc=$NF; # DOC: :multi_doc: # DOC: An action or cltool should be documented once and only once. Notice that because of the # DOC: way the manual is generated, a cltool cannot have the same name of an action. if(doc in plumed_doc) error("multi_doc","doc " doc " already at "plumed_doc[action]); plumed_doc[doc]=FILENAME ":" FNR; n=split($(NF-1),array,"_"); if(n==1) { switch($(NF-1)){ case "TOOLS": plumed_doc_cltools[doc]=FILENAME ":" FNR; information("documented_cltool","doc " doc); break; case "INTERNAL": plumed_doc_internal[doc]=FILENAME ":" FNR; information("documented_internal","doc " doc); break; default: plumed_doc_action[doc]=FILENAME ":" FNR; information("documented_action","doc " doc); } } else { switch(array[2]){ case "TOOLS": plumed_doc_cltools[doc]=FILENAME ":" FNR; information("documented_cltool","doc " doc); break; case "INTERNAL": plumed_doc_internal[doc]=FILENAME ":" FNR; information("documented_internal","doc " doc); break; default: plumed_doc_action[doc]=FILENAME ":" FNR; information("documented_action","doc " doc); } } } # check if, besides the \par Examples text, there is a verbatim command if(in_plumed_doc && (in_plumed_doc in provide_examples) && match($0,"^ *\\\\verbatim *$")){ provide_verbatim[in_plumed_doc]=1 } # check if, besides the \par Examples text, there is a plumedfile command # now it is considered equivalent to a verbatim # later on we might make plumedfile compulsory instead of verbatim if(in_plumed_doc && (in_plumed_doc in provide_examples) && match($0,"^ *\\\\plumedfile *$")){ provide_plumedfile[in_plumed_doc]=1 } #print match($0,"^ *\\\\verbatim *$"),"X" $0 "X" # take note of actions or cltools that provide examples if(match($0,"\\\\par Examples")){ provide_examples[in_plumed_doc]=FILENAME ":" FNR; } # check if guard is present guard="^#ifndef __PLUMED_" module "_" filebase "_h" if(match($0,guard)) found_guard_ifdef=1 # check that "using namespace" statements are not used in header files # DOC: :using_namespace_in_header: # DOC: A statement `using namespace` in header files is considered bad practice. Indeed, this would imply that # DOC: any other file including that header file would be using the same namespace. # DOC: This is true also for `std` namespace, and that's why you typically find full specifications # DOC: such as `std::string` in header files. In source files you are free to use `using namespace PLMD` and/or # DOC: `using namespace std` if you like, since these commands will only affect the source file where they are used. if(filetype=="header" && match($0,"using *namespace") && !in_plumed_doc && !in_doxygen) error("using_namespace_in_header","using namespace statement in header file") } # analyze HAS macros (bouth source and autoconf) if(match($0,"__PLUMED_HAS_[A-Z_]*")){ string=substr( $0, RSTART, RLENGTH ) sub("__PLUMED_HAS_","",string); if(filetype=="autoconf"){ # take note of defined HAS macros if(plumed_definehas_file[string,FILENAME]!="used") information("defined_has",string); plumed_definehas_file[string,FILENAME]="used"; plumed_definehas[string]=FILENAME ":" FNR; } else { # take note of used HAS macros if(plumed_usehas_file[string,FILENAME]!="used") information("used_has",string); # DOC: :used_has_in_header: (style) # DOC: Using `__PLUMED_HAS_SOMETHING` macros in headers should be avoided since # DOC: it could make it difficult to make sure the same macros are correctly defined # DOC: when using PLUMED as a library. This is only a stylistic warning now. Notice that # DOC: with the current implementation of class Communicator it is necessary to include # DOC: `mpi.h` in a header file and thus to use `__PLUMED_HAS_MPI`. if(plumed_usehas_file[string,FILENAME]!="used" && filetype=="header") style("used_has_in_header",string); plumed_usehas_file[string,FILENAME]="used"; plumed_usehas[string]=FILENAME ":" FNR; } } if(filetype=="source" || filetype=="header"){ # detect plumed manual if(match($0,"^//[+]ENDPLUMEDOC")) in_plumed_doc=0 # detect doxygen manual - at least with /** and */ on separate lines if($1=="*/") in_doxygen=0 # detect copyright lines if($0=="+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */") in_copyright=0; } } # at the end of each file: ENDFILE{ # check for namespaces if(found_non_preprocessor_lines && (filetype=="source" || filetype=="header") && !(module in outer_modules) ){ # DOC: :missing_namespace_plmd: # DOC: Every source file should contain a `PLMD` namespace. # DOC: Notice that files in the "outer modules" `wrapper` and `main`, that is those that # DOC: are not included in the kernel library, are exempted from this rule. if(!found_namespace_plmd) error("missing_namespace_plmd","missing PLMD namespace"); # DOC: :missing_namespace_module: # DOC: Every source file should contain a sub namespace with the same name of the module itself. # DOC: Notice that there are important exceptions to this rule: # DOC: - "outer modules" `wrapper` and `main`, that are not included in the kernel library. # DOC: - "core modules" that are part of the core of PLUMED. Since the classes implemented here are # DOC: widely used within PLUMED, they are directly defined in the PLMD namespace. if(module && !(module in core_modules) && !found_namespace_module) error("missing_namespace_module","missing " module " namespace") } if(filetype=="header"){ # this can be done only if module name is known: # DOC: :missing_guard: # DOC: Every header file should contain a proper guard to avoid double inclusion. # DOC: The format of the guard is fixed and should be `__PLUMED_modulename_filename_h`, where # DOC: `modulename` is the name of the module and `filename` is the name of the file, without suffix. # DOC: This choice makes guards unique within the whole codebase. if(module && !found_guard_ifdef) error("missing_guard","missing ifndef guard"); } # check if every header has a corresponding cpp # this is done only for non-trivial headers (i.e. we skip headers that just include another header) if(filetype=="header" && found_non_preprocessor_lines){ notfound=0 cppfile=FILENAME sub("\\.h$",".cpp",cppfile); if((getline < cppfile < 0)){ cppfile=FILENAME sub("\\.h$",".c",cppfile); if((getline < cppfile < 0)) notfound=1 } if(notfound){ # DOC: :non_existing_cpp: # DOC: For every header file there should exist a corresponding source file with the same name. # DOC: Notice that dummy headers that only include another header or which only define preprocessor # DOC: macros are exempted from this rule. error("non_existing_cpp","file " file " is a header but there is no corresponding source"); } while((getline < cppfile)>=0){ if(match($0,"^ *#include")){ sub("^ *#include",""); # DOC: :non_included_h: # DOC: The source file corresponding to a header file (that is: with the same name) should include it as the first included file. # DOC: This is to make sure that all the header files that we install can be individually included and compiled. if($1!="\"" file "\"") error("non_included_h","file " file " is a header but " cppfile " does not include it as first include"); break; } } close(cppfile) } } # after processing all files, perform some global check END{ # checks are only done if enabled if(!opt_global_check) exit(0) # this is required to properly report messages global_check=1 for(action in registered_actions){ if(!(action in plumed_doc_action)){ # DOC: :undocumented_action: # DOC: Every action that is registered with `PLUMED_REGISTER_ACTION` should also be documented # DOC: in a PLUMEDOC page. error("undocumented_action","action " action " at " registered_actions[action] " is not documented") } else if(!(action in provide_examples)){ # DOC: :action_without_examples: # DOC: Every action that is registered should contain an `Example` section in its documentation. # DOC: Notice that if the `Example` section is not added the manual will not show the registered keywords. error("action_without_examples","action " action " at " plumed_doc_action[action] " has no example") } else if(!(action in provide_plumedfile)){ # DOC: :action_without_verbatim: # DOC: Every action that is registered should contain an example in the form of a `plumedfile` section. # DOC: This is currently a stylistic warning since there are many actions not satisfying this requirement. style("action_without_plumedfile","action " action " at " provide_examples[action] " has no plumedfile") } } for(action in plumed_doc_action){ if(!(action in registered_actions)) # DOC: :unregistered_action: # DOC: Every action that is documented should be also registered using `PLUMED_REGISTER_ACTION`. error("unregistered_action"," action " action " at " plumed_doc_action[action] " is not registered"); } for(cltool in registered_cltools){ if(!(cltool in plumed_doc_cltools)){ # DOC: :undocumented_cltool: # DOC: Every cltool that is registered with `PLUMED_REGISTER_CLTOOL` should also be documented # DOC: in a PLUMEDOC page. error("undocumented_cltool","cltool " cltool " at " registered_cltools[cltool] " is not documented") } else if(!(cltool in provide_examples)){ # DOC: :cltool_without_examples: # DOC: Every cltool that is registered should contain an `Example` section in its documentation. # DOC: Notice that if the `Example` section is not added the manual will not show the registered options. error("cltool_without_examples","cltool " cltool " at " plumed_doc_cltools[cltool] " has no example") } else if(!(cltool in provide_verbatim)){ # DOC: :cltool_without_verbatim: # DOC: Every cltool that is registered should contain an example in the form of a `verbatim` section. error("cltool_without_verbatim","cltool " cltool " at " provide_examples[cltool] " has no verbatim") } } for(cltool in plumed_doc_cltools){ if(!(cltool in registered_cltools)) # DOC: :unregistered_cltool: # DOC: Every cltool that is documented should be also registered using `PLUMED_REGISTER_CLTOOL`. error("unregistered_cltool"," cltool " cltool " at " plumed_doc_cltools[cltool] " is not registered"); } for(has in plumed_usehas){ if(!(has in plumed_definehas)){ # DOC: :undefined_has: # DOC: Every macro in the form `__PLUMED_HAS_SOMETHING` that is used in the code # DOC: should be defined in configure.ac. This check is made to avoid errors with mis-typed macros. error("undefined_has","has " has " at " plumed_usehas[has] " is not defined") } } for(has in plumed_definehas){ if(!(has in plumed_usehas)){ # DOC: :unused_has: # DOC: Every macro in the form `__PLUMED_HAS_SOMETHING` that is defined in configure.ac # DOC: should be used at least once in the code. This check is made to avoid errors with mis-typed macros. error("unused_has","has " has " at " plumed_definehas[has] " is not used") } } # remove temporary directory system("rmdir " tmpdir); } EOF gawk -f $TEMP $@