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 5363 : CLToolMain::CLToolMain():
41 5363 : argc(0),
42 5363 : in(stdin),
43 5363 : out(stdout)
44 : {
45 5363 : }
46 :
47 10726 : CLToolMain::~CLToolMain() {
48 : // empty destructor to delete unique_ptr
49 10726 : }
50 :
51 : #define CHECK_NULL(val,word) plumed_assert(val) << "NULL pointer received in cmd(\"CLTool " << word << "\"";
52 :
53 16374 : void CLToolMain::cmd(std::string_view 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 Tools::FastStringUnorderedMap<int> word_map = {
62 : #include "CLToolMainMap.inc"
63 16374 : };
64 :
65 : gch::small_vector<std::string_view> words;
66 16374 : Tools::getWordsSimple(words,word);
67 16374 : unsigned nw=words.size();
68 16374 : if(nw==0) {
69 : // do nothing
70 : } else {
71 : int iword=-1;
72 : const char*const*v;
73 : const char*vv;
74 : const auto it=word_map.find(words[0]);
75 16374 : if(it!=word_map.end()) iword=it->second;
76 16374 : switch(iword) {
77 : case cmd_setArgc:
78 5279 : CHECK_NULL(val,word);
79 5279 : argc=val.get<int>();
80 5279 : break;
81 : case cmd_setArgv:
82 5279 : CHECK_NULL(val,word);
83 5279 : v=val.get<const char*const*>({argc});
84 34129 : for(int i=0; i<argc; ++i) argv.push_back(std::string(v[i]));
85 : break;
86 : case cmd_setArgvLine:
87 84 : CHECK_NULL(val,word);
88 84 : vv=val.getCString();
89 84 : argv=Tools::getWords(vv);
90 84 : break;
91 : case cmd_setIn:
92 0 : CHECK_NULL(val,word);
93 0 : in=val.get<FILE*>();
94 0 : break;
95 : case cmd_setOut:
96 17 : CHECK_NULL(val,word);
97 17 : out=val.get<FILE*>();
98 17 : break;
99 352 : case cmd_setMPIComm:
100 352 : comm.Set_comm(val);
101 : break;
102 0 : case cmd_setMPIFComm:
103 0 : comm.Set_fcomm(val);
104 : break;
105 : case cmd_run:
106 5363 : CHECK_NULL(val,word);
107 5363 : argc=argv.size();
108 : {
109 34701 : int n=0; for(int i=0; i<argc; ++i) n+=argv[i].length()+1;
110 5363 : std::vector<char> args(n);
111 5363 : std::vector<char*> vvv(argc);
112 : char* ptr=&args[0];
113 34701 : for(int i=0; i<argc; ++i) {
114 29338 : vvv[i]=ptr;
115 253542 : for(unsigned c=0; c<argv[i].length(); ++c) {
116 224204 : *ptr=argv[i][c]; ptr++;
117 : }
118 29338 : *ptr=0; ptr++;
119 : }
120 5363 : val.set(int(run(argc,&vvv[0],in,out,comm)));
121 : }
122 5363 : break;
123 0 : default:
124 0 : plumed_error() << "cannot interpret cmd(\"CLTool " << word << "\"). check plumed developers manual to see the available commands.";
125 : break;
126 : }
127 : }
128 16374 : }
129 :
130 : /**
131 : This is the entry point to the command line tools
132 : included in the plumed library.
133 : */
134 :
135 5363 : int CLToolMain::run(int argc, char **argv,FILE*in,FILE*out,Communicator& pc) {
136 : int i;
137 : bool printhelp=false;
138 :
139 5363 : DLLoader dlloader;
140 :
141 5363 : std::string root=config::getPlumedRoot();
142 :
143 : bool standalone_executable=false;
144 :
145 : // Start parsing options
146 5363 : std::string prefix("");
147 5363 : std::string a("");
148 10294 : for(i=1; i<argc; i++) {
149 20588 : a=prefix+argv[i];
150 10294 : if(a.length()==0) continue;
151 30882 : if(a=="help" || a=="-h" || a=="--help") {
152 : printhelp=true;
153 : break;
154 10290 : } else if(a=="--has-mpi") {
155 0 : if(Communicator::initialized()) return 0;
156 0 : else return 1;
157 10290 : } else if(a=="--has-cregex") {
158 0 : return (config::hasCregex()?0:1);
159 10290 : } else if(a=="--has-dlopen") {
160 0 : return (config::hasDlopen()?0:1);
161 10290 : } else if(a=="--has-molfile") {
162 0 : return (config::hasMolfile()?0:1);
163 10290 : } else if(a=="--has-external-molfile") {
164 0 : return (config::hasExternalMolfile()?0:1);
165 10290 : } else if(a=="--has-zlib") {
166 0 : return (config::hasZlib()?0:1);
167 10290 : } else if(a=="--has-xdrfile") {
168 : return 0; // always ok
169 10290 : } else if(a=="--is-installed") {
170 794 : return (config::isInstalled()?0:1);
171 9496 : } else if(a=="--no-mpi") {
172 : // this is ignored, as it is parsed in main
173 4911 : continue;
174 4585 : } else if(a=="--mpi") {
175 : // this is ignored, as it is parsed in main
176 0 : continue;
177 4585 : } else if(a=="--standalone-executable") {
178 : standalone_executable=true;
179 9170 : } else if(Tools::startWith(a,"--load=")) {
180 10 : a.erase(0,a.find("=")+1);
181 : prefix="";
182 10 : dlloader.load(a);
183 4575 : } else if(a=="--load") {
184 : prefix="--load=";
185 4565 : } else if(a[0]=='-') {
186 0 : std::string msg="ERROR: Unknown option " +a;
187 0 : std::fprintf(stderr,"%s\n",msg.c_str());
188 : return 1;
189 : } else break;
190 : }
191 :
192 : // Check if plumedRoot/patches/ directory exists (as a further check)
193 4569 : if(!standalone_executable) {
194 4569 : std::vector<std::string> files=Tools::ls(root);
195 4569 : if(find(files.begin(),files.end(),"patches")==files.end()) {
196 : std::string msg=
197 0 : "WARNING: I cannot find "+root+"/patches/ directory. Set PLUMED_ROOT or reinstall PLUMED\n\n";
198 0 : std::fprintf(stderr,"%s",msg.c_str());
199 : }
200 4569 : }
201 :
202 : // Build list of available C++ tools:
203 4569 : std::vector<std::string> availableCxx=cltoolRegister().list();
204 : // Build list of available shell tools:
205 : std::vector<std::string> availableShell;
206 4569 : if(!standalone_executable) {
207 : std::vector<std::string> tmp;
208 9138 : tmp=Tools::ls(std::string(root+"/scripts"));
209 36552 : for(unsigned j=0; j<tmp.size(); ++j) {
210 31983 : size_t ff=tmp[j].find(".sh");
211 31983 : if(ff==std::string::npos) tmp[j].erase();
212 31983 : else tmp[j].erase(ff);
213 : }
214 36552 : for(unsigned j=0; j<tmp.size(); ++j) if(tmp[j].length()>0) availableShell.push_back(tmp[j]);
215 4569 : }
216 :
217 4569 : if(printhelp) {
218 : std::string msg=
219 : "Usage: plumed [options] [command] [command options]\n"
220 : " plumed [command] -h|--help: to print help for a specific command\n"
221 : "Options:\n"
222 : " [help|-h|--help] : to print this help\n"
223 : " [--is-installed] : fails if plumed is not installed\n"
224 : " [--has-mpi] : fails if plumed is running without MPI\n"
225 : " [--has-dlopen] : fails if plumed is compiled without dlopen\n"
226 : " [--load LIB] : loads a shared object (typically a plugin library)\n"
227 : " [--standalone-executable] : tells plumed not to look for commands implemented as scripts\n"
228 4 : "Commands:\n";
229 : std::fprintf(out,"%s",msg.c_str());
230 80 : for(unsigned j=0; j<availableCxx.size(); ++j) {
231 152 : auto cl=cltoolRegister().create(dlloader.getHandles(),CLToolOptions(availableCxx[j]));
232 76 : plumed_assert(cl);
233 152 : std::string manual=availableCxx[j]+" : "+cl->description();
234 : std::fprintf(out," plumed %s\n", manual.c_str());
235 76 : }
236 32 : for(unsigned j=0; j<availableShell.size(); ++j) {
237 : std::string manual;
238 28 : if(Subprocess::available()) {
239 56 : auto cmd=config::getEnvCommand()+" \""+root+"/scripts/"+availableShell[j]+".sh\" --description";
240 28 : auto proc=Subprocess(cmd);
241 : // we only need the first line, so this is fine:
242 28 : proc.getline(manual);
243 : // process will be killed immediately after
244 28 : } else {
245 : manual="(doc not avail)";
246 : }
247 56 : manual= availableShell[j]+" : "+manual;
248 : std::fprintf(out," plumed %s\n", manual.c_str());
249 : }
250 : return 0;
251 : }
252 4565 : if(i==argc) {
253 : std::fprintf(out,"%s","Nothing to do. Use 'plumed help' for help\n");
254 : return 0;
255 : }
256 :
257 : // this is the command to be executed:
258 4565 : std::string command(argv[i]);
259 :
260 4565 : if(find(availableCxx.begin(),availableCxx.end(),command)!=availableCxx.end()) {
261 7566 : auto cl=cltoolRegister().create(dlloader.getHandles(),CLToolOptions(command));
262 3783 : plumed_assert(cl);
263 : // Read the command line options (returns false if we are just printing help)
264 3783 : if( !cl->readInput( argc-i,&argv[i],in,out ) ) { return 0; }
265 3783 : int ret=cl->main(in,out,pc);
266 : return ret;
267 3783 : }
268 :
269 782 : if(find(availableShell.begin(),availableShell.end(),command)!=availableShell.end()) {
270 782 : plumed_massert(in==stdin,"shell tools can only work on stdin");
271 782 : plumed_massert(out==stdout,"shell tools can only work on stdin");
272 1564 : std::string cmd=config::getEnvCommand()+" \""+root+"/scripts/"+command+".sh\"";
273 2996 : for(int j=i+1; j<argc; j++) cmd+=std::string(" ")+argv[j];
274 782 : int r=std::system(cmd.c_str());
275 : // this is necessary since system seems to return numbers which are multiple
276 : // of 256. this would make the interpretation by the shell wrong
277 : // I just return 1 in case of failure and 0 in case of success
278 782 : if(r!=0) return 1;
279 666 : else return 0;
280 : }
281 :
282 0 : std::string msg="ERROR: unknown command " + command + ". Use 'plumed help' for help";
283 0 : std::fprintf(stderr,"%s\n",msg.c_str());
284 : return 1;
285 :
286 9932 : }
287 : }
|