Vcsn  2.5
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/algorithm/string/case_conv.hpp>
12 #include <boost/filesystem.hpp>
13 
16 #include <lib/vcsn/dyn/type-ast.hh>
17 #include <lib/vcsn/misc/xltdl.hh>
18 
19 #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  {
40 #define XGETENV(Name) xgetenv(#Name, Name)
41 
44  std::string
45  config(const std::string& var)
46  {
47  auto envvar = "VCSN_" + boost::algorithm::to_upper_copy(var);
48  if (auto cp = getenv(envvar.c_str()))
49  return cp;
50  else
51  return get_config()["configuration"][var].str();
52  }
53 
55  // http://stackoverflow.com/questions/4891006.
56  std::string expand_tilda(std::string res)
57  {
58  if (!res.empty() && res[0] == '~')
59  {
60  assert(res.size() == 1 || res[1] == '/');
61  auto home = xgetenv("HOME", xgetenv("USERPROFILE"));
62  char const *hdrive = getenv("HOMEDRIVE");
63  char const *hres = getenv("HOMERES");
64  if (!home.empty())
65  res.replace(0, 1, home);
66  else if (hdrive && hres)
67  res.replace(0, 1, std::string(hdrive) + hres);
68  else
69  res.replace(0, 1, xgetenv("VCSN_TMPDIR", "/tmp"));
70  }
71  return res;
72  }
73 
75  void ensure_parent_directory(const std::string& path)
76  {
77  boost::filesystem::path p(path);
78  boost::filesystem::create_directories(p.parent_path());
79  }
80 
82  std::string tmpname(std::string res)
83  {
84  res += ".";
85  res += std::to_string(getpid());
86  return res;
87  }
88 
89  struct translation
90  {
91  translation()
92  : printer_(os)
93  {}
94 
105  void print(const std::string& base)
106  {
107  ensure_parent_directory(base);
108  // For atomicity, generate a file with PID, then mv it
109  // (which is atomic on any decent OS/FS).
110  auto tmp = tmpname(base);
111  {
112  std::ofstream o{tmp + ".cc"};
113  printer_.print(o);
114  }
115  if (equal_files(tmp + ".cc", base + ".cc"))
116  boost::filesystem::remove(tmp + ".cc");
117  else
118  boost::filesystem::rename(tmp + ".cc", base + ".cc");
119  }
120 
123  void print_context(const std::string& ctx)
124  {
125  ast::parse_context(ctx)->accept(printer_);
126  }
127 
129  void print_type(const std::string& type)
130  {
131  ast::parse_type(type)->accept(printer_);
132  }
133 
137  void throw_compiler_errors(std::string cmd,
138  const std::string& err)
139  {
140  // Try to find assertion failures in the error log.
141  //
142  // $ g++-mp-4.9 -std=c++11 main.cc
143  // main.cc: In function 'int main()':
144  // main.cc:3:3: error: static assertion failed: foo
145  // static_assert(0, "foo");
146  // ^
147  // $ clang++-mp-3.5 -std=c++11 main.cc
148  // main.cc:3:3: error: static_assert failed "foo"
149  // static_assert(0, "foo");
150  // ^ ~
151  // 1 error generated.
152  //
153  // We don't try to catch the "error:" part, because
154  // vcsn-compile loves to add colors, so "error:" is actually
155  // cluttered with ANSI escapes for colors.
156  auto is = open_input_file(err);
157  static auto r1 = std::regex{"static assertion failed: (.*)$",
158  std::regex::extended};
159  static auto r2 = std::regex{"static_assert failed \"(.*)\"$",
160  std::regex::extended};
161  std::string line;
162  std::smatch smatch;
163  std::string assertions;
164  while (std::getline(*is, line))
165  if (std::regex_search(line, smatch, r1)
166  || std::regex_search(line, smatch, r2))
167  assertions += std::string(smatch[1]) + '\n';
168  if (getenv("VCSN_VERBOSE"))
169  {
170  cmd += "\n compiler error messages:\n";
171  cmd += get_file_contents(err);
172  }
173  throw jit_error(assertions, " failed command:\n " + cmd);
174  }
175 
180  void cxx(std::string cmd, const std::string& tmp)
181  {
182  auto err = tmp + ".err";
183  if (getenv("VCSN_DEBUG"))
184  std::cerr << "run: " << cmd << '\n';
185  if (system((cmd + " 2>'" + err + "'").c_str()))
186  throw_compiler_errors(cmd, err);
187  else
188  {
189  // At least we should see the warnings.
190  std::ifstream log{err};
191  std::cerr << log.rdbuf();
192  // If the file is empty the previous instruction sets the state
193  // of cerr to bad. We clear the error state flag to be able to
194  // read from cerr afterwards.
195  std::cerr.clear();
196  boost::filesystem::remove(err);
197  }
198  }
199 
203  void cxx_compile(const std::string& base)
204  {
205  auto tmp = tmpname(base);
206  // We try to read the error message via a regexp below. So
207  // avoid translation (we once had "erreur" instead of "error").
208  auto cmd = (std::string{"LC_ALL=C"}
209  + " " + config("ccache")
210  + " " + config("cxx")
211  + " " + config("cxxflags")
212  + " " + config("cppflags")
213  + " -fPIC '" + base + ".cc' -c"
214  + " -o '" + tmp + ".o'");
215  cxx(cmd, tmp);
216  }
217 
221  void cxx_link(const std::string& base)
222  {
223  auto tmp = tmpname(base);
224  auto cmd = (std::string{"LC_ALL=C"}
225  + " " + config("cxx")
226  + " " + config("cxxflags")
227  + " " + config("ldflags")
228  + " -fPIC -lvcsn '" + tmp + ".o' -shared"
229  + " -o '" + tmp + ".so'"
230  + printer_.linkflags());
231  cxx(cmd, tmp);
232  }
233 
235  std::string plugindir() const
236  {
237  auto res = xgetenv("VCSN_PLUGINDIR",
238  xgetenv("VCSN_HOME", "~/.vcsn") + "/plugins");
239  res = expand_tilda(res);
240  return res + "/";
241  }
242 
245  std::string split(const std::string& s) const
246  {
247  auto res = std::string{};
248  const size_t size = 150;
249  for (unsigned i = 0; i < s.length(); i += size)
250  {
251  if (i)
252  res += '/';
253  res += s.substr(i, size);
254  }
255  return res;
256  }
257 
266  void jit(const std::string& base)
267  {
268  auto tmp = tmpname(base);
269  {
270  namespace chr = std::chrono;
271  using clock = chr::steady_clock;
272  auto start = clock::now();
273  static bool no_python = !!getenv("VCSN_NO_PYTHON");
274  if (no_python)
275  {
276  cxx_compile(base);
277  cxx_link(base);
278  boost::filesystem::rename(tmp + ".so", base + ".so");
279  // Upon success, remove the .o file, it is large (10x
280  // compared to the *.so on erebus using clang) and not
281  // required. However the debug symbols are in there, so
282  // when debugging, leave them!
283  if (!getenv("VCSN_DEBUG"))
284  boost::filesystem::remove(tmp + ".o");
285  }
286  else
287  {
288  auto linkflags = printer_.linkflags();
289  if (!linkflags.empty())
290  linkflags = " LDFLAGS+='" + linkflags + "'";
291  cxx("vcsn compile -shared" + linkflags + " '" + base + ".cc'",
292  tmp);
293  }
294  auto d
295  = chr::duration_cast<chr::milliseconds>(clock::now() - start);
296  if (getenv("VCSN_TIME"))
297  {
298  std::ofstream{"/tmp/vcsn-compile.log",
299  std::ofstream::out | std::ofstream::app}
300  << d.count() << ", "
301  << (no_python ? "C++, " : "Py, ")
302  << '\'' << base.substr(plugindir().size()) << '\''
303  << '\n';
304  if (getenv("VCSN_TIME2"))
305  std::cerr << d.count() << "ms: " << base << '\n';
306  }
307  }
308  vcsn::detail::xlt_openext(base + ".so", true);
309  }
310 
312  void compile(const std::string& ctx)
313  {
314  printer_.header("vcsn/ctx/instantiate.hh");
315  auto base = plugindir() + "contexts/" + split(ctx);
316  os << "using ctx_t =" << incendl;
317  print_context(ctx);
318  os << ';' << decendl
319  <<
320  "\n"
321  "namespace vcsn\n"
322  "{\n"
323  " VCSN_CTX_INSTANTIATE(ctx_t);\n"
324  "}\n";
325  ;
326  print(base);
327  jit(base);
328  }
329 
331  void compile(const std::set<std::pair<std::string, signature>>& algos)
332  {
333  printer_.header("vcsn/misc/attributes.hh"); // ATTRIBUTE_USED
334  printer_.header("vcsn/dyn/name.hh"); // ssignature
335  printer_.header("vcsn/dyn/registries.hh");
336  for (const auto& algo: algos)
337  printer_.header_algo(algo.first);
338 
339  unsigned count = 0;
340  for (const auto& algo: algos)
341  {
342  os << iendl
343  << "// " << algo.first << '.';
344  std::string types;
345  bool first = true;
346  for (const auto& s: algo.second)
347  {
348  os << iendl;
349  std::string t = "t" + std::to_string(count) + "_t";
350  os << "using " << t << " =" << incendl;
351  print_type(s);
352  os << ';' << decendl;
353  types += (first ? "" : ", ") + t;
354  ++count;
355  first = false;
356  }
357  os <<
358  "\n"
359  "static bool vcsn_" << algo.first << " ATTRIBUTE_USED ="
360  << incendl
361  << "vcsn::dyn::detail::" << algo.first << "_register("
362  << incendl
363  << "vcsn::ssignature<" << types << ">(),"
364  << iendl
365  << "vcsn::dyn::detail::" << algo.first << "<" << types << ">"
366  << decendl
367  << ");" << decendl;
368  }
369 
370  // The first algo is the once that gives its name to the
371  // file to compile.
372  auto base = (plugindir()
373  + "algos/"
374  + begin(algos)->first + "/"
375  + split(begin(algos)->second.to_string()));
376  print(base);
377  jit(base);
378  }
379 
381  std::ostringstream os;
382  ast::context_printer printer_;
383  };
384  } // namespace detail
385 
386  void compile(const std::string& ctx)
387  {
388  translation translate;
389  translate.compile(ctx);
390  }
391 
392  void compile(const std::string& algo, const signature& sig)
393  {
394  translation translate;
395  std::set<std::pair<std::string, signature>> algos{{algo, sig}};
396  if (algo == "delay_automaton"
397  || algo == "is_synchronized")
398  {
399  algos.emplace("delay_automaton", sig);
400  algos.emplace("is_synchronized", sig);
401  }
402  translate.compile(algos);
403  }
404  } // namespace dyn
405 } // namespace vcsn
std::ostringstream os
The output stream: the corresponding C++ snippet to compile.
Definition: translate.cc:381
Indentation relative functions.
std::string xgetenv(const std::string &var, const std::string &val="")
getenv(var) if defined, otherwise val.
Definition: stream.cc:210
std::shared_ptr< ast_node > parse_type(const std::string &type)
Parse a type, and return its AST.
std::string assertions
If defined, static assertions that failed (ends with a eol).
Definition: translate.hh:17
Definition: a-star.hh:8
weightset_mixin< detail::log_impl > log
Definition: fwd.hh:50
return res
Definition: multiply.hh:398
detail::config & get_config()
Get the configuration singleton.
std::ostream & decendl(std::ostream &o)
Decrement the indentation, print an end of line, and set the indentation.
Definition: indent.cc:59
Signature of a function call.
Definition: signature.hh:15
std::string expand_tilda(const std::string &res)
Expand initial "~" in res.
Definition: stream.cc:58
ast::context_printer printer_
Definition: translate.cc:382
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
STL namespace.
polynomial split(const expression &exp)
Break exp.
void compile(const std::string &ctx)
Compile, and load, a DSO with instantiations for ctx.
Definition: translate.cc:386
bool equal_files(const std::string &fn1, const std::string &fn2)
Whether two files have exactly equal contents.
Definition: stream.cc:39
auto out(const Aut &aut, state_t_of< Aut > s)
Indexes of visible transitions leaving state s.
Definition: automaton.hh:86
std::ostream & print_context(const context &ctx, std::ostream &o, const std::string &fmt)
Bridge (print).
Definition: print.hh:140
std::string get_file_contents(const std::string &file)
Return the contents of file.
Definition: stream.cc:159
xlt_handle xlt_openext(const std::string &s, bool global)
Wrapper around lt_dlopenext.
Definition: xltdl.cc:160
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
std::ostream & iendl(std::ostream &o)
Print an end of line, then set the indentation.
Definition: indent.cc:49
std::string type(const automaton &a)
The implementation type of a.
Definition: others.cc:239
Request the set implementation (bool weights).
std::shared_ptr< ast_node > parse_context(const std::string &ctx)
Parse a context, and return its AST.
size_t size(const ExpSet &rs, const typename ExpSet::value_t &r)
std::ostream & incendl(std::ostream &o)
Increment the indentation, print an end of line, and set the indentation.
Definition: indent.cc:54
std::shared_ptr< std::istream > open_input_file(const std::string &file)
Open file for reading and return its autoclosing stream.
Definition: stream.cc:174