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 "tools/Subprocess.h"
31 : #include <string>
32 : #include <cstdlib>
33 : #include <cstdio>
34 : #include <iostream>
35 : #include <algorithm>
36 : #include <memory>
37 :
38 : namespace PLMD {
39 :
40 5464 : CLToolMain::CLToolMain():
41 5464 : argc(0),
42 5464 : in(stdin),
43 5464 : out(stdout) {
44 5464 : }
45 :
46 10928 : CLToolMain::~CLToolMain() {
47 : // empty destructor to delete unique_ptr
48 10928 : }
49 :
50 : #define CHECK_NULL(val,word) plumed_assert(val) << "NULL pointer received in cmd(\"CLTool " << word << "\"";
51 :
52 16686 : void CLToolMain::cmd(std::string_view word,const TypesafePtr & val) {
53 :
54 : // Enumerate all possible commands:
55 : enum {
56 : #include "CLToolMainEnum.inc"
57 : };
58 :
59 : // Static object (initialized once) containing the map of commands:
60 : const static Tools::FastStringUnorderedMap<int> word_map = {
61 : #include "CLToolMainMap.inc"
62 16686 : };
63 :
64 : gch::small_vector<std::string_view> words;
65 16686 : Tools::getWordsSimple(words,word);
66 16686 : unsigned nw=words.size();
67 16686 : 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 16686 : if(it!=word_map.end()) {
75 16686 : iword=it->second;
76 : }
77 16686 : switch(iword) {
78 : case cmd_setArgc:
79 5380 : CHECK_NULL(val,word);
80 5380 : argc=val.get<int>();
81 5380 : break;
82 : case cmd_setArgv:
83 5380 : CHECK_NULL(val,word);
84 5380 : v=val.get<const char*const*>({argc});
85 34814 : for(int i=0; i<argc; ++i) {
86 58868 : argv.push_back(std::string(v[i]));
87 : }
88 : break;
89 : case cmd_setArgvLine:
90 84 : CHECK_NULL(val,word);
91 84 : vv=val.getCString();
92 84 : argv=Tools::getWords(vv);
93 84 : break;
94 : case cmd_setIn:
95 0 : CHECK_NULL(val,word);
96 0 : in=val.get<FILE*>();
97 0 : break;
98 : case cmd_setOut:
99 17 : CHECK_NULL(val,word);
100 17 : out=val.get<FILE*>();
101 17 : break;
102 361 : case cmd_setMPIComm:
103 361 : comm.Set_comm(val);
104 : break;
105 0 : case cmd_setMPIFComm:
106 0 : comm.Set_fcomm(val);
107 : break;
108 : case cmd_run:
109 5464 : CHECK_NULL(val,word);
110 5464 : argc=argv.size();
111 : {
112 : int n=0;
113 35386 : for(int i=0; i<argc; ++i) {
114 29922 : n+=argv[i].length()+1;
115 : }
116 5464 : std::vector<char> args(n);
117 5464 : std::vector<char*> vvv(argc);
118 : char* ptr=&args[0];
119 35386 : for(int i=0; i<argc; ++i) {
120 29922 : vvv[i]=ptr;
121 258782 : for(unsigned c=0; c<argv[i].length(); ++c) {
122 228860 : *ptr=argv[i][c];
123 228860 : ptr++;
124 : }
125 29922 : *ptr=0;
126 29922 : ptr++;
127 : }
128 5464 : val.set(int(run(argc,&vvv[0],in,out,comm)));
129 : }
130 5464 : break;
131 0 : default:
132 0 : plumed_error() << "cannot interpret cmd(\"CLTool " << word << "\"). check plumed developers manual to see the available commands.";
133 : break;
134 : }
135 : }
136 16686 : }
137 :
138 : /**
139 : This is the entry point to the command line tools
140 : included in the plumed library.
141 : */
142 :
143 5464 : int CLToolMain::run(int argc, char **argv,FILE*in,FILE*out,Communicator& pc) {
144 : int i;
145 : bool printhelp=false;
146 :
147 5464 : DLLoader dlloader;
148 :
149 5464 : std::string root=config::getPlumedRoot();
150 :
151 : bool standalone_executable=false;
152 :
153 : // Start parsing options
154 5464 : std::string prefix("");
155 5464 : std::string a("");
156 10487 : for(i=1; i<argc; i++) {
157 20974 : a=prefix+argv[i];
158 10487 : if(a.length()==0) {
159 0 : continue;
160 : }
161 31461 : if(a=="help" || a=="-h" || a=="--help") {
162 : printhelp=true;
163 : break;
164 10483 : } else if(a=="--has-mpi") {
165 0 : if(Communicator::initialized()) {
166 : return 0;
167 : } else {
168 0 : return 1;
169 : }
170 10483 : } else if(a=="--has-cregex") {
171 : return (config::hasCregex()?0:1);
172 10483 : } else if(a=="--has-dlopen") {
173 0 : return (config::hasDlopen()?0:1);
174 10483 : } else if(a=="--has-molfile") {
175 0 : return (config::hasMolfile()?0:1);
176 10483 : } else if(a=="--has-external-molfile") {
177 0 : return (config::hasExternalMolfile()?0:1);
178 10483 : } else if(a=="--has-zlib") {
179 0 : return (config::hasZlib()?0:1);
180 10483 : } else if(a=="--has-xdrfile") {
181 : return 0; // always ok
182 10483 : } else if(a=="--is-installed") {
183 812 : return (config::isInstalled()?0:1);
184 9671 : } else if(a=="--no-mpi") {
185 : // this is ignored, as it is parsed in main
186 5003 : continue;
187 4668 : } else if(a=="--mpi") {
188 : // this is ignored, as it is parsed in main
189 0 : continue;
190 4668 : } else if(a=="--standalone-executable") {
191 : standalone_executable=true;
192 9336 : } else if(Tools::startWith(a,"--load=")) {
193 10 : a.erase(0,a.find("=")+1);
194 : prefix="";
195 10 : dlloader.load(a);
196 4658 : } else if(a=="--load") {
197 : prefix="--load=";
198 4648 : } else if(a[0]=='-') {
199 0 : std::string msg="ERROR: Unknown option " +a;
200 0 : std::fprintf(stderr,"%s\n",msg.c_str());
201 : return 1;
202 : } else {
203 : break;
204 : }
205 : }
206 :
207 : // Check if plumedRoot/patches/ directory exists (as a further check)
208 4652 : if(!standalone_executable) {
209 4652 : std::vector<std::string> files=Tools::ls(root);
210 4652 : if(find(files.begin(),files.end(),"patches")==files.end()) {
211 : std::string msg=
212 0 : "WARNING: I cannot find "+root+"/patches/ directory. Set PLUMED_ROOT or reinstall PLUMED\n\n";
213 0 : std::fprintf(stderr,"%s",msg.c_str());
214 : }
215 4652 : }
216 :
217 : // Build list of available C++ tools:
218 4652 : std::vector<std::string> availableCxx=cltoolRegister().list();
219 : // Build list of available shell tools:
220 : std::vector<std::string> availableShell;
221 4652 : if(!standalone_executable) {
222 : std::vector<std::string> tmp;
223 9304 : tmp=Tools::ls(std::string(root+"/scripts"));
224 37216 : for(unsigned j=0; j<tmp.size(); ++j) {
225 32564 : size_t ff=tmp[j].find(".sh");
226 32564 : if(ff==std::string::npos) {
227 0 : tmp[j].erase();
228 : } else {
229 32564 : tmp[j].erase(ff);
230 : }
231 : }
232 37216 : for(unsigned j=0; j<tmp.size(); ++j)
233 32564 : if(tmp[j].length()>0) {
234 32564 : availableShell.push_back(tmp[j]);
235 : }
236 4652 : }
237 :
238 4652 : if(printhelp) {
239 : std::string msg=
240 : "Usage: plumed [options] [command] [command options]\n"
241 : " plumed [command] -h|--help: to print help for a specific command\n"
242 : "Options:\n"
243 : " [help|-h|--help] : to print this help\n"
244 : " [--is-installed] : fails if plumed is not installed\n"
245 : " [--has-mpi] : fails if plumed is running without MPI\n"
246 : " [--has-dlopen] : fails if plumed is compiled without dlopen\n"
247 : " [--load LIB] : loads a shared object (typically a plugin library)\n"
248 : " [--standalone-executable] : tells plumed not to look for commands implemented as scripts\n"
249 4 : "Commands:\n";
250 : std::fprintf(out,"%s",msg.c_str());
251 80 : for(unsigned j=0; j<availableCxx.size(); ++j) {
252 152 : auto cl=cltoolRegister().create(dlloader.getHandles(),CLToolOptions(availableCxx[j]));
253 76 : plumed_assert(cl);
254 152 : std::string manual=availableCxx[j]+" : "+cl->description();
255 : std::fprintf(out," plumed %s\n", manual.c_str());
256 76 : }
257 32 : for(unsigned j=0; j<availableShell.size(); ++j) {
258 : std::string manual;
259 28 : if(Subprocess::available()) {
260 56 : auto cmd=config::getEnvCommand()+" \""+root+"/scripts/"+availableShell[j]+".sh\" --description";
261 28 : auto proc=Subprocess(cmd);
262 : // we only need the first line, so this is fine:
263 28 : proc.getline(manual);
264 : // process will be killed immediately after
265 28 : } else {
266 : manual="(doc not avail)";
267 : }
268 56 : manual= availableShell[j]+" : "+manual;
269 : std::fprintf(out," plumed %s\n", manual.c_str());
270 : }
271 : return 0;
272 : }
273 4648 : if(i==argc) {
274 : std::fprintf(out,"%s","Nothing to do. Use 'plumed help' for help\n");
275 : return 0;
276 : }
277 :
278 : // this is the command to be executed:
279 4648 : std::string command(argv[i]);
280 :
281 4648 : if(find(availableCxx.begin(),availableCxx.end(),command)!=availableCxx.end()) {
282 7718 : auto cl=cltoolRegister().create(dlloader.getHandles(),CLToolOptions(command));
283 3859 : plumed_assert(cl);
284 : // Read the command line options (returns false if we are just printing help)
285 3859 : if( !cl->readInput( argc-i,&argv[i],in,out ) ) {
286 : return 0;
287 : }
288 3859 : int ret=cl->main(in,out,pc);
289 : return ret;
290 3859 : }
291 :
292 789 : if(find(availableShell.begin(),availableShell.end(),command)!=availableShell.end()) {
293 789 : plumed_massert(in==stdin,"shell tools can only work on stdin");
294 789 : plumed_massert(out==stdout,"shell tools can only work on stdin");
295 1578 : std::string cmd=config::getEnvCommand()+" \""+root+"/scripts/"+command+".sh\"";
296 3020 : for(int j=i+1; j<argc; j++) {
297 4462 : cmd+=std::string(" ")+argv[j];
298 : }
299 789 : int r=std::system(cmd.c_str());
300 : // this is necessary since system seems to return numbers which are multiple
301 : // of 256. this would make the interpretation by the shell wrong
302 : // I just return 1 in case of failure and 0 in case of success
303 789 : if(r!=0) {
304 : return 1;
305 : } else {
306 670 : return 0;
307 : }
308 : }
309 :
310 0 : std::string msg="ERROR: unknown command " + command + ". Use 'plumed help' for help";
311 0 : std::fprintf(stderr,"%s\n",msg.c_str());
312 : return 1;
313 :
314 10116 : }
315 : }
|