CASM
AClustersApproachtoStatisticalMechanics
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules
RuntimeLibrary.cc
Go to the documentation of this file.
2 #include "casm/casm_io/Log.hh"
3 
4 namespace CASM {
5 
8  RuntimeLibrary::RuntimeLibrary(std::string filename_base,
9  std::string compile_options,
10  std::string so_options,
11  std::string compile_msg,
12  const Logging &logging) :
13  Logging(logging),
14  m_filename_base(filename_base),
15  m_compile_options(compile_options),
16  m_so_options(so_options),
17  m_handle(nullptr) {
18 
19  // If the shared library doesn't exist
20  if(!fs::exists(m_filename_base + ".so")) {
21 
22  // But the library source code does
23  if(fs::exists(m_filename_base + ".cc")) {
24 
25  // Compile it
27  log().begin_lap();
28  log() << compile_msg << std::endl;
29  try {
30  _compile();
31  }
32  catch(std::exception &e) {
33  log() << "Error compiling clexulator. To fix: \n";
34  log() << " - Check compiler error messages.\n";
35  log() << " - Check compiler options with 'casm settings -l'\n";
36  log() << " - Update compiler options with 'casm settings --set-compile-options '...options...'\n";
37  log() << " - Make sure the casm headers can be found by including '-I/path/to/casm'\n";
38  throw;
39  }
40  log() << "compile time: " << log().lap_time() << " (s)\n" << std::endl;
41  }
42  else {
43  throw std::runtime_error(
44  std::string("Error in RuntimeLibrary\n") +
45  " Could not find '" + m_filename_base + ".so' or '" + m_filename_base + ".cc'");
46  }
47  }
48 
49  // If the shared library exists
50  if(fs::exists(m_filename_base + ".so")) {
51 
52  // Load the library with the Clexulator
53  _load();
54 
55  }
56  else {
57  throw std::runtime_error(
58  std::string("Error in Clexulator constructor\n") +
59  " Did not find '" + m_filename_base + ".so'");
60  }
61 
62  }
63 
65  if(m_handle != nullptr) {
66  _close();
67  }
68  }
69 
82 
83  // compile the source code into a dynamic library
84  Popen p;
85  std::string cmd = m_compile_options + " -o " + m_filename_base + ".o" + " -c " + m_filename_base + ".cc";
86  p.popen(cmd);
87  if(p.exit_code()) {
88  err_log() << "Error compiling: " << m_filename_base + ".cc" << std::endl;
89  err_log() << "Attempted: " << cmd << std::endl;
90  err_log() << p.gets() << std::endl;
91  throw std::runtime_error("Can not compile " + m_filename_base + ".cc");
92  }
93 
94  cmd = m_so_options + " -o " + m_filename_base + ".so" + " " + m_filename_base + ".o";
95  p.popen(cmd);
96  if(p.exit_code()) {
97  err_log() << "Error compiling shared object: " << m_filename_base + ".so" << std::endl;
98  err_log() << "Attempted: " << cmd << std::endl;
99  err_log() << p.gets() << std::endl;
100  throw std::runtime_error("Can not compile " + m_filename_base + ".o");
101  }
102  }
103 
109 
110  m_handle = dlopen((m_filename_base + ".so").c_str(), RTLD_NOW);
111  if(!m_handle) {
112  fprintf(stderr, "dlopen failed: %s\n", dlerror());
113  throw std::runtime_error(std::string("Cannot open library: ") + m_filename_base + ".so");
114  }
115  }
116 
121  // close
122  if(m_handle != nullptr) {
123  dlclose(m_handle);
124  }
125  }
126 
129  _close();
130  // rm
131  Popen p;
132  p.popen(std::string("rm -f ") + m_filename_base + ".cc " + m_filename_base + ".o " + m_filename_base + ".so");
133  }
134 
135  namespace {
136 
137  std::vector<std::string> _cxx_env() {
138  return std::vector<std::string> {
139  "CASM_CXX",
140  "CXX"
141  };
142  }
143 
144  std::vector<std::string> _cxxflags_env() {
145  return std::vector<std::string> {
146  "CASM_CXXFLAGS",
147  };
148  }
149 
150  std::vector<std::string> _soflags_env() {
151  return std::vector<std::string> {
152  "CASM_SOFLAGS"
153  };
154  }
155 
156  // std::vector<std::string> _casm_env() {
157  // return std::vector<std::string> {
158  // "CASM_PREFIX"
159  // };
160  // }
161  //
162  // std::vector<std::string> _boost_env() {
163  // return std::vector<std::string> {
164  // "CASM_BOOST_PREFIX"
165  // };
166  // }
167 
169  std::pair<std::string, std::string> _use_env(std::vector<std::string> var, std::string _default = "") {
170  for(const auto &v : var) {
171  char *_env = std::getenv(v.c_str());
172  if(_env != nullptr) {
173  return std::make_pair(std::string(_env), v);
174  }
175  }
176  return std::make_pair(_default, "default");
177  }
178 
179  fs::path find_executable(std::string name) {
180  char *_env = std::getenv("PATH");
181  std::vector<std::string> splt;
182  boost::split(splt, _env, boost::is_any_of(":"), boost::token_compress_on);
183 
184  for(const auto &p : splt) {
185  fs::path test {fs::path(p) / name};
186  if(fs::exists(test)) {
187  return test;
188  }
189  }
190  return fs::path();
191  }
192 
193  fs::path find_include(std::string executable_name, std::string include_name) {
194  fs::path loc = find_executable(executable_name);
195  if(loc.empty()) {
196  return loc;
197  }
198  fs::path maybe_includedir = loc.parent_path().parent_path() / "include";
199  if(fs::exists(maybe_includedir / include_name)) {
200  return maybe_includedir / include_name;
201  }
202  return fs::path();
203  }
204 
205  fs::path find_includedir(std::string executable_name, std::string include_name) {
206  return find_include(executable_name, include_name).parent_path();
207  }
208 
209  fs::path find_lib(std::string executable_name, std::string lib_name) {
210  fs::path loc = find_executable(executable_name);
211  if(loc.empty()) {
212  return loc;
213  }
214  fs::path maybe_prefix = loc.parent_path().parent_path();
215 
216  auto check_dir = [&](fs::path test_libdir) {
217  std::vector<std::string> check {"dylib", "so"};
218  for(const auto &s : check) {
219  auto res = test_libdir / (lib_name + "." + s);
220  if(fs::exists(res)) {
221  return res;
222  }
223  }
224  return fs::path();
225  };
226 
227  auto check_names = [&](fs::path test_prefix) {
228  std::vector<fs::path> check {"lib", "lib64", "lib/x86_64-linux-gnu"};
229  for(const auto &s : check) {
230  auto res = check_dir(test_prefix / s);
231  if(!res.empty()) {
232  return res;
233  }
234  }
235  return fs::path();
236  };
237 
238  return check_names(maybe_prefix);
239  }
240 
241  fs::path find_libdir(std::string executable_name, std::string lib_name) {
242  return find_lib(executable_name, lib_name).parent_path();
243  }
244 
245  }
246 
252  std::pair<std::string, std::string> RuntimeLibrary::default_cxx() {
253  return _use_env(_cxx_env(), "g++");
254  }
255 
259  std::pair<std::string, std::string> RuntimeLibrary::default_cxxflags() {
260  return _use_env(_cxxflags_env(), "-O3 -Wall -fPIC --std=c++11");
261  }
262 
266  std::pair<std::string, std::string> RuntimeLibrary::default_soflags() {
267  return _use_env(_soflags_env(), "-shared -lboost_system");
268  }
269 
274  std::pair<fs::path, std::string> RuntimeLibrary::default_casm_includedir() {
275  char *_env;
276 
277  // if CASM_INCLUDEDIR exists
278  _env = std::getenv("CASM_INCLUDEDIR");
279  if(_env != nullptr) {
280  return std::make_pair(std::string(_env), "CASM_INCLUDEDIR");
281  }
282 
283  // if CASM_PREFIX exists
284  _env = std::getenv("CASM_PREFIX");
285  if(_env != nullptr) {
286  return std::make_pair(fs::path(_env) / "include", "CASM_PREFIX");
287  }
288 
289  // relpath from ccasm
290  fs::path _default = find_includedir("ccasm", "casm");
291  if(!_default.empty()) {
292  return std::make_pair(_default, "relpath");
293  }
294 
295  // else
296  return std::make_pair(fs::path("/not/found"), "notfound");
297  }
298 
303  std::pair<fs::path, std::string> RuntimeLibrary::default_casm_libdir() {
304  char *_env;
305 
306  // if CASM_INCLUDEDIR exists
307  _env = std::getenv("CASM_LIBDIR");
308  if(_env != nullptr) {
309  return std::make_pair(std::string(_env), "CASM_LIBDIR");
310  }
311 
312  // if CASM_PREFIX exists
313  _env = std::getenv("CASM_PREFIX");
314  if(_env != nullptr) {
315  return std::make_pair(fs::path(_env) / "lib", "CASM_PREFIX");
316  }
317 
318  // relpath from ccasm
319  fs::path _default = find_libdir("ccasm", "libcasm");
320  if(!_default.empty()) {
321  return std::make_pair(_default, "relpath");
322  }
323 
324  // else
325  return std::make_pair(fs::path("/not/found"), "notfound");
326  }
327 
332  std::pair<fs::path, std::string> RuntimeLibrary::default_boost_includedir() {
333  char *_env;
334 
335  // if CASM_BOOST_INCLUDEDIR exists
336  _env = std::getenv("CASM_BOOST_INCLUDEDIR");
337  if(_env != nullptr) {
338  return std::make_pair(std::string(_env), "CASM_BOOST_INCLUDEDIR");
339  }
340 
341  // if CASM_BOOST_PREFIX exists
342  _env = std::getenv("CASM_BOOST_PREFIX");
343  if(_env != nullptr) {
344  return std::make_pair(fs::path(_env) / "include", "CASM_BOOST_PREFIX");
345  }
346 
347  // relpath from ccasm
348  fs::path _default = find_includedir("ccasm", "boost");
349  if(!_default.empty()) {
350  return std::make_pair(_default, "relpath");
351  }
352 
353  // else
354  return std::make_pair(fs::path("/not/found"), "notfound");
355  }
356 
361  std::pair<fs::path, std::string> RuntimeLibrary::default_boost_libdir() {
362  char *_env;
363 
364  // if CASM_BOOST_INCLUDEDIR exists
365  _env = std::getenv("CASM_BOOST_LIBDIR");
366  if(_env != nullptr) {
367  return std::make_pair(std::string(_env), "CASM_BOOST_LIBDIR");
368  }
369 
370  // if CASM_BOOST_PREFIX exists
371  _env = std::getenv("CASM_BOOST_PREFIX");
372  if(_env != nullptr) {
373  return std::make_pair(fs::path(_env) / "lib", "CASM_BOOST_PREFIX");
374  }
375 
376  // relpath from ccasm
377  fs::path _default = find_libdir("ccasm", "libboost_system");
378  if(!_default.empty()) {
379  return std::make_pair(_default, "relpath");
380  }
381 
382  // else
383  return std::make_pair(fs::path("/not/found"), "notfound");
384  }
385 
386  std::string include_path(const fs::path &dir) {
387  if(!dir.empty()) {
388  return "-I" + dir.string();
389  }
390  return "";
391  };
392 
393  std::string link_path(const fs::path &dir) {
394  if(!dir.empty()) {
395  return "-L" + dir.string();
396  }
397  return "";
398  };
399 
400 }
RuntimeLibrary(std::string _filename_base, std::string _compile_options, std::string _so_options, std::string compile_msg, const Logging &logging=Logging())
Construct a RuntimeLibrary object, with the options to be used for compile the '.o' file and the '...
static std::pair< std::string, std::string > default_cxxflags()
Default c++ compiler options.
void _load()
Load a library with a given name.
static std::pair< fs::path, std::string > default_casm_includedir()
Return default includedir for CASM.
std::string include_path(const fs::path &dir)
std::string gets() const
Returns the stdout resulting from the last popen call.
Definition: Popen.cc:44
void begin_lap()
Definition: Log.cc:28
Definition: Common.hh:7
std::string link_path(const fs::path &dir)
static std::pair< std::string, std::string > default_cxx()
Return default compiler.
void _close()
Close the current library.
void popen(std::string _command)
Execute popen for a given command.
Definition: Popen.cc:19
std::string m_compile_options
Main CASM namespace.
Definition: complete.cpp:8
Log & log() const
Definition: Log.hh:255
double lap_time() const
Definition: Log.cc:32
Remember how to use popen.
Definition: Popen.hh:12
std::string m_so_options
static const int standard
Definition: Log.hh:15
static std::pair< fs::path, std::string > default_boost_libdir()
Return default libdir for boost.
static std::pair< fs::path, std::string > default_casm_libdir()
Return default libdir for CASM.
static std::pair< std::string, std::string > default_soflags()
Default c++ compiler options.
std::string m_filename_base
void compiling(const std::string &what)
Definition: Log.hh:91
bool check(std::string test, const jsonParser &expected, const jsonParser &calculated, fs::path test_cases_path, bool quiet, double tol=0.0)
Check expected JSON vs calculated JSON using BOOST_CHECK_EQUAL.
int exit_code() const
Returns pclose(fp)/256.
Definition: Popen.cc:55
DirectoryStructure & dir
Definition: settings.cc:102
void _compile()
Compile a shared library.
void rm()
Remove the current library and source code.
static std::pair< fs::path, std::string > default_boost_includedir()
Return default includedir for boost.
Log & err_log() const
Definition: Log.hh:263