/*
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;
}