CASM  1.1.0
A Clusters Approach to Statistical Mechanics
RuntimeLibrary.cc
Go to the documentation of this file.
2 
3 #include <boost/algorithm/string.hpp>
4 #include <boost/filesystem.hpp>
5 #include <vector>
6 
7 #include "casm/system/Popen.hh"
8 
9 namespace CASM {
10 
12  std::string _cmd,
13  std::string _result,
14  std::string _what)
15  : std::runtime_error(_what),
16  filename_base(_filename_base),
17  cmd(_cmd),
18  result(_result) {}
19 
20 void runtime_lib_compile_error::print(std::ostream &sout) const {
21  sout << "Error compiling: " << filename_base + ".cc" << std::endl;
22  sout << "Attempted: " << cmd << std::endl;
23  sout << result << std::endl;
24  sout << what() << std::endl;
25 }
26 
28  std::string _cmd,
29  std::string _result,
30  std::string _what)
31  : std::runtime_error(_what),
32  filename_base(_filename_base),
33  cmd(_cmd),
34  result(_result) {}
35 
36 void runtime_lib_shared_error::print(std::ostream &sout) const {
37  sout << "Error compiling shared object: " << filename_base + ".so"
38  << std::endl;
39  sout << "Attempted: " << cmd << std::endl;
40  sout << result << std::endl;
41  sout << what() << std::endl;
42 }
43 
47 RuntimeLibrary::RuntimeLibrary(std::string filename_base,
48  std::string compile_options,
49  std::string so_options)
50  : m_filename_base(filename_base),
51  m_compile_options(compile_options),
52  m_so_options(so_options),
53  m_handle(nullptr) {
54  // If the shared library doesn't exist
55  if (!fs::exists(m_filename_base + ".so")) {
56  // But the library source code does
57  if (fs::exists(m_filename_base + ".cc")) {
58  // Compile it
59  _compile();
60 
61  } else {
62  throw std::runtime_error(std::string("Error in RuntimeLibrary\n") +
63  " Could not find '" + m_filename_base +
64  ".so' or '" + m_filename_base + ".cc'");
65  }
66  }
67 
68  // If the shared library exists
69  if (fs::exists(m_filename_base + ".so")) {
70  // Load the library with the Clexulator
71  _load();
72 
73  } else {
74  throw std::runtime_error(std::string("Error in Clexulator constructor\n") +
75  " Did not find '" + m_filename_base + ".so'");
76  }
77 }
78 
80  if (m_handle != nullptr) {
81  _close();
82  }
83 }
84 
101  // compile the source code into a dynamic library
102  Popen p;
103  std::string cmd = m_compile_options + " -o " + m_filename_base + ".o" +
104  " -c " + m_filename_base + ".cc";
105  p.popen(cmd);
106  if (p.exit_code()) {
108  m_filename_base, cmd, p.gets(),
109  "Can not compile " + m_filename_base + ".cc");
110  }
111 
112  cmd = m_so_options + " -o " + m_filename_base + ".so" + " " +
113  m_filename_base + ".o";
114  p.popen(cmd);
115  if (p.exit_code()) {
117  m_filename_base, cmd, p.gets(),
118  "Can not compile " + m_filename_base + ".cc");
119  }
120 }
121 
127  m_handle = dlopen((m_filename_base + ".so").c_str(), RTLD_NOW);
128  if (!m_handle) {
129  fprintf(stderr, "dlopen failed: %s\n", dlerror());
130  throw std::runtime_error(std::string("Cannot open library: ") +
131  m_filename_base + ".so");
132  }
133 }
134 
139  // close
140  if (m_handle != nullptr) {
141  dlclose(m_handle);
142  m_handle = nullptr;
143  }
144 }
145 
148  _close();
149  // rm
150  Popen p;
151  p.popen(std::string("rm -f ") + m_filename_base + ".cc " + m_filename_base +
152  ".o " + m_filename_base + ".so");
153 }
154 
155 namespace {
156 
157 std::vector<std::string> _cxx_env() {
158  return std::vector<std::string>{"CASM_CXX", "CXX"};
159 }
160 
161 std::vector<std::string> _cxxflags_env() {
162  return std::vector<std::string>{"CASM_CXXFLAGS"};
163 }
164 
165 std::vector<std::string> _soflags_env() {
166  return std::vector<std::string>{"CASM_SOFLAGS"};
167 }
168 
169 // std::vector<std::string> _casm_env() {
170 // return std::vector<std::string> {
171 // "CASM_PREFIX"
172 // };
173 // }
174 //
175 // std::vector<std::string> _boost_env() {
176 // return std::vector<std::string> {
177 // "CASM_BOOST_PREFIX"
178 // };
179 // }
180 
182 std::pair<std::string, std::string> _use_env(std::vector<std::string> var,
183  std::string _default = "") {
184  for (const auto &v : var) {
185  char *_env = std::getenv(v.c_str());
186  if (_env != nullptr) {
187  return std::make_pair(std::string(_env), v);
188  }
189  }
190  return std::make_pair(_default, "default");
191 }
192 
193 fs::path find_executable(std::string name) {
194  char *_env = std::getenv("PATH");
195  std::vector<std::string> splt;
196  boost::split(splt, _env, boost::is_any_of(":"), boost::token_compress_on);
197 
198  for (const auto &p : splt) {
199  fs::path test{fs::path(p) / name};
200  if (fs::exists(test)) {
201  return test;
202  }
203  }
204  return fs::path();
205 }
206 
207 fs::path find_include(std::string executable_name, std::string include_name) {
208  fs::path loc = find_executable(executable_name);
209  if (loc.empty()) {
210  return loc;
211  }
212  fs::path maybe_includedir = loc.parent_path().parent_path() / "include";
213  if (fs::exists(maybe_includedir / include_name)) {
214  return maybe_includedir / include_name;
215  }
216  return fs::path();
217 }
218 
219 fs::path find_includedir(std::string executable_name,
220  std::string include_name) {
221  return find_include(executable_name, include_name).parent_path();
222 }
223 
224 fs::path find_lib(std::string executable_name, std::string lib_name) {
225  fs::path loc = find_executable(executable_name);
226  if (loc.empty()) {
227  return loc;
228  }
229  fs::path maybe_prefix = loc.parent_path().parent_path();
230 
231  auto check_dir = [&](fs::path test_libdir) {
232  std::vector<std::string> check{"dylib", "so"};
233  for (const auto &s : check) {
234  auto res = test_libdir / (lib_name + "." + s);
235  if (fs::exists(res)) {
236  return res;
237  }
238  }
239  return fs::path();
240  };
241 
242  auto check_names = [&](fs::path test_prefix) {
243  std::vector<fs::path> check{"lib", "lib64", "lib/x86_64-linux-gnu"};
244  for (const auto &s : check) {
245  auto res = check_dir(test_prefix / s);
246  if (!res.empty()) {
247  return res;
248  }
249  }
250  return fs::path();
251  };
252 
253  return check_names(maybe_prefix);
254 }
255 
256 fs::path find_libdir(std::string executable_name, std::string lib_name) {
257  return find_lib(executable_name, lib_name).parent_path();
258 }
259 
260 } // namespace
261 
267 std::pair<std::string, std::string> RuntimeLibrary::default_cxx() {
268  return _use_env(_cxx_env(), "g++");
269 }
270 
274 std::pair<std::string, std::string> RuntimeLibrary::default_cxxflags() {
275  return _use_env(_cxxflags_env(), "-O3 -Wall -fPIC --std=c++17");
276 }
277 
281 std::pair<std::string, std::string> RuntimeLibrary::default_soflags() {
282  return _use_env(_soflags_env(), "-shared -lboost_system");
283 }
284 
289 std::pair<fs::path, std::string> RuntimeLibrary::default_casm_includedir() {
290  char *_env;
291 
292  // if CASM_INCLUDEDIR exists
293  _env = std::getenv("CASM_INCLUDEDIR");
294  if (_env != nullptr) {
295  return std::make_pair(std::string(_env), "CASM_INCLUDEDIR");
296  }
297 
298  // if CASM_PREFIX exists
299  _env = std::getenv("CASM_PREFIX");
300  if (_env != nullptr) {
301  return std::make_pair(fs::path(_env) / "include", "CASM_PREFIX");
302  }
303 
304  // relpath from ccasm
305  fs::path _default = find_includedir("ccasm", "casm");
306  if (!_default.empty()) {
307  return std::make_pair(_default, "relpath");
308  }
309 
310  // else
311  return std::make_pair(fs::path("/not/found"), "notfound");
312 }
313 
318 std::pair<fs::path, std::string> RuntimeLibrary::default_casm_libdir() {
319  char *_env;
320 
321  // if CASM_INCLUDEDIR exists
322  _env = std::getenv("CASM_LIBDIR");
323  if (_env != nullptr) {
324  return std::make_pair(std::string(_env), "CASM_LIBDIR");
325  }
326 
327  // if CASM_PREFIX exists
328  _env = std::getenv("CASM_PREFIX");
329  if (_env != nullptr) {
330  return std::make_pair(fs::path(_env) / "lib", "CASM_PREFIX");
331  }
332 
333  // relpath from ccasm
334  fs::path _default = find_libdir("ccasm", "libcasm");
335  if (!_default.empty()) {
336  return std::make_pair(_default, "relpath");
337  }
338 
339  // else
340  return std::make_pair(fs::path("/not/found"), "notfound");
341 }
342 
347 std::pair<fs::path, std::string> RuntimeLibrary::default_boost_includedir() {
348  char *_env;
349 
350  // if CASM_BOOST_INCLUDEDIR exists
351  _env = std::getenv("CASM_BOOST_INCLUDEDIR");
352  if (_env != nullptr) {
353  return std::make_pair(std::string(_env), "CASM_BOOST_INCLUDEDIR");
354  }
355 
356  // if CASM_BOOST_PREFIX exists
357  _env = std::getenv("CASM_BOOST_PREFIX");
358  if (_env != nullptr) {
359  return std::make_pair(fs::path(_env) / "include", "CASM_BOOST_PREFIX");
360  }
361 
362  // relpath from ccasm
363  fs::path _default = find_includedir("ccasm", "boost");
364  if (!_default.empty()) {
365  return std::make_pair(_default, "relpath");
366  }
367 
368  // else
369  return std::make_pair(fs::path("/not/found"), "notfound");
370 }
371 
376 std::pair<fs::path, std::string> RuntimeLibrary::default_boost_libdir() {
377  char *_env;
378 
379  // if CASM_BOOST_INCLUDEDIR exists
380  _env = std::getenv("CASM_BOOST_LIBDIR");
381  if (_env != nullptr) {
382  return std::make_pair(std::string(_env), "CASM_BOOST_LIBDIR");
383  }
384 
385  // if CASM_BOOST_PREFIX exists
386  _env = std::getenv("CASM_BOOST_PREFIX");
387  if (_env != nullptr) {
388  return std::make_pair(fs::path(_env) / "lib", "CASM_BOOST_PREFIX");
389  }
390 
391  // relpath from ccasm
392  fs::path _default = find_libdir("ccasm", "libboost_system");
393  if (!_default.empty()) {
394  return std::make_pair(_default, "relpath");
395  }
396 
397  // else
398  return std::make_pair(fs::path("/not/found"), "notfound");
399 }
400 
401 std::string include_path(const fs::path &dir) {
402  if (!dir.empty()) {
403  return "-I" + dir.string();
404  }
405  return "";
406 };
407 
408 std::string link_path(const fs::path &dir) {
409  if (!dir.empty()) {
410  return "-L" + dir.string();
411  }
412  return "";
413 };
414 
415 } // namespace CASM
std::set< std::string > & s
Remember how to use popen.
Definition: Popen.hh:13
std::string gets() const
Returns the stdout resulting from the last popen call.
Definition: Popen.cc:43
void popen(std::string _command)
Execute popen for a given command.
Definition: Popen.cc:19
int exit_code() const
Returns pclose(fp)/256.
Definition: Popen.cc:52
std::string m_compile_options
static std::pair< std::string, std::string > default_soflags()
Default c++ compiler options.
static std::pair< fs::path, std::string > default_boost_includedir()
Return default includedir for boost.
static std::pair< std::string, std::string > default_cxxflags()
Default c++ compiler options.
void _close()
Close the current library.
RuntimeLibrary(std::string _filename_base, std::string _compile_options, std::string _so_options)
Construct a RuntimeLibrary object, with the options to be used for compile the '.o' file and the '....
static std::pair< fs::path, std::string > default_casm_includedir()
Return default includedir for CASM.
static std::pair< fs::path, std::string > default_boost_libdir()
Return default libdir for boost.
void rm()
Remove the current library and source code.
static std::pair< std::string, std::string > default_cxx()
Return default compiler.
void _load()
Load a library with a given name.
void _compile()
Compile a shared library.
static std::pair< fs::path, std::string > default_casm_libdir()
Return default libdir for CASM.
std::string m_filename_base
void print(std::ostream &sout) const
runtime_lib_compile_error(std::string _filename_base, std::string _cmd, std::string _result, std::string _what)
void print(std::ostream &sout) const
runtime_lib_shared_error(std::string _filename_base, std::string _cmd, std::string _result, std::string _what)
bool check(const Lattice &lat)
Main CASM namespace.
Definition: APICommand.hh:8
std::string include_path(const fs::path &dir)
GenericDatumFormatter< std::string, DataObject > name()
std::string link_path(const fs::path &dir)
Definition: stream_io.hh:24
DirectoryStructure const & dir
Definition: settings.cc:136