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.
260 lines
8.0 KiB
C++
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:
|