You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
gnucap-custom/c_make_attach.cc

260 lines
8.0 KiB
C++

/* -*- C++ -*-
* Copyright (C) 2007 Albert Davis
* Author: 2018 Felix Salfelder <felix@salfelder.org>
* 2007 Albert Davis <aldavis@gnu.org>
*
* This file is part of "Gnucap", the Gnu Circuit Analysis Package
*
* This program 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, or (at your option)
* any later version.
*
* This program 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 this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*------------------------------------------------------------------
*/
#include "e_cardlist.h"
#include "c_comand.h"
#include "constant.h"
#include "globals.h"
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include <sys/stat.h>
#include <libgen.h>
#include <sys/types.h>
#include <sys/wait.h>
/*--------------------------------------------------------------------------*/
namespace {
/*--------------------------------------------------------------------------*/
template<class T>
T getenv(const std::string& s, T def) {
char* ev = ::getenv(s.c_str());
if(!ev){
return def;
}else{
return T(ev);
}
}
/*--------------------------------------------------------------------------*/
std::map<const std::string, void*> attach_list;
/*--------------------------------------------------------------------------*/
/*--------------------------------------------------------------------------*/
struct built_in_load{
built_in_load(){
_cmd = command_dispatcher["load"];
if(!_cmd){ untested();
throw(Exception("need load command"));
}else{
}
}
CMD* _cmd;
}bil;
/*--------------------------------------------------------------------------*/
class CMD_ATTACH : public CMD {
static void compile(std::string& filename, std::string source, std::string make);
static void do_attach(std::string filename, int flags, bool force=false);
public:
void do_it(CS& cmd, CARD_LIST*)
{
size_t here = cmd.cursor();
int dl_scope = RTLD_LOCAL;
int check = RTLD_NOW;
bool force = false;
std::string make=getenv("GNUCAP_MAKE", GNUCAP_MAKE);
// RTLD_NOW means to resolve symbols on loading
// RTLD_LOCAL means symbols defined in a plugin are local
do {
if (cmd.umatch("public ")) { untested();
dl_scope = RTLD_GLOBAL;
// RTLD_GLOBAL means symbols defined in a plugin are global
// Use this when a plugin depends on another.
}else if (cmd.umatch("lazy ")) { untested();
check = RTLD_LAZY;
// RTLD_LAZY means to defer resolving symbols until needed
// Use when a plugin will not load because of unresolved symbols,
// but it may work without it.
}else if (cmd.umatch("force ")) { untested();
force = true;
// ignore version mismatch of some kind
}else{
Get(cmd,"make{file}", &make);
}
} while (cmd.more() && !cmd.stuck(&here));
trace1("attach::do_it", make);
std::string file_name;
cmd >> file_name;
OMSTREAM _out = IO::mstdout;
// uf?! _out.outset(cmd);
if(file_name==""){ untested();
std::string comma;
for (std::map<std::string, void*>::iterator
ii = attach_list.begin(); ii != attach_list.end(); ++ii) { untested();
if (ii->second) { untested();
_out << comma << ii->first;
comma = ",\n";
}else{ untested();
}
}
_out << "\n";
return;
}
void*& handle = attach_list[file_name];
trace2("...", file_name, handle);
if (handle) { untested();
if (CARD_LIST::card_list.is_empty()) { untested();
cmd.warn(bDANGER, here, "\"" + file_name + "\": already loaded, replacing");
dlclose(handle);
handle = NULL;
}else{untested();
cmd.reset(here);
throw Exception_CS("already loaded, cannot replace when there is a circuit", cmd);
}
}else{
}
std::string source_filename(file_name);
// FIXME: incomplete... some more control...
// global list of supported suffixes?
if (file_name.size()>3 && !strcmp(file_name.c_str()+file_name.size()-3,".so")) { untested();
source_filename = "";
}else if (file_name.size()>3 && file_name.c_str()[file_name.size()-3] == '.') {
file_name[file_name.size()-2]='s';
file_name[file_name.size()-1]='o';
if(file_name[0]=='/') { itested();
} else {
char* cwd = get_current_dir_name(); // POSIX, no C++ implementation available
source_filename = std::string(cwd) + "/" + source_filename;
free(cwd);
}
} else { untested();
source_filename = "";
}
if (source_filename!="") {
trace1("attach", source_filename);
assert(source_filename[0]=='/');
try {
compile(file_name, source_filename, make);
}catch(Exception& e){ untested();
cmd.reset(here);
throw Exception_CS(e.message(), cmd);
}
}
do_attach(file_name, check | dl_scope, force);
}
} p1;
DISPATCHER<CMD>::INSTALL d1(&command_dispatcher, "attach|load", &p1);
/*--------------------------------------------------------------------------*/
void CMD_ATTACH::do_attach(std::string file_name, int flags, bool force)
{
std::string cmd;
if(flags & RTLD_GLOBAL){ untested();
cmd += "public ";
}else{
}
if(flags & RTLD_LAZY){ untested();
cmd += "lazy ";
}else{
}
CS Cmd(CS::_STRING, cmd + file_name);
bil._cmd->do_it(Cmd, &CARD_LIST::card_list);
}
/*--------------------------------------------------------------------------*/
void CMD_ATTACH::compile(std::string &filename, std::string source_filename,
std::string make)
{
struct stat ccattrib;
int ccstat = stat(source_filename.c_str(), &ccattrib);
if (ccstat) { untested();
throw Exception("cannot compile: " + source_filename +
" does not exist (" + to_string(ccstat) + ")\n");
} else {
}
struct stat soattrib;
int sostat = stat(filename.c_str(), &soattrib);
std::string gnucap_cppflags=getenv("GNUCAP_CPPFLAGS", GNUCAP_CPPFLAGS);
FILE* f = NULL;
char* sf2 = strdup(filename.c_str());
std::string f1(basename(sf2)); free(sf2);
char* tmplt = NULL;
char* t = NULL;
if (filename[0]=='/') { untested();
}else if (filename[0]=='.') {
}else{
filename = "./" + filename;
}
if (!sostat && ccattrib.st_mtime < soattrib.st_mtime) {
trace0("so exists and is newer");
return;
} else if(!sostat) { untested();
trace0("so needs to be refreshed");
f = fopen(filename.c_str(),"a");
if (f) { untested();
trace0("so is writable");
fclose(f);
} else { untested();
tmplt = strdup("/tmp/soXXXXXX");
t = mkdtemp(tmplt);
if (!t) throw Exception("cannot create temporary file");
filename = std::string(t) + "/" + f1;
}
} else {
}
free(tmplt);
t = NULL;
char* sf1 = strdup(source_filename.c_str());
char* fn1 = strdup(filename.c_str());
std::string d1(dirname(fn1));
std::string d2(dirname(sf1));
int childret;
pid_t p = vfork();
if (p) {
waitpid(p, &childret, 0);
} else {
error(bDEBUG, "calling " + make + " -C" + d1 + " VPATH=" + d2 + " " + f1 + "\n");
int ret = execlp( make.c_str(), make.c_str(),
"-C", d1.c_str(),
("GNUCAP_CPPFLAGS="+gnucap_cppflags+"").c_str(),
("VPATH=" + d2).c_str(),
f1.c_str(), (char*) NULL);
_exit(ret);
}
free(sf1);
free(fn1);
// if (t) { incomplete();
// // rm -r t;
// }
if(childret){ untested();
throw Exception("cannot make " + filename + "(" + to_string(childret) + ")");
}
}
/*--------------------------------------------------------------------------*/
/*--------------------------------------------------------------------------*/
}
/*--------------------------------------------------------------------------*/
/*--------------------------------------------------------------------------*/
// vim:ts=8:sw=2:noet: