Line data Source code
1 : /* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2 : Copyright (c) 2012-2023 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 <memory>
36 : #include <unordered_map>
37 :
38 : namespace PLMD {
39 :
40 3445 : CLToolMain::CLToolMain():
41 3445 : argc(0),
42 3445 : in(stdin),
43 3445 : out(stdout)
44 : {
45 3445 : }
46 :
47 6890 : CLToolMain::~CLToolMain() {
48 : // empty destructor to delete unique_ptr
49 6890 : }
50 :
51 : #define CHECK_NULL(val,word) plumed_massert(val,"NULL pointer received in cmd(\"CLTool " + word + "\")");
52 :
53 10668 : void CLToolMain::cmd(const std::string& word,const TypesafePtr & val) {
54 :
55 : // Enumerate all possible commands:
56 : enum {
57 : #include "CLToolMainEnum.inc"
58 : };
59 :
60 : // Static object (initialized once) containing the map of commands:
61 : const static std::unordered_map<std::string, int> word_map = {
62 : #include "CLToolMainMap.inc"
63 41655 : };
64 :
65 10668 : std::vector<std::string> words=Tools::getWords(word);
66 10668 : unsigned nw=words.size();
67 10668 : if(nw==0) {
68 : // do nothing
69 : } else {
70 : int iword=-1;
71 : const char*const*v;
72 : const char*vv;
73 : const auto it=word_map.find(words[0]);
74 10668 : if(it!=word_map.end()) iword=it->second;
75 10668 : switch(iword) {
76 : case cmd_setArgc:
77 3441 : CHECK_NULL(val,word);
78 3441 : argc=val.get<int>();
79 3441 : break;
80 : case cmd_setArgv:
81 3441 : CHECK_NULL(val,word);
82 3441 : v=val.get<const char*const*>(argc);
83 23258 : for(int i=0; i<argc; ++i) argv.push_back(std::string(v[i]));
84 : break;
85 : case cmd_setArgvLine:
86 4 : CHECK_NULL(val,word);
87 : vv=val.get<const char*>();
88 4 : argv=Tools::getWords(vv);
89 4 : break;
90 : case cmd_setIn:
91 0 : CHECK_NULL(val,word);
92 0 : in=val.get<FILE*>();
93 0 : break;
94 : case cmd_setOut:
95 1 : CHECK_NULL(val,word);
96 1 : out=val.get<FILE*>();
97 1 : break;
98 336 : case cmd_setMPIComm:
99 336 : comm.Set_comm(val);
100 : break;
101 0 : case cmd_setMPIFComm:
102 0 : comm.Set_fcomm(val);
103 : break;
104 : case cmd_run:
105 3445 : CHECK_NULL(val,word);
106 3445 : argc=argv.size();
107 : {
108 23270 : int n=0; for(int i=0; i<argc; ++i) n+=argv[i].length()+1;
109 3445 : std::vector<char> args(n);
110 3445 : std::vector<char*> vvv(argc);
111 : char* ptr=&args[0];
112 23270 : for(int i=0; i<argc; ++i) {
113 19825 : vvv[i]=ptr;
114 174134 : for(unsigned c=0; c<argv[i].length(); ++c) {
115 154309 : *ptr=argv[i][c]; ptr++;
116 : }
117 19825 : *ptr=0; ptr++;
118 : }
119 3445 : val.set(int(run(argc,&vvv[0],in,out,comm)));
120 : }
121 3445 : break;
122 0 : default:
123 0 : plumed_merror("cannot interpret cmd(\"CLTool " + word + "\"). check plumed developers manual to see the available commands.");
124 : break;
125 : }
126 : }
127 10668 : }
128 :
129 : /**
130 : This is the entry point to the command line tools
131 : included in the plumed library.
132 : */
133 :
134 3445 : int CLToolMain::run(int argc, char **argv,FILE*in,FILE*out,Communicator& pc) {
135 : int i;
136 : bool printhelp=false;
137 :
138 3445 : DLLoader dlloader;
139 :
140 3445 : std::string root=config::getPlumedRoot();
141 :
142 : bool standalone_executable=false;
143 :
144 : // Start parsing options
145 3445 : std::string prefix("");
146 3445 : std::string a("");
147 6550 : for(i=1; i<argc; i++) {
148 13100 : a=prefix+argv[i];
149 6550 : if(a.length()==0) continue;
150 19650 : if(a=="help" || a=="-h" || a=="--help") {
151 : printhelp=true;
152 : break;
153 6546 : } else if(a=="--has-mpi") {
154 0 : if(Communicator::initialized()) return 0;
155 0 : else return 1;
156 6546 : } else if(a=="--has-cregex") {
157 0 : return (config::hasCregex()?0:1);
158 6546 : } else if(a=="--has-dlopen") {
159 0 : return (config::hasDlopen()?0:1);
160 6546 : } else if(a=="--has-molfile") {
161 0 : return (config::hasMolfile()?0:1);
162 6546 : } else if(a=="--has-external-molfile") {
163 0 : return (config::hasExternalMolfile()?0:1);
164 6546 : } else if(a=="--has-zlib") {
165 0 : return (config::hasZlib()?0:1);
166 6546 : } else if(a=="--has-xdrfile") {
167 : return 0; // always ok
168 6546 : } else if(a=="--is-installed") {
169 619 : return (config::isInstalled()?0:1);
170 5927 : } else if(a=="--no-mpi") {
171 : // this is ignored, as it is parsed in main
172 3105 : continue;
173 2822 : } else if(a=="--mpi") {
174 : // this is ignored, as it is parsed in main
175 0 : continue;
176 2822 : } else if(a=="--standalone-executable") {
177 : standalone_executable=true;
178 5644 : } else if(Tools::startWith(a,"--load=")) {
179 0 : a.erase(0,a.find("=")+1);
180 : prefix="";
181 0 : void *p=dlloader.load(a);
182 0 : if(!p) {
183 0 : std::fprintf(stderr,"ERROR: cannot load library %s\n",a.c_str());
184 0 : std::fprintf(stderr,"ERROR: %s\n",dlloader.error().c_str());
185 : return 1;
186 : }
187 2822 : } else if(a=="--load") {
188 : prefix="--load=";
189 2822 : } else if(a[0]=='-') {
190 0 : std::string msg="ERROR: Unknown option " +a;
191 0 : std::fprintf(stderr,"%s\n",msg.c_str());
192 : return 1;
193 : } else break;
194 : }
195 :
196 : // Check if plumedRoot/patches/ directory exists (as a further check)
197 2826 : if(!standalone_executable) {
198 2826 : std::vector<std::string> files=Tools::ls(root);
199 2826 : if(find(files.begin(),files.end(),"patches")==files.end()) {
200 : std::string msg=
201 0 : "WARNING: I cannot find "+root+"/patches/ directory. Set PLUMED_ROOT or reinstall PLUMED\n\n";
202 0 : std::fprintf(stderr,"%s",msg.c_str());
203 : }
204 2826 : }
205 :
206 : // Build list of available C++ tools:
207 2826 : std::vector<std::string> availableCxx=cltoolRegister().list();
208 : // Build list of available shell tools:
209 : std::vector<std::string> availableShell;
210 2826 : if(!standalone_executable) {
211 : std::vector<std::string> tmp;
212 5652 : tmp=Tools::ls(std::string(root+"/scripts"));
213 22608 : for(unsigned j=0; j<tmp.size(); ++j) {
214 19782 : size_t ff=tmp[j].find(".sh");
215 19782 : if(ff==std::string::npos) tmp[j].erase();
216 19782 : else tmp[j].erase(ff);
217 : }
218 22608 : for(unsigned j=0; j<tmp.size(); ++j) if(tmp[j].length()>0) availableShell.push_back(tmp[j]);
219 2826 : }
220 :
221 2826 : if(printhelp) {
222 : std::string msg=
223 : "Usage: plumed [options] [command] [command options]\n"
224 : " plumed [command] -h|--help: to print help for a specific command\n"
225 : "Options:\n"
226 : " [help|-h|--help] : to print this help\n"
227 : " [--is-installed] : fails if plumed is not installed\n"
228 : " [--has-mpi] : fails if plumed is running without MPI\n"
229 : " [--has-dlopen] : fails if plumed is compiled without dlopen\n"
230 : " [--load LIB] : loads a shared object (typically a plugin library)\n"
231 : " [--standalone-executable] : tells plumed not to look for commands implemented as scripts\n"
232 4 : "Commands:\n";
233 : std::fprintf(out,"%s",msg.c_str());
234 64 : for(unsigned j=0; j<availableCxx.size(); ++j) {
235 120 : auto cl=cltoolRegister().create(CLToolOptions(availableCxx[j]));
236 60 : plumed_assert(cl);
237 176 : std::string manual=availableCxx[j]+" : "+cl->description();
238 : std::fprintf(out," plumed %s\n", manual.c_str());
239 60 : }
240 32 : for(unsigned j=0; j<availableShell.size(); ++j) {
241 : std::string manual;
242 : #ifdef __PLUMED_HAS_POPEN
243 56 : std::string cmd=config::getEnvCommand()+" \""+root+"/scripts/"+availableShell[j]+".sh\" --description";
244 28 : FILE *fp=popen(cmd.c_str(),"r");
245 : std::string line;
246 56 : while(Tools::getline(fp,line))manual+=line;
247 28 : pclose(fp);
248 : #else
249 : manual="(doc not avail)";
250 : #endif
251 56 : manual= availableShell[j]+" : "+manual;
252 : std::fprintf(out," plumed %s\n", manual.c_str());
253 : }
254 : return 0;
255 : }
256 2822 : if(i==argc) {
257 : std::fprintf(out,"%s","Nothing to do. Use 'plumed help' for help\n");
258 : return 0;
259 : }
260 :
261 : // this is the command to be executed:
262 2822 : std::string command(argv[i]);
263 :
264 2822 : if(find(availableCxx.begin(),availableCxx.end(),command)!=availableCxx.end()) {
265 4570 : auto cl=cltoolRegister().create(CLToolOptions(command));
266 2285 : plumed_assert(cl);
267 : // Read the command line options (returns false if we are just printing help)
268 2285 : if( !cl->readInput( argc-i,&argv[i],in,out ) ) { return 0; }
269 2285 : int ret=cl->main(in,out,pc);
270 : return ret;
271 2285 : }
272 :
273 537 : if(find(availableShell.begin(),availableShell.end(),command)!=availableShell.end()) {
274 537 : plumed_massert(in==stdin,"shell tools can only work on stdin");
275 537 : plumed_massert(out==stdout,"shell tools can only work on stdin");
276 1074 : std::string cmd=config::getEnvCommand()+" \""+root+"/scripts/"+command+".sh\"";
277 2034 : for(int j=i+1; j<argc; j++) cmd+=std::string(" ")+argv[j];
278 537 : int r=std::system(cmd.c_str());
279 : // this is necessary since system seems to return numbers which are multiple
280 : // of 256. this would make the interpretation by the shell wrong
281 : // I just return 1 in case of failure and 0 in case of success
282 537 : if(r!=0) return 1;
283 426 : else return 0;
284 : }
285 :
286 0 : std::string msg="ERROR: unknown command " + command + ". Use 'plumed help' for help";
287 0 : std::fprintf(stderr,"%s\n",msg.c_str());
288 : return 1;
289 :
290 6271 : }
291 : }
|