Switch time() calls to monotonic clock calls (#7597)
[libt2n] / codegen / main.cpp
1 /*
2     Copyright (C) 2006-2008 by Intra2net AG
3     intra2net.com
4
5     The software in this package is distributed under the GNU General
6     Public License version 2 (with a special exception described below).
7
8     A copy of GNU General Public License (GPL) is included in this distribution,
9     in the file COPYING.GPL.
10
11     As a special exception, if other files instantiate templates or use macros
12     or inline functions from this file, or you compile this file and link it
13     with other works to produce a work based on this file, this file
14     does not by itself cause the resulting work to be covered
15     by the GNU General Public License.
16
17     However the source code for this file must still be made available
18     in accordance with section (3) of the GNU General Public License.
19
20     This exception does not invalidate any other reasons why a work based
21     on this file might be covered by the GNU General Public License.
22 */
23
24 #include <libxml++/libxml++.h>
25 #include <cassert>
26 #include <iostream>
27 #include <set>
28 #include <fstream>
29 #include <list>
30 #include <stdexcept>
31 #include <string>
32 #include <boost/lexical_cast.hpp>
33 #include <boost/serialization/extended_type_info.hpp>
34
35 #include "config.h"
36
37
38
39 //! map group to class name
40 std::string groupClass(const std::string &group)
41 {
42     return std::string("cmd_group_")+group;
43 }
44
45 //! convert string to upper case
46 std::string toupper(std::string s)
47 {
48     for (unsigned i=0; i<s.length(); ++i) s[i]=toupper(s[i]);
49     return s;
50 }
51
52 //! replace all characters f by r in string s
53 std::string replace(std::string s, char f, char r)
54 {
55     for (unsigned i=0; i<s.length(); ++i) if (s[i]==f) s[i]=r;
56     return s;
57 }
58
59 //! strip prefix from string s
60 /*!
61   \return string s without prefix or an empty string on error
62  */
63 std::string strip(std::string s, std::string prefix)
64 {
65     std::string error;
66     if ( (prefix.length()>s.length() ) || ( std::string(s,0,prefix.length())!=prefix ) ) return error;
67     return std::string(s, prefix.length(), s.length()-prefix.length());
68 }
69
70 //! get child element by id
71 /*!
72   \return pointer to element having id or null on error
73   \todo find libxmlpp pendant
74 */
75 const xmlpp::Element* get_element_by_id(const xmlpp::Element* element, const std::string &id)
76 {
77     const xmlpp::Attribute* cid = element->get_attribute("id");
78     if ( cid && ( cid->get_value() == id)) return element;
79
80     //Recurse through child nodes:
81     xmlpp::Node::NodeList list = element->get_children();
82     for (xmlpp::Node::NodeList::iterator iter = list.begin(); iter != list.end(); ++iter)
83     {
84         const xmlpp::Element* element = dynamic_cast<const xmlpp::Element*>(*iter);
85         if (element)
86         {
87             const xmlpp::Element* match = get_element_by_id(element, id);
88             if (match) return match;
89         }
90     }
91     return NULL;
92 }
93
94 std::string get_file(const xmlpp::Element* root, const std::string &file_id)
95 {
96     std::string error;
97     const xmlpp::Element* e=get_element_by_id(root, file_id);
98     if ((!e)||(!e->get_attribute("name"))) return error;
99     return e->get_attribute("name")->get_value();
100 }
101
102 //! get namespace by id
103 /*!
104   \return namespace name or empty string on error
105 */
106 std::string get_namespace(const xmlpp::Element* root, const std::string &id)
107 {
108     std::string error;
109     const xmlpp::Element* element(get_element_by_id(root, id));
110     // [RP:20071024]: use "demangled" attribute instead of "name" to cover nested namespaces:
111     if ((!element)||(!element->get_attribute("demangled"))) return error;
112     return element->get_attribute("demangled")->get_value();
113 }
114
115 //! procedure marked for export?
116 bool is_marked(const std::string &attrs)
117 {
118     // todo: improve this
119     std::string to_match("gccxml(libt2n-");
120     std::string::size_type p(attrs.find(to_match));
121     return (p!=std::string::npos);
122 }
123
124 struct type_info
125 {
126     std::string name;
127     std::string noref_name;
128     bool operator==(const type_info& o) {return (name==o.name) && (noref_name == o.noref_name);}
129     std::string noref() const {return noref_name.empty() ? name : noref_name;}
130     bool isVoid() const {return name=="void";}
131 };
132
133 std::ostream &operator<<(std::ostream &o, const type_info &t)
134 {
135     o << t.name;
136     return o;
137 }
138
139 struct parse_error : public std::runtime_error
140 {
141     parse_error(const std::string &file, unsigned line, const std::string &msg)
142             : std::runtime_error(file+":"+boost::lexical_cast<std::string>(line)+": error: "+msg)
143     {}
144 };
145
146 struct error_name_too_long : public std::runtime_error
147 {
148     error_name_too_long(const std::string &name)
149             : std::runtime_error("symbol name '" + name + "' too long for serialization ("
150                                  + boost::lexical_cast<std::string>(name.length()) + ">"
151                                  + boost::lexical_cast<std::string>(BOOST_SERIALIZATION_MAX_KEY_SIZE-1) + ")")
152     {}
153 };
154
155 //! get type by id
156 /*!
157   \return type name or empty string on error
158 */
159 type_info get_type(const xmlpp::Element* root, const std::string &id)
160 {
161     type_info error;
162     const xmlpp::Element* element(get_element_by_id(root, id));
163     if (!element) return error;
164
165     // TODO: not yet complete
166     // if we recurse - when do we stop?
167     // if it is a typedef? yes? (hmm if the typedef is in the file parsed this will not work)
168
169     // TODO: const and reference types handling is a ugly hack
170
171     std::string tag(element->get_name());
172     if (tag=="ReferenceType")
173     {
174         assert(element->get_attribute("type"));
175         type_info ret(get_type(root, element->get_attribute("type")->get_value()));
176         if (ret==error) return error;
177         // at the moment we only support const &
178         // todo: nice error message!
179         if ((ret.noref_name=strip(ret.name,"const ")).empty()) return error;
180         ret.name=ret.name+"&";
181         return ret;
182     }
183     else if (tag=="CvQualifiedType")
184     {
185         assert(element->get_attribute("type"));
186         type_info ret(get_type(root, element->get_attribute("type")->get_value()));
187         if (ret==error) return error;
188         ret.name=std::string("const ")+ret.name;
189         return ret;
190     }
191     else if (tag=="PointerType")
192     {
193         // todo: nearly the same as reference type handling
194         assert(element->get_attribute("type"));
195         type_info ret(get_type(root, element->get_attribute("type")->get_value()));
196         if (ret==error) return error;
197         // at the moment we only support const &
198         // todo: nice error message!
199         if ((ret.noref_name=strip(ret.name,"const ")).empty()) return error;
200         ret.name=ret.name+"*";
201         return ret;
202     }
203
204     assert(element->get_attribute("name"));
205     type_info ret;
206     if (element->get_attribute("context"))
207     {
208         ret.name=get_namespace(root, element->get_attribute("context")->get_value());
209         if (ret.name!="::")
210             ret.name+="::";
211         else
212             // do not explicitely add ::
213             ret.name="";
214     }
215     ret.name+=element->get_attribute("name")->get_value();
216     return ret;
217 }
218
219 struct Arg
220 {
221     std::string name;
222     type_info type;
223     std::string defaultArg;
224 };
225
226 struct t2n_procedure
227 {
228     typedef std::list<Arg> Args;
229
230     type_info ret_type;
231     std::string name;
232     std::string mangled;
233     Args  args;
234
235     std::string ret_classname() const
236     {
237         std::string result = name+mangled+"_res";
238         if (result.length() >= BOOST_SERIALIZATION_MAX_KEY_SIZE)
239             throw error_name_too_long(result);
240         return result;
241     }
242     std::string cmd_classname() const
243     {
244         std::string result = name+mangled+"_cmd";
245         if (result.length() >= BOOST_SERIALIZATION_MAX_KEY_SIZE)
246             throw error_name_too_long(result);
247         return result;
248     }
249     bool hasReturn() const {return !ret_type.isVoid();}
250 };
251
252 std::ostream &operator<<(std::ostream &o, const t2n_procedure::Args &args)
253 {
254     for (t2n_procedure::Args::const_iterator it=args.begin();it!=args.end();++it)
255     {
256         if (it!=args.begin()) o << ", ";
257         o << it->type << " " << it->name;
258
259         if (!it->defaultArg.empty())
260             o << "=" << it->defaultArg;
261     }
262     return o;
263 }
264
265 std::ostream &operator<<(std::ostream &o, const t2n_procedure &f)
266 {
267     o << f.ret_type << " " << f.name << "(" << f.args << ")";
268     return o;
269 }
270
271 std::pair<std::string, unsigned>
272 get_file_and_line(const xmlpp::Element* root, const xmlpp::Element* element)
273 {
274     return std::pair<std::string, unsigned>(get_file(root, element->get_attribute("file")->get_value()),
275                                             boost::lexical_cast<unsigned>(element->get_attribute("line")->get_value())-1);
276 }
277
278 std::string get_file_and_line_as_string(const xmlpp::Element* root, const xmlpp::Element* element)
279 {
280     std::pair<std::string, unsigned> fl(get_file_and_line(root,element));
281     return std::string(fl.first)+":"+boost::lexical_cast<std::string>(fl.second);
282 }
283
284 class Parser
285 {
286 public:
287     Parser(const std::string &fname) : m_fname(fname) {}
288
289     std::list<t2n_procedure> get_procedures()
290     {
291         xmlpp::DomParser parser;
292         //    parser.set_validate();
293         parser.set_substitute_entities(); //We just want the text to be resolved/unescaped automatically.
294         parser.parse_file(m_fname);
295         if (parser)
296         {
297             //Walk the tree:
298             const xmlpp::Node* pNode = parser.get_document()->get_root_node(); //deleted by DomParser.
299             const xmlpp::Element* root = dynamic_cast<const xmlpp::Element*>(pNode);
300             assert(root);
301             visit_node(root);
302         }
303         return m_procedures;
304     }
305 protected:
306     std::string m_fname;
307     std::list<t2n_procedure> m_procedures;
308
309     void parse_function(const xmlpp::Element* root, const xmlpp::Node* node)
310     {
311         const xmlpp::Element* element = dynamic_cast<const xmlpp::Element*>(node);
312         if (!element) return;
313
314         const xmlpp::Attribute* attributes = element->get_attribute("attributes");
315         const xmlpp::Attribute* name = element->get_attribute("name");
316         const xmlpp::Attribute* mangled = element->get_attribute("mangled");
317         const xmlpp::Attribute* returns = element->get_attribute("returns");
318         if ((!attributes)||(!name)||(!mangled)||(!returns)) return;
319
320         // check wether the procedure is marked (TODO: improve)
321         // attributes are speparated by spaces?
322
323         t2n_procedure f;
324         if (!is_marked(attributes->get_value())) return;
325
326         // we need the return type
327         f.ret_type=get_type(root, returns->get_value());
328         f.name=name->get_value();
329         f.mangled=mangled->get_value();
330
331         xmlpp::Node::NodeList list = node->get_children("Argument");
332         for (xmlpp::Node::NodeList::iterator iter = list.begin(); iter != list.end(); ++iter)
333         {
334             const xmlpp::Element* arg = dynamic_cast<const xmlpp::Element*>(*iter);
335             if ( arg )
336             {
337                 struct Arg a;
338
339                 assert(arg->get_name() == "Argument");
340
341                 assert(arg->get_attribute("name"));
342                 a.name=arg->get_attribute("name")->get_value();
343
344                 assert(arg->get_attribute("type"));
345                 a.type=get_type(root, arg->get_attribute("type")->get_value());
346
347                 // this would be the nice way of solving the problem of default arguments
348                 // but currently the output of gccxml is unreliable (e.g. namespace only
349                 // sometimes available)
350                 // if(arg->get_attribute("default"))
351                 //    a.defaultArg=arg->get_attribute("default")->get_value();
352
353                 // so we need to get the def. arg. via attribute
354                 // which is previously set by the macro LIBT2N_DEFAULT_ARG(type,value)
355                 if(arg->get_attribute("attributes"))
356                 {
357                     std::string attr=arg->get_attribute("attributes")->get_value();
358                     const std::string look_for = "gccxml(libt2n-default-arg,";
359
360                     if (attr.compare(0,look_for.size(),look_for) == 0)
361                         a.defaultArg=attr.substr(look_for.size(),attr.size()-look_for.size()-1);
362                 }
363
364                 // todo: ugly - could be any other error
365                 if (a.type.name.empty())
366                 {
367                     assert(element->get_attribute("file"));
368                     assert(element->get_attribute("line"));
369                     std::pair<std::string, unsigned> file_and_line(get_file_and_line(root, element));
370                     throw parse_error(file_and_line.first,
371                                       file_and_line.second,
372                                       std::string("type of parameter '")+a.name+"' not (yet?) supported");
373                 }
374
375                 f.args.push_back(a);
376             }
377         }
378         std::cerr << get_file_and_line_as_string(root, element) << ":\texport procedure: " << f << std::endl;
379         m_procedures.push_back(f);
380     }
381
382     void visit_node(const xmlpp::Element* root, const xmlpp::Node* node = NULL, unsigned int indentation = 0)
383     {
384         if (!node) node=root;
385
386         const xmlpp::ContentNode* nodeContent = dynamic_cast<const xmlpp::ContentNode*>(node);
387         const xmlpp::TextNode* nodeText = dynamic_cast<const xmlpp::TextNode*>(node);
388         const xmlpp::CommentNode* nodeComment = dynamic_cast<const xmlpp::CommentNode*>(node);
389
390         if (nodeText && nodeText->is_white_space()) //Let's ignore the indenting - you don't always want to do this.
391             return;
392
393         std::string nodename = node->get_name();
394
395         if (!nodeText && !nodeComment && !nodename.empty()) //Let's not say "name: text".
396         {
397             if (node->get_name() == "Function") parse_function(root, node);
398         }
399         if (!nodeContent)
400         {
401             //Recurse through child nodes:
402             xmlpp::Node::NodeList list = node->get_children();
403             for (xmlpp::Node::NodeList::iterator iter = list.begin(); iter != list.end(); ++iter)
404             {
405                 visit_node(root, *iter, indentation + 2); //recursive
406             }
407         }
408     }
409 };
410
411 void output_common_hpp(std::ostream &o, const std::string &group, const std::list<t2n_procedure> &procs)
412 {
413     o << "class " << groupClass(group) << " : public libt2n::command\n"
414     << "{\n"
415     << "private:\n"
416     << " friend class boost::serialization::access;\n"
417     << " template<class Archive>\n"
418     << " void serialize(Archive & ar, const unsigned int /* version */)\n"
419     << " {ar & BOOST_SERIALIZATION_BASE_OBJECT_NVP(libt2n::command);}\n"
420     << "};\n";
421
422     for (std::list<t2n_procedure>::const_iterator it=procs.begin();it!=procs.end();++it)
423     {
424         o << "class " << it->ret_classname() << " : public libt2n::result\n"
425         << "{\n"
426         << "private:\n";
427         if (it->hasReturn())
428             o << " " << it->ret_type << " res;\n";
429         o << " friend class boost::serialization::access;\n"
430         << " template<class Archive>\n"
431         << " void serialize(Archive & ar, const unsigned int /* version */)\n"
432         << " {\n"
433         << "  ar & BOOST_SERIALIZATION_BASE_OBJECT_NVP(libt2n::result);\n";
434         if (it->hasReturn())
435             o << "  ar & BOOST_SERIALIZATION_NVP(res);\n";
436         o << " }\n"
437         << "public:\n"
438         << " " << it->ret_classname() << "() {}\n";
439         if (it->hasReturn())
440         {
441             o << " " << it->ret_classname() << "(const " << it->ret_type << " &_res) : res(_res) {}\n"
442             << " " << it->ret_type << " get_data() { return res; }\n";
443         }
444         o << "};\n";
445     }
446     for (std::list<t2n_procedure>::const_iterator it=procs.begin();it!=procs.end();++it)
447     {
448         o << "class " << it->cmd_classname() << " : public " << groupClass(group) << "\n"
449         << "{\n"
450         << "private:\n";
451         for (t2n_procedure::Args::const_iterator ait=it->args.begin();ait!=it->args.end();++ait)
452         {
453             o << " " << ait->type.noref() << " " << ait->name << ";\n";
454         }
455         o << " friend class boost::serialization::access;\n"
456         << " template<class Archive>\n"
457         << " void serialize(Archive & ar, const unsigned int /* version */)\n"
458         << " {\n"
459         << "  ar & BOOST_SERIALIZATION_BASE_OBJECT_NVP(" << groupClass(group) << ");\n";
460         for (t2n_procedure::Args::const_iterator ait=it->args.begin();ait!=it->args.end();++ait)
461         {
462             o << "  ar & BOOST_SERIALIZATION_NVP(" << ait->name << ");\n";
463         }
464
465         // default constructor
466         o << " }\n"
467         << "\n"
468         << "public:\n"
469         << " " << it->cmd_classname() << "() {}\n";
470
471         // constructor taking all arguments
472         if (!it->args.empty())
473         {
474             o << " " << it->cmd_classname() << "(";
475             for (t2n_procedure::Args::const_iterator ait=it->args.begin();ait!=it->args.end();++ait)
476             {
477                 if (ait!=it->args.begin()) o << ", ";
478                 o << ait->type << " _" << ait->name;
479             }
480             o << ") : ";
481             for (t2n_procedure::Args::const_iterator ait=it->args.begin();ait!=it->args.end();++ait)
482             {
483                 if (ait!=it->args.begin()) o << ", ";
484                 // pointers are const pointers and must be dereferenced
485                 o << ait->name << "(" << ((ait->type.name.find_first_of('*')!=std::string::npos) ? "*" : "" ) << "_" << ait->name << ")";
486             }
487             o << " {}\n";
488         }
489         o << " libt2n::result* operator()();\n"
490         << "};\n";
491     }
492 }
493
494 void output_common_cpp(std::ostream &o, const std::string &group, const std::list<t2n_procedure> &procs, const std::string &common_hpp)
495 {
496     o << "#include \"" << common_hpp << "\"\n"
497     << "#include <boost/serialization/export.hpp>\n"
498     << "\n"
499     << "/* register types with boost serialization */\n";
500     o << "BOOST_CLASS_EXPORT(" << groupClass(group) << ")\n";
501     for (std::list<t2n_procedure>::const_iterator it=procs.begin();it!=procs.end();++it)
502     {
503         o << "BOOST_CLASS_EXPORT("<<it->ret_classname()<<")\n"
504         << "BOOST_CLASS_EXPORT("<<it->cmd_classname()<<")\n";
505     }
506 }
507
508 void output_client_hpp(std::ostream &o, const std::string &group, const std::list<t2n_procedure> &procs)
509 {
510     o << "#include <command_client.hxx>\n";
511
512     o << "class " << groupClass(group) << "_client : public libt2n::command_client\n"
513     << "{\n"
514     << "public:\n"
515     << groupClass(group) << "_client(libt2n::client_connection *_c,\n"
516     << " long long _command_timeout_usec=command_timeout_usec_default,\n"
517     << " long long _hello_timeout_usec=hello_timeout_usec_default)\n"
518     << " : libt2n::command_client(_c,_command_timeout_usec,_hello_timeout_usec)\n"
519     << " {}\n";
520     for (std::list<t2n_procedure>::const_iterator pit=procs.begin();pit!=procs.end();++pit)
521     {
522         o << " " << *pit << ";\n";
523     }
524     o << "};\n";
525 }
526
527 void output_client_cpp(std::ostream &o, const std::string &group, const std::list<t2n_procedure> &procs, const std::string &common_hpp, const std::string &common_cpp, const std::string &client_hpp)
528 {
529     o << "#include \"" << client_hpp << "\"\n"
530     << "#include \"" << common_hpp << "\"\n"
531     << "// fake\n";
532     for (std::list<t2n_procedure>::const_iterator it=procs.begin();it!=procs.end();++it)
533     {
534         o << "libt2n::result* " << it->cmd_classname() << "::operator()() { return NULL; }\n";
535     }
536
537     for (std::list<t2n_procedure>::const_iterator pit=procs.begin();pit!=procs.end();++pit)
538     {
539         o << pit->ret_type << " " << groupClass(group) << "_client::" << pit->name << "(";
540
541         // we need to do this by hand here because we don't want default arguments within the cpp
542         for (t2n_procedure::Args::const_iterator xit=pit->args.begin();xit!=pit->args.end();++xit)
543         {
544             if (xit!=pit->args.begin())
545                 o << ", ";
546             o << xit->type << " " << xit->name;
547         }
548
549         o << ")\n"
550         << "{\n"
551         << " libt2n::result_container rc;\n"
552         << " send_command(new " << pit->cmd_classname() << "(";
553         for (t2n_procedure::Args::const_iterator ait=pit->args.begin();ait!=pit->args.end();++ait)
554         {
555             if (ait!=pit->args.begin()) o << ", ";
556             o << ait->name;
557         }
558         o << "), rc);\n"
559         << " " << pit->ret_classname() << "* res=dynamic_cast<" << pit->ret_classname() << "*>(rc.get_result());\n"
560         << " if (!res) throw libt2n::t2n_communication_error(\"result object of wrong type\");\n";
561         if (pit->hasReturn())
562             o << " return res->get_data();\n";
563         o << "}\n";
564     }
565
566     // include in this compilation unit to ensure the compilation unit is used
567     // see also:
568     // http://www.google.de/search?q=g%2B%2B+static+initializer+in+static+library
569     o << "#include \"" << common_cpp << "\"\n";
570 }
571
572 void output_server_hpp(std::ostream &o, const std::string & /* group */, const std::list<t2n_procedure> &procs, const std::string &common_hpp)
573 {
574     o << "#include \"" << common_hpp << "\"\n";
575
576     // output function declarations
577     for (std::list<t2n_procedure>::const_iterator it=procs.begin();it!=procs.end();++it)
578         o << *it << ";\n";
579 }
580
581 void output_server_cpp(std::ostream &o, const std::string &group, const std::list<t2n_procedure> &procs, const std::string &common_hpp, const std::string &common_cpp)
582 {
583     o << "#include \"" << common_hpp << "\"\n";
584
585     for (std::list<t2n_procedure>::const_iterator it=procs.begin();it!=procs.end();++it)
586     {
587         o << *it << ";\n";
588         o << "libt2n::result* " << it->cmd_classname() << "::operator()() { ";
589
590         if (it->hasReturn())
591             o << "return new " << it->ret_classname() << "(";
592
593         // output function name and args
594         o << it->name << "(";
595         for (t2n_procedure::Args::const_iterator ait=it->args.begin();ait!=it->args.end();++ait)
596         {
597             if (ait!=it->args.begin()) o << ", ";
598             // get pointer
599             if (ait->type.name.find_first_of('*')!=std::string::npos)
600                 o << '&';
601             o << ait->name;
602         }
603
604         if (it->hasReturn())
605             o << "));";
606         else
607             o << "); return new " << it->ret_classname() << "();";
608
609         o << " }\n";
610     }
611     o << "#include \"" << common_cpp << "\"\n";
612 }
613
614 struct header_file : public std::ofstream
615 {
616     header_file(const char* fname) : std::ofstream(fname)
617     {
618         std::cerr << "create header: '" << fname << "'" << std::endl;
619         std::string macro(replace(toupper(fname),'.','_'));
620         *this << "// automatically generated code (generated by libt2n-codegen " << VERSION << ") - do not edit\n" << std::endl;
621         *this << "#ifndef " << macro << "\n"
622         << "#define " << macro << "\n";
623     }
624     ~header_file()
625     {
626         *this << "#endif" << std::endl;
627     }
628 };
629
630 struct cpp_file : public std::ofstream
631 {
632     cpp_file(const char* fname) : std::ofstream(fname)
633     {
634         std::cerr << "create cpp: '" << fname << "'" << std::endl;
635         *this << "// automatically generated code - do not edit\n" << std::endl;
636     }
637 };
638
639 int
640 main(int argc, char* argv[])
641 {
642     // todo: maybe use getopt
643     if ((argc>1)&&(std::string(argv[1])=="--version"))
644     {
645         std::cerr << VERSION << std::endl;
646         return 0;
647     }
648     if (argc < 3)
649     {
650         std::cerr << "Usage: " << argv[0] << "default-group gccxml-file1 gccxml-file2 ... " << std::endl;
651         return 1;
652     }
653
654     try
655     {
656         std::string group(argv[1]);
657         std::list<std::string> xmlfiles;
658         for (int i=2;i<argc;++i)
659             xmlfiles.push_back(argv[i]);
660
661         std::string prefix=group+"_";
662         std::list<t2n_procedure> procedures;
663         for (std::list<std::string>::iterator it=xmlfiles.begin();it!=xmlfiles.end();++it)
664         {
665             std::cerr << "Parse " << *it << std::endl;
666             Parser parser(*it);
667             const std::list<t2n_procedure> &p(parser.get_procedures());
668             std::copy(p.begin(), p.end(), std::back_inserter(procedures));
669         }
670
671         std::cerr << "All procedures:" << std::endl;
672         for (std::list<t2n_procedure>::const_iterator it=procedures.begin();it!=procedures.end();++it)
673             std::cerr << *it << ";" << std::endl;
674
675         std::string common_hpp_fname(prefix+"common.hxx");
676         std::string common_cpp_fname(prefix+"common.cpp");
677         std::string client_hpp_fname(prefix+"client.hxx");
678         std::string client_cpp_fname(prefix+"client.cpp");
679         std::string server_hpp_fname(prefix+"server.hxx");
680         std::string server_cpp_fname(prefix+"server.cpp");
681
682         header_file common_hpp(common_hpp_fname.c_str());
683         common_hpp << "// boost serialization is picky about order of include files => we have to include this one first\n"
684         << "#include \"codegen-stubhead.hxx\"\n"
685         << "#include \"" << group << ".hxx\"\n";
686
687         output_common_hpp(common_hpp, group, procedures);
688
689         cpp_file common_cpp(common_cpp_fname.c_str());
690         output_common_cpp(common_cpp, group, procedures, common_hpp_fname);
691
692         header_file client_hpp(client_hpp_fname.c_str());
693         client_hpp << "// boost serialization is picky about order of include files => we have to include this one first\n"
694         << "#include \"codegen-stubhead.hxx\"\n"
695         << "#include \"" << group << ".hxx\"\n";
696         output_client_hpp(client_hpp, group, procedures);
697
698         cpp_file client_cpp(client_cpp_fname.c_str());
699         output_client_cpp(client_cpp, group, procedures, common_hpp_fname, common_cpp_fname, client_hpp_fname);
700
701         header_file server_hpp(server_hpp_fname.c_str());
702         output_server_hpp(server_hpp, group, procedures, common_hpp_fname);
703
704         cpp_file server_cpp(server_cpp_fname.c_str());
705         output_server_cpp(server_cpp, group, procedures, common_hpp_fname, common_cpp_fname);
706     }
707     catch (const parse_error &e)
708     {
709         std::cerr << e.what() << std::endl;
710         return EXIT_FAILURE;
711     }
712     return EXIT_SUCCESS;
713 }