LCOV - code coverage report
Current view: top level - core - CLToolMain.cpp (source / functions) Hit Total Coverage
Test: plumed test coverage Lines: 113 134 84.3 %
Date: 2024-10-18 13:59:31 Functions: 5 5 100.0 %

          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             : }

Generated by: LCOV version 1.16