Vcsn  2.0
Be Rational
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros
translate.cc
Go to the documentation of this file.
1 #include <vcsn/dyn/translate.hh>
2 
3 #include <fstream>
4 #include <memory>
5 #include <set>
6 #include <sstream>
7 #include <string>
8 #include <unistd.h> // getpid
9 
10 #include <boost/filesystem.hpp>
11 
12 #include <ltdl.h>
13 
16 
17 #include <vcsn/config.hh>
18 #include <vcsn/dyn/context.hh>
19 #include <vcsn/misc/escape.hh>
20 #include <vcsn/misc/indent.hh>
21 #include <vcsn/misc/raise.hh>
22 #include <vcsn/misc/regex.hh>
23 #include <vcsn/misc/signature.hh>
24 #include <vcsn/misc/stream.hh>
25 
26 namespace vcsn
27 {
28  namespace dyn
29  {
30 
31  jit_error::jit_error(const std::string& a, const std::string& what)
32  : std::runtime_error(what)
33  , assertions(a)
34  {}
35 
36  namespace
37  {
39  std::string
40  xgetenv(const std::string& var, const std::string& val = "")
41  {
42  const char* cp = getenv(var.c_str());
43  return cp ? cp : val;
44  }
45 
47  // http://stackoverflow.com/questions/4891006.
48  std::string expand_tilda(std::string res)
49  {
50  if (!res.empty() && res[0] == '~')
51  {
52  assert(res.size() == 1 || res[1] == '/');
53  auto home = xgetenv("HOME", xgetenv("USERPROFILE"));
54  char const *hdrive = getenv("HOMEDRIVE");
55  char const *hres = getenv("HOMERES");
56  if (!home.empty())
57  res.replace(0, 1, home);
58  else if (hdrive && hres)
59  res.replace(0, 1, std::string(hdrive) + hres);
60  else
61  res.replace(0, 1, xgetenv("VCSN_TMPDIR", "/tmp"));
62  }
63  return res;
64  }
65 
67  void ensure_parent_directory(const std::string& path)
68  {
69  boost::filesystem::path p(path);
70  boost::filesystem::create_directories(p.parent_path());
71  }
72 
74  std::string tmpname(std::string res)
75  {
76  res += ".";
77  res += std::to_string(getpid());
78  return res;
79  }
80 
81  struct translation
82  {
85  std::string word()
86  {
87  std::string res;
88  int c;
89  while ((c = is.get()) != EOF)
90  if (c == '<' || c == ',' || c == '_' || c == '>')
91  {
92  is.unget();
93  break;
94  }
95  else
96  res += c;
97  return res;
98  }
99 
100  translation()
101  : parser_(is), printer_(os)
102  {}
103 
111  void print(const std::string& base)
112  {
113  ensure_parent_directory(base);
114  // For atomicity, generate a file with PID, then mv it
115  // (which is atomic on any decent OS/FS).
116  auto tmp = tmpname(base);
117  {
118  std::ofstream o{tmp + ".cc"};
119  printer_.print(o);
120  }
121  boost::filesystem::rename(tmp + ".cc", base + ".cc");
122  }
123 
126  void print_context(const std::string& ctx)
127  {
128  is.clear();
129  is.str(ctx);
130  auto ast = parser_.parse_context();
131  ast->accept(printer_);
132  }
133 
135  void print_type(const std::string& type)
136  {
137  is.clear();
138  is.str(type);
139  auto ast = parser_.parse();
140  ast->accept(printer_);
141  }
142 
148  void cxx(std::string cmd, const std::string& tmp)
149  {
150  auto err = tmp + ".err";
151  // We try to read the error message via a regexp below. So
152  // avoid translation (we did "erreur" instead of "error").
153  cmd = "LC_ALL=C " + cmd;
154 
155  if (getenv("VCSN_DEBUG"))
156  std::cerr << "run: " << cmd << std::endl;
157  std::string assertions;
158  if (system((cmd + + " 2>'" + err + "'").c_str()))
159  {
160  // Try to find assertion failures in the error log.
161  //
162  // $ g++-mp-4.9 -std=c++11 main.cc
163  // main.cc: In function 'int main()':
164  // main.cc:3:3: error: static assertion failed: foo
165  // static_assert(0, "foo");
166  // ^
167  // $ clang++-mp-3.5 -std=c++11 main.cc
168  // main.cc:3:3: error: static_assert failed "foo"
169  // static_assert(0, "foo");
170  // ^ ~
171  // 1 error generated.
172  auto is = open_input_file(err);
173  static std::regex r1("error: static assertion failed: (.*)$",
174  std::regex::extended);
175  static std::regex r2("error: static_assert failed \"(.*)\"$",
176  std::regex::extended);
177  std::string line;
178  std::smatch smatch;
179  while (std::getline(*is, line))
180  {
181  if (std::regex_search(line, smatch, r1)
182  || std::regex_search(line, smatch, r2))
183  assertions += std::string(smatch[1]) + "\n";
184  }
185  throw jit_error(assertions, " failed command:\n " + cmd);
186  }
187  else if (!getenv("VCSN_DEBUG"))
188  boost::filesystem::remove(err);
189  }
190 
194  void cxx_compile(const std::string& base)
195  {
196  auto tmp = tmpname(base);
197  auto cmd = (xgetenv("VCSN_CCACHE", VCSN_CCACHE)
198  + " " + xgetenv("VCSN_CXX", VCSN_CXX)
199  + " " + xgetenv("VCSN_CXXFLAGS", VCSN_CXXFLAGS)
200  + " " + xgetenv("VCSN_CPPFLAGS", VCSN_CPPFLAGS)
201  + " -fPIC '" + base + ".cc' -c"
202  + " -o '" + tmp + ".o'");
203  cxx(cmd, tmp);
204  }
205 
209  void cxx_link(const std::string& base)
210  {
211  auto tmp = tmpname(base);
212  auto cmd = (xgetenv("VCSN_CXX", VCSN_CXX)
213  + " " + xgetenv("VCSN_CXXFLAGS", VCSN_CXXFLAGS)
214  + " " + xgetenv("VCSN_LDFLAGS", VCSN_LDFLAGS)
215  + " -fPIC -lvcsn '" + tmp + ".o' -shared"
216  + " -o '" + tmp + ".so'"
217  + printer_.linkflags());
218  cxx(cmd, tmp);
219  }
220 
222  std::string plugindir() const
223  {
224  auto res = xgetenv("VCSN_PLUGINDIR",
225  xgetenv("VCSN_HOME", "~/.vcsn") + "/plugins");
226  res = expand_tilda(res);
227  return res + "/";
228  }
229 
232  std::string split(const std::string& s) const
233  {
234  std::string res;
235  const size_t size = 200;
236  for (unsigned i = 0; i < s.length(); i += size)
237  {
238  if (i)
239  res += '/';
240  res += s.substr(i, size);
241  }
242  return res;
243  }
244 
253  void jit(const std::string& base)
254  {
255  cxx_compile(base);
256  cxx_link(base);
257  auto tmp = tmpname(base);
258  boost::filesystem::rename(tmp + ".so", base + ".so");
259  static bool first = true;
260  if (first)
261  {
262  lt_dlinit();
263  first = false;
264  }
265  lt_dlhandle lib = lt_dlopen((base + ".so").c_str());
266  require(lib, "cannot load lib: ", base, ".so: ", lt_dlerror());
267  // Upon success, remove the .o file, it is large (10x
268  // compared to the *.so on erebus using clang) and not
269  // required. However the debug symbols are in there, so
270  // when debugging, leave them!
271  if (!getenv("VCSN_DEBUG"))
272  {
273  boost::filesystem::remove(tmp + ".cc");
274  boost::filesystem::remove(tmp + ".o");
275  }
276  }
277 
279  void compile(const std::string& ctx)
280  {
281  printer_.header("vcsn/ctx/instantiate.hh");
282  std::string base =
283  plugindir() + "contexts/" + split(detail::context_base::sname(ctx));
284  os << "using ctx_t =" << incendl;
285  print_context(ctx);
286  os << ';' << decendl
287  <<
288  "\n"
289  "namespace vcsn\n"
290  "{\n"
291  " VCSN_CTX_INSTANTIATE(ctx_t);\n"
292  "}\n";
293  ;
294  print(base);
295  jit(base);
296  }
297 
299  void compile(const std::string& name, const signature& sig)
300  {
301  printer_.header("vcsn/misc/name.hh"); // ssignature
302  printer_.header_algo(name);
303  std::string base =
304  plugindir() + "algos/" + name + "/" + split(sig.to_string());
305  int count = 0;
306  std::string types;
307  for (const auto& s: sig)
308  {
309  os << iendl;
310  std::string t = "t" + std::to_string(count) + "_t";
311  os << "using " << t << " =" << incendl;
312  print_type(s);
313  os << ';' << decendl;
314  types += (count ? ", " : "") + t;
315  ++count;
316  }
317  os <<
318  "\n"
319  "static bool f =" << incendl
320  << "vcsn::dyn::detail::" << name << "_register(" << incendl
321  << "vcsn::ssignature<" << types << ">()," << iendl
322  << "vcsn::dyn::detail::" << name << "<" << types << ">" << decendl
323  << ");" << decendl;
324  print(base);
325  jit(base);
326  }
327 
329  std::istringstream is;
331  std::ostringstream os;
332  ast::context_parser parser_;
333  ast::context_printer printer_;
334  };
335  } // namespace detail
336 
337  void compile(const std::string& ctx)
338  {
339  translation translate;
340  translate.compile(ctx);
341  }
342 
343  void compile(const std::string& algo, const signature& sig)
344  {
345  translation translate;
346  translate.compile(algo, sig);
347  }
348 
349  } // namespace dyn
350 } // namespace vcsn
std::ostream & iendl(std::ostream &o)
Print an end of line, then set the indentation.
Definition: indent.cc:49
Indentation relative functions.
std::string to_string(identities i)
Definition: identities.cc:13
std::ostream & print(const automaton &a, std::ostream &o, const std::string &format="default")
Print automaton a on o using format format.
Definition: print.cc:21
std::ostream & incendl(std::ostream &o)
Increment the indentation, print an end of line, and set the indentation.
Definition: indent.cc:54
void compile(const std::string &ctx)
Compile, and load, a DSO with instantiations for ctx.
Definition: translate.cc:337
polynomial split(const polynomial &p)
Break all the expressions in p.
Definition: derivation.cc:62
Signature of a function call.
Definition: signature.hh:14
std::ostream & decendl(std::ostream &o)
Decrement the indentation, print an end of line, and set the indentation.
Definition: indent.cc:59
static std::string sname(const std::string &vname)
Convert a dynamic name into a static one.
Definition: ctx.cc:9
std::istringstream is
The input stream: the specification to translate.
Definition: translate.cc:329
jit_error(const std::string &assert, const std::string &what)
Definition: translate.cc:31
std::ostringstream os
The output stream: the corresponding C++ snippet to compile.
Definition: translate.cc:331
std::shared_ptr< std::istream > open_input_file(const std::string &file)
Open file for reading and return its autoclosing stream.
Definition: stream.cc:78
ast::context_printer printer_
Definition: translate.cc:333
ast::context_parser parser_
Definition: translate.cc:332
void require(bool b, Args &&...args)
If b is not verified, raise an error with args as message.
Definition: raise.hh:39