Vcsn  2.4
Be Rational
translate.cc
Go to the documentation of this file.
2 
3 #include <chrono>
4 #include <fstream>
5 #include <memory>
6 #include <set>
7 #include <sstream>
8 #include <string>
9 #include <unistd.h> // getpid
10 
11 #include <boost/filesystem.hpp>
12 
13 #include <ltdl.h>
14 
16 #include <lib/vcsn/dyn/type-ast.hh>
18 
19 #include <vcsn/config.hh>
20 #include <vcsn/dyn/context.hh>
21 #include <vcsn/misc/escape.hh>
22 #include <vcsn/misc/indent.hh>
23 #include <vcsn/misc/raise.hh>
24 #include <vcsn/misc/regex.hh>
25 #include <vcsn/misc/signature.hh>
26 #include <vcsn/misc/stream.hh>
27 
28 namespace vcsn
29 {
30  namespace dyn
31  {
32 
33  jit_error::jit_error(const std::string& a, const std::string& what)
34  : std::runtime_error(what)
35  , assertions(a)
36  {}
37 
38  namespace
39  {
41  std::string
42  xgetenv(const std::string& var, const std::string& val = "")
43  {
44  const char* cp = getenv(var.c_str());
45  return cp ? cp : val;
46  }
47 
48 #define XGETENV(Name) xgetenv(#Name, Name)
49 
51  // http://stackoverflow.com/questions/4891006.
52  std::string expand_tilda(std::string res)
53  {
54  if (!res.empty() && res[0] == '~')
55  {
56  assert(res.size() == 1 || res[1] == '/');
57  auto home = xgetenv("HOME", xgetenv("USERPROFILE"));
58  char const *hdrive = getenv("HOMEDRIVE");
59  char const *hres = getenv("HOMERES");
60  if (!home.empty())
61  res.replace(0, 1, home);
62  else if (hdrive && hres)
63  res.replace(0, 1, std::string(hdrive) + hres);
64  else
65  res.replace(0, 1, xgetenv("VCSN_TMPDIR", "/tmp"));
66  }
67  return res;
68  }
69 
71  void ensure_parent_directory(const std::string& path)
72  {
73  boost::filesystem::path p(path);
74  boost::filesystem::create_directories(p.parent_path());
75  }
76 
78  std::string tmpname(std::string res)
79  {
80  res += ".";
81  res += std::to_string(getpid());
82  return res;
83  }
84 
85  struct translation
86  {
87  translation()
88  : printer_(os)
89  {}
90 
98  void print(const std::string& base)
99  {
100  ensure_parent_directory(base);
101  // For atomicity, generate a file with PID, then mv it
102  // (which is atomic on any decent OS/FS).
103  auto tmp = tmpname(base);
104  {
105  std::ofstream o{tmp + ".cc"};
106  printer_.print(o);
107  }
108  boost::filesystem::rename(tmp + ".cc", base + ".cc");
109  }
110 
113  void print_context(const std::string& ctx)
114  {
115  ast::parse_context(ctx)->accept(printer_);
116  }
117 
119  void print_type(const std::string& type)
120  {
121  ast::parse_type(type)->accept(printer_);
122  }
123 
127  void throw_compiler_errors(std::string cmd,
128  const std::string& err)
129  {
130  // Try to find assertion failures in the error log.
131  //
132  // $ g++-mp-4.9 -std=c++11 main.cc
133  // main.cc: In function 'int main()':
134  // main.cc:3:3: error: static assertion failed: foo
135  // static_assert(0, "foo");
136  // ^
137  // $ clang++-mp-3.5 -std=c++11 main.cc
138  // main.cc:3:3: error: static_assert failed "foo"
139  // static_assert(0, "foo");
140  // ^ ~
141  // 1 error generated.
142  //
143  // We don't try to catch the "error:" part, because
144  // vcsn-compile loves to add colors, so "error:" is actually
145  // cluttered with ANSI escapes for colors.
146  auto is = open_input_file(err);
147  static auto r1 = std::regex{"static assertion failed: (.*)$",
148  std::regex::extended};
149  static auto r2 = std::regex{"static_assert failed \"(.*)\"$",
150  std::regex::extended};
151  std::string line;
152  std::smatch smatch;
153  std::string assertions;
154  while (std::getline(*is, line))
155  if (std::regex_search(line, smatch, r1)
156  || std::regex_search(line, smatch, r2))
157  assertions += std::string(smatch[1]) + '\n';
158  if (getenv("VCSN_VERBOSE"))
159  {
160  cmd += "\n compiler error messages:\n";
161  cmd += get_file_contents(err);
162  }
163  throw jit_error(assertions, " failed command:\n " + cmd);
164  }
165 
170  void cxx(std::string cmd, const std::string& tmp)
171  {
172  auto err = tmp + ".err";
173  if (getenv("VCSN_DEBUG"))
174  std::cerr << "run: " << cmd << std::endl;
175  if (system((cmd + " 2>'" + err + "'").c_str()))
176  throw_compiler_errors(cmd, err);
177  else
178  {
179  // At least we should see the warnings.
180  std::ifstream log{err};
181  std::cerr << log.rdbuf();
182  // If the file is empty the previous instruction sets the state
183  // of cerr to bad. We clear the error state flag to be able to
184  // read from cerr afterwards.
185  std::cerr.clear();
186  boost::filesystem::remove(err);
187  }
188  }
189 
193  void cxx_compile(const std::string& base)
194  {
195  auto tmp = tmpname(base);
196  // We try to read the error message via a regexp below. So
197  // avoid translation (we once had "erreur" instead of "error").
198  auto cmd = (std::string{"LC_ALL=C"}
199  + " " + XGETENV(VCSN_CCACHE)
200  + " " + XGETENV(VCSN_CXX)
201  + " " + XGETENV(VCSN_CXXFLAGS)
202  + " " + XGETENV(VCSN_CPPFLAGS)
203  + " -fPIC '" + base + ".cc' -c"
204  + " -o '" + tmp + ".o'");
205  cxx(cmd, tmp);
206  }
207 
211  void cxx_link(const std::string& base)
212  {
213  auto tmp = tmpname(base);
214  auto cmd = (std::string{"LC_ALL=C"}
215  + " " + XGETENV(VCSN_CXX)
216  + " " + XGETENV(VCSN_CXXFLAGS)
217  + " " + XGETENV(VCSN_LDFLAGS)
218  + " -fPIC -lvcsn '" + tmp + ".o' -shared"
219  + " -o '" + tmp + ".so'"
220  + printer_.linkflags());
221  cxx(cmd, tmp);
222  }
223 
225  std::string plugindir() const
226  {
227  auto res = xgetenv("VCSN_PLUGINDIR",
228  xgetenv("VCSN_HOME", "~/.vcsn") + "/plugins");
229  res = expand_tilda(res);
230  return res + "/";
231  }
232 
235  std::string split(const std::string& s) const
236  {
237  std::string res;
238  const size_t size = 150;
239  for (unsigned i = 0; i < s.length(); i += size)
240  {
241  if (i)
242  res += '/';
243  res += s.substr(i, size);
244  }
245  return res;
246  }
247 
256  void jit(const std::string& base)
257  {
258  auto tmp = tmpname(base);
259  {
260  namespace chr = std::chrono;
261  using clock = chr::steady_clock;
262  auto start = clock::now();
263  static bool no_python = !!getenv("VCSN_NO_PYTHON");
264  if (no_python)
265  {
266  cxx_compile(base);
267  cxx_link(base);
268  boost::filesystem::rename(tmp + ".so", base + ".so");
269  // Upon success, remove the .o file, it is large (10x
270  // compared to the *.so on erebus using clang) and not
271  // required. However the debug symbols are in there, so
272  // when debugging, leave them!
273  if (!getenv("VCSN_DEBUG"))
274  boost::filesystem::remove(tmp + ".o");
275  }
276  else
277  {
278  auto linkflags = printer_.linkflags();
279  if (!linkflags.empty())
280  linkflags = " LDFLAGS+='" + linkflags + "'";
281  cxx("vcsn compile -shared" + linkflags + " '" + base + ".cc'",
282  tmp);
283  }
284  auto d
285  = chr::duration_cast<chr::milliseconds>(clock::now() - start);
286  if (getenv("VCSN_TIME"))
287  {
288  std::ofstream{"/tmp/vcsn-compile.log",
289  std::ofstream::out | std::ofstream::app}
290  << d.count() << ", "
291  << (no_python ? "C++, " : "Py, ")
292  << '\'' << base.substr(plugindir().size()) << '\''
293  << '\n';
294  if (getenv("VCSN_TIME2"))
295  std::cerr << d.count() << "ms: " << base << '\n';
296  }
297  }
298  static bool first = true;
299  if (first)
300  {
301  lt_dlinit();
302  first = false;
303  }
304  lt_dlhandle lib = lt_dlopen((base + ".so").c_str());
305  VCSN_REQUIRE(lib, "cannot load lib: ", base, ".so: ", lt_dlerror());
306  }
307 
309  void compile(const std::string& ctx)
310  {
311  printer_.header("vcsn/ctx/instantiate.hh");
312  std::string base =
313  plugindir() + "contexts/" + split(ctx);
314  os << "using ctx_t =" << incendl;
315  print_context(ctx);
316  os << ';' << decendl
317  <<
318  "\n"
319  "namespace vcsn\n"
320  "{\n"
321  " VCSN_CTX_INSTANTIATE(ctx_t);\n"
322  "}\n";
323  ;
324  print(base);
325  jit(base);
326  }
327 
329  void compile(const std::set<std::pair<std::string, signature>>& algos)
330  {
331  printer_.header("vcsn/misc/attributes.hh"); // ATTRIBUTE_USED
332  printer_.header("vcsn/dyn/name.hh"); // ssignature
333  printer_.header("vcsn/dyn/registries.hh");
334  for (const auto& algo: algos)
335  printer_.header_algo(algo.first);
336 
337  unsigned count = 0;
338  for (const auto& algo: algos)
339  {
340  os << iendl
341  << "// " << algo.first << '.';
342  std::string types;
343  bool first = true;
344  for (const auto& s: algo.second)
345  {
346  os << iendl;
347  std::string t = "t" + std::to_string(count) + "_t";
348  os << "using " << t << " =" << incendl;
349  print_type(s);
350  os << ';' << decendl;
351  types += (first ? "" : ", ") + t;
352  ++count;
353  first = false;
354  }
355  os <<
356  "\n"
357  "static bool vcsn_" << algo.first << " ATTRIBUTE_USED =" << incendl
358  << "vcsn::dyn::detail::" << algo.first << "_register(" << incendl
359  << "vcsn::ssignature<" << types << ">()," << iendl
360  << "vcsn::dyn::detail::" << algo.first << "<" << types << ">" << decendl
361  << ");" << decendl;
362  }
363 
364  // The first algo is the once that gives its name to the
365  // file to compile.
366  std::string base = (plugindir()
367  + "algos/"
368  + begin(algos)->first + "/"
369  + split(begin(algos)->second.to_string()));
370  print(base);
371  jit(base);
372  }
373 
375  std::ostringstream os;
376  ast::context_printer printer_;
377  };
378  } // namespace detail
379 
380  void compile(const std::string& ctx)
381  {
382  translation translate;
383  translate.compile(ctx);
384  }
385 
386  void compile(const std::string& algo, const signature& sig)
387  {
388  translation translate;
389  std::set<std::pair<std::string, signature>> algos{{algo, sig}};
390  if (algo == "delay_automaton"
391  || algo == "is_synchronized")
392  {
393  algos.emplace("delay_automaton", sig);
394  algos.emplace("is_synchronized", sig);
395  }
396  translate.compile(algos);
397  }
398  } // namespace dyn
399 } // namespace vcsn
STL namespace.
std::ostream & print(const automaton &aut, std::ostream &out=std::cout, const std::string &format="default")
Print automaton a on out using format format.
Definition: print.hh:121
return res
Definition: multiply.hh:398
std::string get_file_contents(const std::string &file)
Return the contents of file.
Definition: stream.cc:119
std::ostream & decendl(std::ostream &o)
Decrement the indentation, print an end of line, and set the indentation.
Definition: indent.cc:59
polynomial split(const expression &exp)
Break exp.
Definition: split.hh:275
#define XGETENV(Name)
Definition: translate.cc:48
void compile(const std::string &ctx)
Compile, and load, a DSO with instantiations for ctx.
Definition: translate.cc:380
std::shared_ptr< std::istream > open_input_file(const std::string &file)
Open file for reading and return its autoclosing stream.
Definition: stream.cc:134
std::string type(const automaton &a)
The implementation type of a.
Definition: others.cc:239
std::ostringstream os
The output stream: the corresponding C++ snippet to compile.
Definition: translate.cc:375
Definition: a-star.hh:8
auto out(const Aut &aut, state_t_of< Aut > s)
Indexes of visible transitions leaving state s.
Definition: automaton.hh:85
size_t size(const ExpSet &rs, const typename ExpSet::value_t &r)
#define VCSN_REQUIRE(Cond,...)
A macro similar to require.
Definition: raise.hh:111
std::shared_ptr< ast_node > parse_context(const std::string &ctx)
Parse a context, and return its AST.
std::shared_ptr< ast_node > parse_type(const std::string &type)
Parse a type, and return its AST.
std::ostream & print_context(const context &ctx, std::ostream &o, const std::string &fmt)
Bridge (print).
Definition: print.hh:140
std::ostream & iendl(std::ostream &o)
Print an end of line, then set the indentation.
Definition: indent.cc:49
weightset_mixin< detail::log_impl > log
Definition: fwd.hh:50
ast::context_printer printer_
Definition: translate.cc:376
std::ostream & incendl(std::ostream &o)
Increment the indentation, print an end of line, and set the indentation.
Definition: indent.cc:54
Indentation relative functions.
jit_error(const std::string &assert, const std::string &what)
Definition: translate.cc:33
std::string to_string(identities i)
Wrapper around operator<<.
Definition: identities.cc:42
Request the set implementation (bool weights).
Signature of a function call.
Definition: signature.hh:15