Newer
Older
# 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)
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
# 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
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;
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
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
"mktemp -dt plumed.XXXXXX" | getline tmpdir
# 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
module=""
nf=split(filename,ff,"/");
if(nf>1){
module=ff[nf-1];
if(!(module in 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] "'");
# 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)
# 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
sub("^# *include +\"\\.\\./","",included_module_name);
sub("/.*$","",included_module_name);
# 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(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;
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
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
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")
# 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;
}
}
}
}
# 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");
}
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")
# 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);