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