buildat/src/server/rccpp.cpp

211 lines
5.6 KiB
C++

// http://www.apache.org/licenses/LICENSE-2.0
// Copyright 2014 Perttu Ahola <celeron55@gmail.com>
#include "rccpp.h"
#include "core/log.h"
#include "interface/server.h"
#include "interface/process.h"
#include "interface/fs.h"
#include <c55/filesys.h>
#include <vector>
#include <string>
#include <iostream>
#ifdef _WIN32
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
// Without this some of the network functions are not found on mingw
#ifndef _WIN32_WINNT
#define _WIN32_WINNT 0x0501
#endif
#include <windows.h>
#else
#include <dlfcn.h>
#endif
#include <cstddef>
#include <vector>
#include <cassert>
#include <memory>
#include <string>
#include <unordered_map>
#include <algorithm>
#define MODULE "__rccpp"
namespace rccpp {
#ifdef _WIN32
static void* library_load(const char *filename){
return LoadLibrary(filename);
}
static void library_unload(void *module){
FreeLibrary((HMODULE)module);
}
static void* library_get_address(void *module, const char *name){
// FARPROC {aka int (__attribute__((__stdcall__)) *)()}
return (void*)GetProcAddress((HMODULE)module, name);
}
static const char* library_error(){
DWORD last_error = GetLastError();
if(!last_error)
return "No error";
static TCHAR buf[1000];
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, last_error,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), buf, 1000-1, NULL);
return buf;
}
#else
static void* library_load(const char *filename){
return dlopen(filename, RTLD_NOW);
}
static void library_unload(void *module){
dlclose(module);
}
static void* library_get_address(void *module, const char *name){
return dlsym(module, name);
}
static const char* library_error(){
return dlerror();
}
#endif
typedef void*(*RCCPP_Constructor)(interface::Server * server);
struct RCCPP_Info {
void *module;
RCCPP_Constructor constructor;
};
struct CCompiler: public Compiler
{
ss_ m_compiler_command;
std::unordered_map<std::string, RCCPP_Info> m_module_info;
CCompiler(const ss_ &compiler_command):
m_compiler_command(compiler_command)
{
}
bool compile(const std::string &in_path, const std::string &out_path,
const ss_ &extra_cxxflags, const ss_ &extra_ldflags)
{
ss_ command = m_compiler_command;
command += " -DRCCPP -g -fPIC -fvisibility=hidden -shared";
command += " -std=c++11";
if(extra_cxxflags != "")
command += ss_()+" "+extra_cxxflags;
for(const std::string &dir : include_directories) command += " -I"+dir;
for(const std::string &dir : library_directories) command += " -L"+dir;
command += " -o"+out_path;
command += " "+in_path;
if(extra_ldflags != "")
command += ss_()+" "+extra_ldflags;
for(const std::string &lib : libraries) command += " "+lib;
interface::process::ExecOptions exec_opts;
#ifdef _WIN32
// If compiler_command looks like a path, add the directory part of it to
// PATH. This seems to be required on Wine for running mingw g++, in which
// case the DLLs are in the directory of the called executable, but the
// called executable calls another executable in a directory that does not
// contain the DLLs.
if(m_compiler_command.find("/") != ss_::npos ||
m_compiler_command.find("\\") != ss_::npos){
ss_ command_dir = interface::fs::strip_file_name(m_compiler_command);
exec_opts.env["PATH"] = command_dir + ";" +
interface::process::get_environment_variable("PATH");
log_d(MODULE, "Using PATH=%s", cs(exec_opts.env["PATH"]));
}
#endif
int exit_status = interface::process::shell_exec(command, exec_opts);
return exit_status == 0;
}
bool build(const std::string &module_name,
const std::string &in_path, const std::string &out_path,
const ss_ &extra_cxxflags, const ss_ &extra_ldflags,
bool skip_compile)
{
log_nd(MODULE, "Building %s: %s -> %s... ", cs(module_name), cs(in_path),
cs(out_path));
std::string out_dir = c55fs::stripFilename(out_path);
c55fs::CreateAllDirs(out_dir);
if(skip_compile){
log_d(MODULE, "Skipped");
} else {
if(!compile(in_path, out_path, extra_cxxflags, extra_ldflags)){
log_d(MODULE, "Failed!");
return false;
}
log_d(MODULE, "Success!");
}
void *new_module = library_load(out_path.c_str());
if(new_module == NULL){
log_i(MODULE, "Failed to load compiled library: %s (path: %s)",
library_error(), cs(out_path));
return false;
}
ss_ constructor_name = ss_()+"createModule_"+module_name;
RCCPP_Constructor constructor = (RCCPP_Constructor)library_get_address(
new_module, constructor_name.c_str());
if(constructor == nullptr){
log_i(MODULE, "%s is missing from the library", cs(constructor_name));
return false;
}
auto it = m_module_info.find(module_name);
if(it != m_module_info.end()){
RCCPP_Info &funcs = it->second;
funcs.constructor = constructor;
funcs.module = new_module;
} else {
RCCPP_Info funcs;
funcs.constructor = constructor;
funcs.module = new_module;
m_module_info.emplace(module_name, std::move(funcs));
}
return true;
}
void* construct(const char *name, interface::Server *server)
{
auto it = m_module_info.find(name);
if(it == m_module_info.end()){
log_w(MODULE, "construct(%s): Module is not loaded", name);
return NULL;
}
RCCPP_Info &info = it->second;
RCCPP_Constructor constructor = info.constructor;
return constructor(server);
}
void unload(const std::string &module_name)
{
auto it = m_module_info.find(module_name);
if(it == m_module_info.end()){
assert(nullptr && "Failed to get class info");
return;
}
void *module = it->second.module;
library_unload(module);
m_module_info.erase(module_name);
}
};
Compiler* createCompiler(const ss_ &compiler_command)
{
return new CCompiler(compiler_command);
}
} // rccpp
// vim: set noet ts=4 sw=4: