/* This file is part of c++script. c++script is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. c++script is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with c++script. If not, see . */ /* Main component for the project. See the README file for more information. */ #include #include #include #include #include #include "c++script.h" using namespace std; bool operator > (const timespec &t1, const timespec &t2) { return tie(t1.tv_sec,t1.tv_nsec) > tie(t2.tv_sec,t2.tv_nsec); } int main (int argc, char *argv[]) { auto envs = cpps::makeenviron(); cpps::progdesc ("c++script [ c++script options ... ] script [ script options ... ] [ script arguments ... ]\n" "\n" "\tSet KEEPTEMPFILE=on to keep the temporary C++ file\n" "\tSet COMPILE=on to force compile the script"); cpps::option keeptempfile ('k',"keeptempfile","Keep temporary C++ file"); cpps::option forcecompile ('c',"compile","Always compile the script"); cpps::option donotexec (' ',"donotexec","Only compile (if needed) the script, do not execute it",false,false); cpps::option target ('t',"target","Where to place the compiled script","",false); cpps::option link_options ('L',"ldoptions","Link options","",false); cpps::option compile_options ('O',"ccoptions","Compiler options","-Wall -O2 -g -std=c++26",false); cpps::endoptions (argc,argv); if (cpps::args.size() == 0){ cpps::usage(); exit (-1); }else{ if(envs.find("KEEPTEMPFILE")!=envs.end()) keeptempfile.val = true; if(envs.find("COMPILE")!=envs.end()) forcecompile.val = true; string exec_cpp(format("{}/.c++script",envs["HOME"])); string exec_dir(format("{}/exec",exec_cpp)); string home_insert(format("{}/insert.cc",exec_cpp)); string share_insert("/usr/share/c++script/insert.cc"); string ccoptions(format("{}/ccoptions",exec_cpp)); { // Make sure the exec directory exist cpps::FILENAME execc(exec_cpp); if (!execc.exist()) mkdir (exec_cpp.c_str(),0755); cpps::FILENAME execd(exec_dir); if (!execd.exist()) mkdir (exec_dir.c_str(),0755); } // The argument is the script cpps::FILENAME f(cpps::args[0]); string absname; for (auto c:f.abspath()|views::transform([](auto c){ return c == '/' ? '_' : c; })) absname += c; // execfile will hold (or is holding) the binary produced by the compiler cpps::FILENAME execfile(target.val.size() != 0 ? target.val : format("{}/{}",exec_dir,absname)); char *tb[cpps::args.size()+1]; for (unsigned i=0; i f.stat().modified){ if (!donotexec.val){ execv (execfile.abspath().c_str(),tb); cout << format ("exec {} failed, {}\n",execfile.abspath(),strerror(errno)); } }else{ // We create a temporary file and compile it char tempname[] = "/tmp/c++script-XXXXXX.cc"; int fd = mkstemps(tempname,3); string script = tempname; ofstream fout; fout.open(script); close (fd); cpps::FILENAME home_ins(home_insert); cpps::FILENAME share_ins(share_insert); if (home_ins.exist()){ for (auto &l:home_ins.cat()){ fout << l.line << "\n"; } }else if (share_ins.exist()){ for (auto &l:share_ins.cat()){ fout << l.line << "\n"; } }else{ fout << "#include \n"; fout << "#include \n"; //fout << "#include \n"; fout << "#include \n"; fout << "using namespace std;\n"; fout << "using namespace cpps;\n"; } vector includes,before_main,mainfunc; bool not_include_seen = false; for (auto &l: f.cat()|views::drop(1)){ string tmp = cpps::string_trim(l.line); if (!not_include_seen && (tmp.compare(0,8,"#include")==0 || tmp.compare(0,2,"//")==0)){ includes.push_back(l); }else if (tmp.compare(0,10,"#ccoptions")==0){ compile_options.val = tmp.substr(10); }else if (tmp.compare(0,10,"#ldoptions")==0){ link_options.val = tmp.substr(10); }else{ not_include_seen = true; if (tmp == "#main"){ before_main = move(mainfunc); }else{ mainfunc.push_back(l); } } } for (auto &l:includes){ fout << format ("#line {} \"{}\"\n",l.line_number,f.abspath()); fout << l.line << "\n"; } for (auto &l:before_main){ fout << format ("#line {} \"{}\"\n",l.line_number,f.abspath()); fout << l.line << "\n"; } fout << "int main (int argc, char *argv[])\n"; fout << "{\n"; fout << "auto envs = makeenviron();\n"; //fout << format ("#line 2 \"{}\"\n",f.abspath()); bool last_option = true; for (auto &l: mainfunc){ string tmp = cpps::string_trim(l.line); if (tmp.compare(0,2,"//")!=0 && tmp.compare(0,6,"option")!=0 && tmp.compare(0,8,"progdesc")!=0){ if (last_option){ fout << "option donot_do_anything(' ',\"donotdoanything\",\"End immediatly, just to prove the script works\",false,false);\n"; fout << "endoptions(argc,argv);\n"; fout << "if (donot_do_anything.val) return 0;\n"; fout << format ("#line {} \"{}\"\n",l.line_number,f.abspath()); } last_option = false; } fout << format ("#line {} \"{}\"\n",l.line_number,f.abspath()); fout << l.line << "\n"; } if (last_option) fout << "endoptions(argc,argv);\n"; fout << "}\n"; fout.close(); string options ("-lc++script"); cpps::FILENAME optfile(ccoptions); if (optfile.exist()){ options.clear(); for (auto &l:optfile.cat()){ options += ' ' + l.line; } } if (system(format("g++ -I /usr/include/c++script -I. {} {} {} -o {} {} -lstdc++" ,compile_options.val,script,options,execfile.abspath(),link_options.val).c_str()) == 0){ if (!keeptempfile.val) unlink(script.c_str()); if (!donotexec.val){ execv (execfile.abspath().c_str(),tb); cout << format ("exec {} failed, {}\n",execfile.abspath(),strerror(errno)); } }else{ if (!keeptempfile.val) unlink(script.c_str()); cerr << format ("compile of {} failed\n",script); } } } // Gets there only if there is an error, so always return -1 return -1; }