Line data Source code
1 : /* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2 : Copyright (c) 2012-2019 The plumed team
3 : (see the PEOPLE file at the root of the distribution for a list of names)
4 :
5 : See http://www.plumed.org for more information.
6 :
7 : This file is part of plumed, version 2.
8 :
9 : plumed is free software: you can redistribute it and/or modify
10 : it under the terms of the GNU Lesser General Public License as published by
11 : the Free Software Foundation, either version 3 of the License, or
12 : (at your option) any later version.
13 :
14 : plumed is distributed in the hope that it will be useful,
15 : but WITHOUT ANY WARRANTY; without even the implied warranty of
16 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 : GNU Lesser General Public License for more details.
18 :
19 : You should have received a copy of the GNU Lesser General Public License
20 : along with plumed. If not, see <http://www.gnu.org/licenses/>.
21 : +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
22 : #include "CLToolMain.h"
23 : #include "config/Config.h"
24 : #include "tools/Exception.h"
25 : #include "tools/Communicator.h"
26 : #include "CLTool.h"
27 : #include "CLToolRegister.h"
28 : #include "tools/Tools.h"
29 : #include "tools/DLLoader.h"
30 : #include <string>
31 : #include <cstdlib>
32 : #include <cstdio>
33 : #include <iostream>
34 : #include <algorithm>
35 : #include <unordered_map>
36 :
37 : using namespace std;
38 :
39 : #include "CLToolMainEnum.inc"
40 :
41 : namespace PLMD {
42 :
43 10292 : const std::unordered_map<std::string, int> & clToolMainWordMap() {
44 11889 : static std::unordered_map<std::string, int> word_map;
45 : static bool init=false;
46 10292 : if(!init) {
47 : #include "CLToolMainMap.inc"
48 : }
49 10292 : init=true;
50 10292 : return word_map;
51 : }
52 :
53 1597 : CLToolMain::CLToolMain():
54 : argc(0),
55 : in(stdin),
56 : out(stdout),
57 3194 : comm(*new Communicator)
58 : {
59 1597 : }
60 :
61 4791 : CLToolMain::~CLToolMain() {
62 1597 : delete &comm;
63 3194 : }
64 :
65 : #define CHECK_NULL(val,word) plumed_massert(val,"NULL pointer received in cmd(\"CLTool " + word + "\")");
66 :
67 5146 : void CLToolMain::cmd(const std::string& word,void*val) {
68 :
69 10292 : std::vector<std::string> words=Tools::getWords(word);
70 5146 : unsigned nw=words.size();
71 5146 : if(nw==0) {
72 : // do nothing
73 : } else {
74 : int iword=-1;
75 : char**v;
76 : char*vv;
77 5146 : const auto it=clToolMainWordMap().find(words[0]);
78 5146 : if(it!=clToolMainWordMap().end()) iword=it->second;
79 5146 : switch(iword) {
80 1597 : case cmd_setArgc:
81 1597 : CHECK_NULL(val,word);
82 1597 : argc=*static_cast<int*>(val);
83 1597 : break;
84 1597 : case cmd_setArgv:
85 1597 : CHECK_NULL(val,word);
86 : v=static_cast<char**>(val);
87 24159 : for(int i=0; i<argc; ++i) argv.push_back(string(v[i]));
88 : break;
89 0 : case cmd_setArgvLine:
90 0 : CHECK_NULL(val,word);
91 : vv=static_cast<char*>(val);
92 0 : argv=Tools::getWords(vv);
93 0 : break;
94 0 : case cmd_setIn:
95 0 : CHECK_NULL(val,word);
96 0 : in=static_cast<FILE*>(val);
97 0 : break;
98 0 : case cmd_setOut:
99 0 : CHECK_NULL(val,word);
100 0 : out=static_cast<FILE*>(val);
101 0 : break;
102 355 : case cmd_setMPIComm:
103 355 : comm.Set_comm(val);
104 : break;
105 0 : case cmd_setMPIFComm:
106 0 : comm.Set_fcomm(val);
107 : break;
108 1597 : case cmd_run:
109 1597 : CHECK_NULL(val,word);
110 1597 : argc=argv.size();
111 : {
112 24159 : int n=0; for(int i=0; i<argc; ++i) n+=argv[i].length()+1;
113 1597 : std::vector<char> args(n);
114 1597 : std::vector<char*> vvv(argc);
115 : char* ptr=&args[0];
116 24159 : for(int i=0; i<argc; ++i) {
117 22562 : vvv[i]=ptr;
118 290798 : for(unsigned c=0; c<argv[i].length(); ++c) {
119 89412 : *ptr=argv[i][c]; ptr++;
120 : }
121 11281 : *ptr=0; ptr++;
122 : }
123 3194 : int ret=run(argc,&vvv[0],in,out,comm);
124 1597 : *static_cast<int*>(val)=ret;
125 : }
126 1597 : break;
127 0 : default:
128 0 : plumed_merror("cannot interpret cmd(\"CLTool " + word + "\"). check plumed developers manual to see the available commands.");
129 : break;
130 : }
131 : }
132 5146 : }
133 :
134 : /**
135 : This is the entry point to the command line tools
136 : included in the plumed library.
137 : */
138 :
139 1597 : int CLToolMain::run(int argc, char **argv,FILE*in,FILE*out,Communicator& pc) {
140 : int i;
141 : bool printhelp=false;
142 :
143 3194 : DLLoader dlloader;
144 :
145 1597 : string root=config::getPlumedRoot();
146 :
147 : bool standalone_executable=false;
148 :
149 : // Start parsing options
150 1597 : string prefix("");
151 1597 : string a("");
152 4081 : for(i=1; i<argc; i++) {
153 5678 : a=prefix+argv[i];
154 2839 : if(a.length()==0) continue;
155 8517 : if(a=="help" || a=="-h" || a=="--help") {
156 : printhelp=true;
157 : break;
158 2839 : } else if(a=="--has-mpi") {
159 0 : if(Communicator::initialized()) return 0;
160 0 : else return 1;
161 2839 : } else if(a=="--has-matheval") {
162 0 : return (config::hasMatheval()?0:1);
163 2839 : } else if(a=="--has-cregex") {
164 0 : return (config::hasCregex()?0:1);
165 2839 : } else if(a=="--has-dlopen") {
166 0 : return (config::hasDlopen()?0:1);
167 2839 : } else if(a=="--has-molfile") {
168 0 : return (config::hasMolfile()?0:1);
169 2839 : } else if(a=="--has-external-molfile") {
170 0 : return (config::hasExternalMolfile()?0:1);
171 2839 : } else if(a=="--has-zlib") {
172 0 : return (config::hasZlib()?0:1);
173 2839 : } else if(a=="--has-xdrfile") {
174 0 : return (config::hasXdrfile()?0:1);
175 2839 : } else if(a=="--is-installed") {
176 13 : return (config::isInstalled()?0:1);
177 2826 : } else if(a=="--no-mpi") {
178 : // this is ignored, as it is parsed in main
179 1242 : if(i>1) {
180 0 : fprintf(stderr,"--no-mpi option can only be used as the first option");
181 : return 1;
182 : }
183 1584 : } else if(a=="--mpi") {
184 : // this is ignored, as it is parsed in main
185 0 : if(i>1) {
186 0 : fprintf(stderr,"--mpi option can only be used as the first option");
187 : return 1;
188 : }
189 1584 : } else if(a=="--standalone-executable") {
190 : standalone_executable=true;
191 3168 : } else if(Tools::startWith(a,"--load=")) {
192 0 : a.erase(0,a.find("=")+1);
193 : prefix="";
194 0 : void *p=dlloader.load(a);
195 0 : if(!p) {
196 0 : fprintf(stderr,"ERROR: cannot load library %s\n",a.c_str());
197 0 : fprintf(stderr,"ERROR: %s\n",dlloader.error().c_str());
198 : return 1;
199 : }
200 1584 : } else if(a=="--load") {
201 : prefix="--load=";
202 1584 : } else if(a[0]=='-') {
203 0 : string msg="ERROR: Unknown option " +a;
204 0 : fprintf(stderr,"%s\n",msg.c_str());
205 : return 1;
206 : } else break;
207 : }
208 :
209 : // Check if plumedRoot/patches/ directory exists (as a further check)
210 1584 : if(!standalone_executable) {
211 3168 : vector<string> files=Tools::ls(root);
212 3168 : if(find(files.begin(),files.end(),"patches")==files.end()) {
213 : string msg=
214 0 : "WARNING: I cannot find "+root+"/patches/ directory. Set PLUMED_ROOT or reinstall PLUMED\n\n";
215 0 : fprintf(stderr,"%s",msg.c_str());
216 : }
217 : }
218 :
219 : // Build list of available C++ tools:
220 3168 : vector<string> availableCxx=cltoolRegister().list();
221 : // Build list of available shell tools:
222 1584 : vector<string> availableShell;
223 1584 : if(!standalone_executable) {
224 1584 : vector<string> tmp;
225 4752 : tmp=Tools::ls(string(root+"/scripts"));
226 36432 : for(unsigned j=0; j<tmp.size(); ++j) {
227 : size_t ff=tmp[j].find(".sh");
228 12672 : if(ff==string::npos) tmp[j].erase();
229 9504 : else tmp[j].erase(ff);
230 : }
231 36432 : for(unsigned j=0; j<tmp.size(); ++j) if(tmp[j].length()>0) availableShell.push_back(tmp[j]);
232 : }
233 :
234 1584 : if(printhelp) {
235 : string msg=
236 : "Usage: plumed [options] [command] [command options]\n"
237 : " plumed [command] -h|--help: to print help for a specific command\n"
238 : "Options:\n"
239 : " [help|-h|--help] : to print this help\n"
240 : " [--is-installed] : fails if plumed is not installed\n"
241 : " [--has-mpi] : fails if plumed is running without MPI\n"
242 : " [--has-matheval] : fails if plumed is compiled without matheval\n"
243 : " [--has-dlopen] : fails if plumed is compiled without dlopen\n"
244 : " [--load LIB] : loads a shared object (typically a plugin library)\n"
245 : " [--standalone-executable] : tells plumed not to look for commands implemented as scripts\n"
246 0 : "Commands:\n";
247 : fprintf(out,"%s",msg.c_str());
248 0 : for(unsigned j=0; j<availableCxx.size(); ++j) {
249 0 : CLTool *cl=cltoolRegister().create(CLToolOptions(availableCxx[j]));
250 0 : plumed_assert(cl);
251 0 : string manual=availableCxx[j]+" : "+cl->description();
252 0 : delete cl;
253 : fprintf(out," plumed %s\n", manual.c_str());
254 : }
255 0 : for(unsigned j=0; j<availableShell.size(); ++j) {
256 0 : string cmd=config::getEnvCommand()+" \""+root+"/scripts/"+availableShell[j]+".sh\" --description";
257 0 : FILE *fp=popen(cmd.c_str(),"r");
258 : string line,manual;
259 0 : while(Tools::getline(fp,line))manual+=line;
260 0 : pclose(fp);
261 0 : manual= availableShell[j]+" : "+manual;
262 : fprintf(out," plumed %s\n", manual.c_str());
263 : }
264 : return 0;
265 : }
266 1584 : if(i==argc) {
267 : fprintf(out,"%s","Nothing to do. Use 'plumed help' for help\n");
268 : return 0;
269 : }
270 :
271 : // this is the command to be executed:
272 1584 : string command(argv[i]);
273 :
274 3168 : if(find(availableCxx.begin(),availableCxx.end(),command)!=availableCxx.end()) {
275 2490 : CLTool *cl=cltoolRegister().create(CLToolOptions(command));
276 1245 : plumed_assert(cl);
277 : // Read the command line options (returns false if we are just printing help)
278 1245 : if( !cl->readInput( argc-i,&argv[i],in,out ) ) { delete cl; return 0; }
279 1245 : int ret=cl->main(in,out,pc);
280 1245 : delete cl;
281 1245 : return ret;
282 : }
283 :
284 678 : if(find(availableShell.begin(),availableShell.end(),command)!=availableShell.end()) {
285 339 : plumed_massert(in==stdin,"shell tools can only work on stdin");
286 339 : plumed_massert(out==stdout,"shell tools can only work on stdin");
287 2034 : string cmd=config::getEnvCommand()+" \""+root+"/scripts/"+command+".sh\"";
288 2229 : for(int j=i+1; j<argc; j++) cmd+=string(" ")+argv[j];
289 339 : int r=system(cmd.c_str());
290 : // this is necessary since system seems to return numbers which are multiple
291 : // of 256. this would make the interpretation by the shell wrong
292 : // I just return 1 in case of failure and 0 in case of success
293 339 : if(r!=0) return 1;
294 267 : else return 0;
295 : }
296 :
297 0 : string msg="ERROR: unknown command " + command + ". Use 'plumed help' for help";
298 0 : fprintf(stderr,"%s\n",msg.c_str());
299 : return 1;
300 :
301 : }
302 4839 : }
|