summaryrefslogtreecommitdiff
path: root/odb/odb/generator.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'odb/odb/generator.cxx')
-rw-r--r--odb/odb/generator.cxx1044
1 files changed, 1044 insertions, 0 deletions
diff --git a/odb/odb/generator.cxx b/odb/odb/generator.cxx
new file mode 100644
index 0000000..f0b92ab
--- /dev/null
+++ b/odb/odb/generator.cxx
@@ -0,0 +1,1044 @@
+// file : odb/generator.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <cctype> // std::toupper, std::is{alpha,upper,lower}
+#include <string>
+#include <memory> // std::unique_ptr
+#include <iomanip>
+#include <fstream>
+#include <sstream>
+#include <iostream>
+
+#include <libcutl/fs/auto-remove.hxx>
+
+#include <libcutl/compiler/code-stream.hxx>
+#include <libcutl/compiler/cxx-indenter.hxx>
+#include <libcutl/compiler/sloc-counter.hxx>
+
+#include <libstudxml/parser.hxx>
+#include <libstudxml/serializer.hxx>
+
+#include <odb/version.hxx>
+#include <odb/context.hxx>
+#include <odb/generator.hxx>
+
+#include <odb/semantics/relational/model.hxx>
+#include <odb/semantics/relational/changeset.hxx>
+#include <odb/semantics/relational/changelog.hxx>
+
+#include <odb/generate.hxx>
+#include <odb/relational/generate.hxx>
+
+using namespace std;
+using namespace cutl;
+
+using semantics::path;
+typedef vector<string> strings;
+typedef vector<path> paths;
+typedef vector<cutl::shared_ptr<ofstream> > ofstreams;
+
+namespace
+{
+ static char const cxx_file_header[] =
+ "// -*- C++ -*-\n"
+ "//\n"
+ "// This file was generated by ODB, object-relational mapping (ORM)\n"
+ "// compiler for C++.\n"
+ "//\n\n";
+
+ static char const sql_file_header[] =
+ "/* This file was generated by ODB, object-relational mapping (ORM)\n"
+ " * compiler for C++.\n"
+ " */\n\n";
+
+ void
+ open (ifstream& ifs, path const& p)
+ {
+ ifs.open (p.string ().c_str (), ios_base::in | ios_base::binary);
+
+ if (!ifs.is_open ())
+ {
+ cerr << "error: unable to open '" << p << "' in read mode" << endl;
+ throw generator_failed ();
+ }
+ }
+
+ void
+ open (ofstream& ofs, path const& p, ios_base::openmode m = ios_base::out)
+ {
+ ofs.open (p.string ().c_str (), ios_base::out | m);
+
+ if (!ofs.is_open ())
+ {
+ cerr << "error: unable to open '" << p << "' in write mode" << endl;
+ throw generator_failed ();
+ }
+ }
+
+ void
+ append (ostream& os, strings const& text)
+ {
+ for (strings::const_iterator i (text.begin ());
+ i != text.end (); ++i)
+ {
+ os << *i << endl;
+ }
+ }
+
+ void
+ append (ostream& os, path const& file)
+ {
+ ifstream ifs;
+ open (ifs, file);
+
+ // getline() will set the failbit if it failed to extract anything,
+ // not even the delimiter and eofbit if it reached eof before seeing
+ // the delimiter.
+ //
+ // We used to just do:
+ //
+ // os << ifs.rdbuf ();
+ //
+ // But that has some drawbacks: it won't end with a newline if the file
+ // doesn't end with one. There were also some issues with Windows newlines
+ // (we ended up doubling them).
+ //
+ for (string s; getline (ifs, s); )
+ os << s << endl;
+ }
+
+ // Append prologue/interlude/epilogue.
+ //
+ void
+ append_logue (ostream& os,
+ database db,
+ database_map<vector<string> > const& text,
+ database_map<vector<string> > const& file,
+ char const* begin_comment,
+ char const* end_comment)
+ {
+ bool t (text.count (db) != 0);
+ bool f (file.count (db) != 0);
+
+ if (t || f)
+ {
+ os << begin_comment << endl;
+
+ if (t)
+ append (os, text[db]);
+
+ if (f)
+ {
+ strings const& fs (file[db]);
+
+ for (strings::const_iterator i (fs.begin ());
+ i != fs.end (); ++i)
+ append (os, path (*i));
+ }
+
+ os << end_comment << endl
+ << endl;
+ }
+ }
+}
+
+void
+generate (options const& ops,
+ features& fts,
+ semantics::unit& unit,
+ path const& p,
+ paths const& inputs)
+{
+ namespace sema_rel = semantics::relational;
+ using cutl::shared_ptr;
+
+ try
+ {
+ database db (ops.database ()[0]);
+ multi_database md (ops.multi_database ());
+
+ // First create the database model.
+ //
+ bool gen_schema (ops.generate_schema () && db != database::common);
+
+ shared_ptr<sema_rel::model> model;
+
+ if (gen_schema)
+ {
+ unique_ptr<context> ctx (create_context (cerr, unit, ops, fts, 0));
+
+ switch (db)
+ {
+ case database::mssql:
+ case database::mysql:
+ case database::oracle:
+ case database::pgsql:
+ case database::sqlite:
+ {
+ model = relational::model::generate ();
+ break;
+ }
+ case database::common:
+ break;
+ }
+ }
+
+ // Input files.
+ //
+ path file (ops.input_name ().empty ()
+ ? p.leaf ()
+ : path (ops.input_name ()).leaf ());
+ string base (file.base ().string ());
+
+ path in_log_path;
+ path log_dir (ops.changelog_dir ().count (db) != 0
+ ? ops.changelog_dir ()[db]
+ : "");
+ if (ops.changelog_in ().count (db) != 0)
+ {
+ in_log_path = path (ops.changelog_in ()[db]);
+
+ if (!log_dir.empty () && !in_log_path.absolute ())
+ in_log_path = log_dir / in_log_path;
+ }
+ else if (ops.changelog ().count (db) != 0)
+ {
+ in_log_path = path (ops.changelog ()[db]);
+
+ if (!in_log_path.absolute () && !log_dir.empty ())
+ in_log_path = log_dir / in_log_path;
+ }
+ else
+ {
+ string log_name (base + ops.changelog_file_suffix ()[db] +
+ ops.changelog_suffix ());
+ in_log_path = path (log_name);
+
+ if (!log_dir.empty ())
+ in_log_path = log_dir / in_log_path;
+ else
+ in_log_path = p.directory () / in_log_path; // Use input directory.
+ }
+
+ // Load the old changelog and generate a new one.
+ //
+ bool gen_changelog (gen_schema && unit.count ("model-version") != 0);
+ cutl::shared_ptr<sema_rel::changelog> changelog;
+ cutl::shared_ptr<sema_rel::changelog> old_changelog;
+ string old_changelog_xml;
+
+ path out_log_path;
+ if (ops.changelog_out ().count (db))
+ {
+ out_log_path = path (ops.changelog_out ()[db]);
+
+ if (!log_dir.empty () && !out_log_path.absolute ())
+ out_log_path = log_dir / out_log_path;
+ }
+ else
+ out_log_path = in_log_path;
+
+ if (gen_changelog)
+ {
+ ifstream log;
+
+ // Unless we are forced to re-initialize the changelog, load the
+ // old one.
+ //
+ if (!ops.init_changelog ())
+ log.open (in_log_path.string ().c_str (),
+ ios_base::in | ios_base::binary);
+
+ if (log.is_open ()) // The changelog might not exist.
+ {
+ try
+ {
+ // Get the XML into a buffer. We use it to avoid modifying the
+ // file when the changelog hasn't changed.
+ //
+ for (bool first (true); !log.eof (); )
+ {
+ string line;
+ getline (log, line);
+
+ if (log.fail ())
+ ios_base::failure ("getline");
+
+ if (first)
+ first = false;
+ else
+ old_changelog_xml += '\n';
+
+ old_changelog_xml += line;
+ }
+
+ istringstream is (old_changelog_xml);
+ is.exceptions (ios_base::badbit | ios_base::failbit);
+
+ xml::parser p (is, in_log_path.string ());
+ old_changelog.reset (new (shared) sema_rel::changelog (p));
+
+ if (old_changelog->database () != db.string ())
+ {
+ cerr << in_log_path << ": error: wrong database '" <<
+ old_changelog->database () << "', expected '" << db <<
+ "'" << endl;
+ throw generator_failed ();
+ }
+
+ string sn (ops.schema_name ()[db]);
+ if (old_changelog->schema_name () != sn)
+ {
+ cerr << in_log_path << ": error: wrong schema name '" <<
+ old_changelog->schema_name () << "', expected '" << sn <<
+ "'" << endl;
+ throw generator_failed ();
+ }
+ }
+ catch (const ios_base::failure& e)
+ {
+ cerr << in_log_path << ": read failure" << endl;
+ throw generator_failed ();
+ }
+ catch (const xml::parsing& e)
+ {
+ cerr << e.what () << endl;
+ throw generator_failed ();
+ }
+ }
+
+ changelog = relational::changelog::generate (
+ *model,
+ unit.get<model_version> ("model-version"),
+ old_changelog.get (),
+ in_log_path.string (),
+ out_log_path.string (),
+ ops);
+ }
+
+ // Output files.
+ //
+ fs::auto_removes auto_rm;
+
+ string hxx_name (base + ops.odb_file_suffix ()[db] + ops.hxx_suffix ());
+ string ixx_name (base + ops.odb_file_suffix ()[db] + ops.ixx_suffix ());
+ string cxx_name (base + ops.odb_file_suffix ()[db] + ops.cxx_suffix ());
+ string sch_name (base + ops.schema_file_suffix ()[db] + ops.cxx_suffix ());
+ string sql_name (base + ops.sql_file_suffix ()[db] + ops.sql_suffix ());
+
+ path hxx_path (hxx_name);
+ path ixx_path (ixx_name);
+ path cxx_path (cxx_name);
+ path sch_path (sch_name);
+ path sql_path (sql_name);
+ paths mig_pre_paths;
+ paths mig_post_paths;
+
+ bool gen_migration (gen_changelog && !ops.suppress_migration ());
+ bool gen_sql_migration (
+ gen_migration && ops.schema_format ()[db].count (schema_format::sql));
+
+ if (gen_sql_migration)
+ {
+ for (sema_rel::changelog::contains_changeset_iterator i (
+ changelog->contains_changeset_begin ());
+ i != changelog->contains_changeset_end (); ++i)
+ {
+ sema_rel::changeset& cs (i->changeset ());
+
+ // Default format: %N[-D%]-%3V-{pre|post}.sql
+ //
+ string n (base);
+
+ if (md != multi_database::disabled)
+ n += '-' + db.string ();
+
+ ostringstream os;
+ os << setfill ('0') << setw (3) << cs.version ();
+ n += '-' + os.str ();
+
+ mig_pre_paths.push_back (path (n + "-pre" + ops.sql_suffix ()));
+ mig_post_paths.push_back (path (n + "-post" + ops.sql_suffix ()));
+ }
+ }
+
+ if (!ops.output_dir ().empty ())
+ {
+ path dir (ops.output_dir ());
+ hxx_path = dir / hxx_path;
+ ixx_path = dir / ixx_path;
+ cxx_path = dir / cxx_path;
+ sch_path = dir / sch_path;
+ sql_path = dir / sql_path;
+
+ if (gen_sql_migration)
+ {
+ for (paths::size_type i (0); i < mig_pre_paths.size (); ++i)
+ {
+ mig_pre_paths[i] = dir / mig_pre_paths[i];
+ mig_post_paths[i] = dir / mig_post_paths[i];
+ }
+ }
+ }
+
+ //
+ //
+ bool gen_cxx (!ops.generate_schema_only ());
+
+ ofstream hxx;
+ if (gen_cxx)
+ {
+ open (hxx, hxx_path);
+ auto_rm.add (hxx_path);
+ }
+
+ //
+ //
+ ofstream ixx;
+ if (gen_cxx)
+ {
+ open (ixx, ixx_path);
+ auto_rm.add (ixx_path);
+ }
+
+ //
+ //
+ ofstream cxx;
+ if (gen_cxx && (db != database::common || md == multi_database::dynamic))
+ {
+ open (cxx, cxx_path);
+ auto_rm.add (cxx_path);
+ }
+
+ //
+ //
+ bool gen_sep_schema (
+ gen_schema &&
+ ops.schema_format ()[db].count (schema_format::separate));
+
+ ofstream sch;
+ if (gen_sep_schema)
+ {
+ open (sch, sch_path);
+ auto_rm.add (sch_path);
+ }
+
+ //
+ //
+ bool gen_sql_schema (gen_schema &&
+ ops.schema_format ()[db].count (schema_format::sql));
+ ofstream sql;
+ if (gen_sql_schema)
+ {
+ open (sql, sql_path);
+ auto_rm.add (sql_path);
+ }
+
+ //
+ //
+ ofstreams mig_pre, mig_post;
+ if (gen_sql_migration)
+ {
+ for (paths::size_type i (0); i < mig_pre_paths.size (); ++i)
+ {
+ shared_ptr<ofstream> pre (new (shared) ofstream);
+ shared_ptr<ofstream> post (new (shared) ofstream);
+
+ open (*pre, mig_pre_paths[i]);
+ auto_rm.add (mig_pre_paths[i]);
+ mig_pre.push_back (pre);
+
+ open (*post, mig_post_paths[i]);
+ auto_rm.add (mig_post_paths[i]);
+ mig_post.push_back (post);
+ }
+ }
+
+ // Print output file headers.
+ //
+ if (gen_cxx)
+ {
+ hxx << cxx_file_header;
+ ixx << cxx_file_header;
+
+ if (db != database::common)
+ cxx << cxx_file_header;
+ }
+
+ if (gen_sep_schema)
+ sch << cxx_file_header;
+
+ if (gen_sql_schema)
+ sql << sql_file_header;
+
+ if (gen_sql_migration)
+ {
+ for (ofstreams::size_type i (0); i < mig_pre.size (); ++i)
+ {
+ *mig_pre[i] << sql_file_header;
+ *mig_post[i] << sql_file_header;
+ }
+ }
+
+ typedef compiler::ostream_filter<compiler::cxx_indenter, char> ind_filter;
+ typedef compiler::ostream_filter<compiler::sloc_counter, char> sloc_filter;
+
+ size_t sloc_total (0);
+
+ // Include settings.
+ //
+ string gp (ops.guard_prefix ());
+ if (!gp.empty () && gp[gp.size () - 1] != '_')
+ gp.append ("_");
+
+ // HXX
+ //
+ if (gen_cxx)
+ {
+ unique_ptr<context> ctx (
+ create_context (hxx, unit, ops, fts, model.get ()));
+
+ sloc_filter sloc (ctx->os);
+
+ string guard (ctx->make_guard (gp + hxx_name));
+
+ hxx << "#ifndef " << guard << endl
+ << "#define " << guard << endl
+ << endl;
+
+ // Copy prologue.
+ //
+ append_logue (hxx,
+ db,
+ ops.hxx_prologue (),
+ ops.hxx_prologue_file (),
+ "// Begin prologue.\n//",
+ "//\n// End prologue.");
+
+ // Version check (see similar check in odb.cxx for background).
+ //
+ hxx << "#include <odb/version.hxx>" << endl
+ << endl
+#if 1
+ << "#if ODB_VERSION != " << ODB_VERSION << "UL" << endl
+#else
+ << "#if LIBODB_VERSION_FULL != " << ODB_COMPILER_VERSION << "ULL || \\" << endl
+ << " LIBODB_SNAPSHOT != " << ODB_COMPILER_SNAPSHOT << "ULL" << endl
+#endif
+ << "#error ODB runtime version mismatch" << endl
+ << "#endif" << endl
+ << endl;
+
+ hxx << "#include <odb/pre.hxx>" << endl
+ << endl;
+
+ // Include main file(s).
+ //
+ for (paths::const_iterator i (inputs.begin ()); i != inputs.end (); ++i)
+ hxx << "#include " <<
+ ctx->process_include_path (i->leaf ().string ()) << endl;
+
+ hxx << endl;
+
+ // There are no -odb.hxx includes if we are generating code for
+ // everything.
+ //
+ if (!ops.at_once ())
+ if (include::generate (true))
+ hxx << endl;
+
+ {
+ // We don't want to indent prologues/epilogues.
+ //
+ ind_filter ind (ctx->os);
+
+ switch (db)
+ {
+ case database::common:
+ {
+ header::generate ();
+ break;
+ }
+ case database::mssql:
+ case database::mysql:
+ case database::oracle:
+ case database::pgsql:
+ case database::sqlite:
+ {
+ if (md == multi_database::disabled)
+ header::generate ();
+ else
+ {
+ string n (base +
+ ops.odb_file_suffix ()[database::common] +
+ ops.hxx_suffix ());
+
+ ctx->os << "#include " << ctx->process_include_path (n) << endl
+ << endl;
+ }
+
+ relational::header::generate ();
+ break;
+ }
+ }
+ }
+
+ hxx << "#include " << ctx->process_include_path (ixx_name) << endl
+ << endl;
+
+ hxx << "#include <odb/post.hxx>" << endl
+ << endl;
+
+ // Copy epilogue.
+ //
+ append_logue (hxx,
+ db,
+ ops.hxx_epilogue (),
+ ops.hxx_epilogue_file (),
+ "// Begin epilogue.\n//",
+ "//\n// End epilogue.");
+
+ hxx << "#endif // " << guard << endl;
+
+ if (ops.show_sloc ())
+ cerr << hxx_name << ": " << sloc.stream ().count () << endl;
+
+ sloc_total += sloc.stream ().count ();
+ }
+
+ // IXX
+ //
+ if (gen_cxx)
+ {
+ unique_ptr<context> ctx (
+ create_context (ixx, unit, ops, fts, model.get ()));
+
+ sloc_filter sloc (ctx->os);
+
+ // Copy prologue.
+ //
+ append_logue (ixx,
+ db,
+ ops.ixx_prologue (),
+ ops.ixx_prologue_file (),
+ "// Begin prologue.\n//",
+ "//\n// End prologue.");
+
+ {
+ // We don't want to indent prologues/epilogues.
+ //
+ ind_filter ind (ctx->os);
+
+ switch (db)
+ {
+ case database::common:
+ {
+ inline_::generate ();
+ break;
+ }
+ case database::mssql:
+ case database::mysql:
+ case database::oracle:
+ case database::pgsql:
+ case database::sqlite:
+ {
+ if (md == multi_database::disabled)
+ inline_::generate ();
+
+ relational::inline_::generate ();
+ break;
+ }
+ }
+ }
+
+ // Copy epilogue.
+ //
+ append_logue (ixx,
+ db,
+ ops.ixx_epilogue (),
+ ops.ixx_epilogue_file (),
+ "// Begin epilogue.\n//",
+ "//\n// End epilogue.");
+
+ if (ops.show_sloc ())
+ cerr << ixx_name << ": " << sloc.stream ().count () << endl;
+
+ sloc_total += sloc.stream ().count ();
+ }
+
+ // CXX
+ //
+ if (gen_cxx && (db != database::common || md == multi_database::dynamic))
+ {
+ unique_ptr<context> ctx (
+ create_context (cxx, unit, ops, fts, model.get ()));
+
+ sloc_filter sloc (ctx->os);
+
+ // Copy prologue.
+ //
+ append_logue (cxx,
+ db,
+ ops.cxx_prologue (),
+ ops.cxx_prologue_file (),
+ "// Begin prologue.\n//",
+ "//\n// End prologue.");
+
+ cxx << "#include <odb/pre.hxx>" << endl
+ << endl;
+
+ // Include query columns implementations for explicit instantiations.
+ //
+ string impl_guard;
+ if (md == multi_database::dynamic && ctx->ext.empty ())
+ {
+ impl_guard = ctx->make_guard (
+ "ODB_" + db.string () + "_QUERY_COLUMNS_DEF");
+
+ cxx << "#define " << impl_guard << endl;
+ }
+
+ cxx << "#include " << ctx->process_include_path (hxx_name) << endl;
+
+ // There are no -odb.hxx includes if we are generating code for
+ // everything.
+ //
+ if (!ops.at_once ())
+ include::generate (false);
+
+ if (!impl_guard.empty ())
+ cxx << "#undef " << impl_guard << endl;
+
+ cxx << endl;
+
+ {
+ // We don't want to indent prologues/epilogues.
+ //
+ ind_filter ind (ctx->os);
+
+ switch (db)
+ {
+ case database::common:
+ {
+ // Dynamic multi-database support.
+ //
+ source::generate ();
+ break;
+ }
+ case database::mssql:
+ case database::mysql:
+ case database::oracle:
+ case database::pgsql:
+ case database::sqlite:
+ {
+ relational::source::generate ();
+
+ if (gen_schema &&
+ ops.schema_format ()[db].count (schema_format::embedded))
+ relational::schema::generate_source (changelog.get ());
+
+ break;
+ }
+ }
+ }
+
+ cxx << "#include <odb/post.hxx>" << endl;
+
+ // Copy epilogue.
+ //
+ append_logue (cxx,
+ db,
+ ops.cxx_epilogue (),
+ ops.cxx_epilogue_file (),
+ "// Begin epilogue.\n//",
+ "//\n// End epilogue.");
+
+ if (ops.show_sloc ())
+ cerr << cxx_name << ": " << sloc.stream ().count () << endl;
+
+ sloc_total += sloc.stream ().count ();
+ }
+
+ // SCH
+ //
+ if (gen_sep_schema)
+ {
+ unique_ptr<context> ctx (
+ create_context (sch, unit, ops, fts, model.get ()));
+
+ sloc_filter sloc (ctx->os);
+
+ // Copy prologue.
+ //
+ append_logue (sch,
+ db,
+ ops.schema_prologue (),
+ ops.schema_prologue_file (),
+ "// Begin prologue.\n//",
+ "//\n// End prologue.");
+
+ sch << "#include <odb/pre.hxx>" << endl
+ << endl;
+
+ sch << "#include <odb/database.hxx>" << endl
+ << "#include <odb/schema-catalog-impl.hxx>" << endl
+ << endl
+ << "#include <odb/details/unused.hxx>" << endl
+ << endl;
+
+ {
+ // We don't want to indent prologues/epilogues.
+ //
+ ind_filter ind (ctx->os);
+
+ switch (db)
+ {
+ case database::mssql:
+ case database::mysql:
+ case database::oracle:
+ case database::pgsql:
+ case database::sqlite:
+ {
+ relational::schema::generate_source (changelog.get ());
+ break;
+ }
+ case database::common:
+ assert (false);
+ }
+ }
+
+ sch << "#include <odb/post.hxx>" << endl;
+
+ // Copy epilogue.
+ //
+ append_logue (sch,
+ db,
+ ops.schema_epilogue (),
+ ops.schema_epilogue_file (),
+ "// Begin epilogue.\n//",
+ "//\n// End epilogue.");
+
+ if (ops.show_sloc ())
+ cerr << sch_name << ": " << sloc.stream ().count () << endl;
+
+ sloc_total += sloc.stream ().count ();
+ }
+
+ // SQL
+ //
+ if (gen_sql_schema)
+ {
+ unique_ptr<context> ctx (
+ create_context (sql, unit, ops, fts, model.get ()));
+
+ switch (db)
+ {
+ case database::mssql:
+ case database::mysql:
+ case database::oracle:
+ case database::pgsql:
+ case database::sqlite:
+ {
+ // Prologue.
+ //
+ relational::schema::generate_prologue ();
+ append_logue (sql,
+ db,
+ ops.sql_prologue (),
+ ops.sql_prologue_file (),
+ "/* Begin prologue.\n */",
+ "/*\n * End prologue. */");
+
+ if (!ops.omit_drop ())
+ relational::schema::generate_drop ();
+
+ // Interlude.
+ //
+ append_logue (sql,
+ db,
+ ops.sql_interlude (),
+ ops.sql_interlude_file (),
+ "/* Begin interlude.\n */",
+ "/*\n * End interlude. */");
+
+ if (!ops.omit_create ())
+ relational::schema::generate_create ();
+
+ // Epilogue.
+ //
+ append_logue (sql,
+ db,
+ ops.sql_epilogue (),
+ ops.sql_epilogue_file (),
+ "/* Begin epilogue.\n */",
+ "/*\n * End epilogue. */");
+ relational::schema::generate_epilogue ();
+
+ break;
+ }
+ case database::common:
+ assert (false);
+ }
+ }
+
+ // MIG
+ //
+ if (gen_sql_migration)
+ {
+ for (ofstreams::size_type i (0); i < mig_pre.size (); ++i)
+ {
+ sema_rel::changeset& cs (
+ changelog->contains_changeset_at (i).changeset ());
+
+ // pre
+ //
+ {
+ ofstream& mig (*mig_pre[i]);
+ unique_ptr<context> ctx (create_context (mig, unit, ops, fts, 0));
+
+ switch (db)
+ {
+ case database::mssql:
+ case database::mysql:
+ case database::oracle:
+ case database::pgsql:
+ case database::sqlite:
+ {
+ // Prologue.
+ //
+ relational::schema::generate_prologue ();
+ append_logue (mig,
+ db,
+ ops.migration_prologue (),
+ ops.migration_prologue_file (),
+ "/* Begin prologue.\n */",
+ "/*\n * End prologue. */");
+
+ relational::schema::generate_migrate_pre (cs);
+
+ // Epilogue.
+ //
+ append_logue (mig,
+ db,
+ ops.migration_epilogue (),
+ ops.migration_epilogue_file (),
+ "/* Begin epilogue.\n */",
+ "/*\n * End epilogue. */");
+ relational::schema::generate_epilogue ();
+
+ break;
+ }
+ case database::common:
+ assert (false);
+ }
+ }
+
+ // post
+ //
+ {
+ ofstream& mig (*mig_post[i]);
+ unique_ptr<context> ctx (create_context (mig, unit, ops, fts, 0));
+
+ switch (db)
+ {
+ case database::mssql:
+ case database::mysql:
+ case database::oracle:
+ case database::pgsql:
+ case database::sqlite:
+ {
+ // Prologue.
+ //
+ relational::schema::generate_prologue ();
+ append_logue (mig,
+ db,
+ ops.migration_prologue (),
+ ops.migration_prologue_file (),
+ "/* Begin prologue.\n */",
+ "/*\n * End prologue. */");
+
+ relational::schema::generate_migrate_post (cs);
+
+ // Epilogue.
+ //
+ append_logue (mig,
+ db,
+ ops.migration_epilogue (),
+ ops.migration_epilogue_file (),
+ "/* Begin epilogue.\n */",
+ "/*\n * End epilogue. */");
+ relational::schema::generate_epilogue ();
+
+ break;
+ }
+ case database::common:
+ assert (false);
+ }
+ }
+ }
+ }
+
+ // Save the changelog if it has changed.
+ //
+ if (gen_changelog)
+ {
+ try
+ {
+ ostringstream os;
+ os.exceptions (ifstream::badbit | ifstream::failbit);
+ xml::serializer s (os, out_log_path.string ());
+ changelog->serialize (s);
+ string const& changelog_xml (os.str ());
+
+ if (changelog_xml != old_changelog_xml)
+ {
+ ofstream log;
+ open (log, out_log_path, ios_base::binary);
+
+ if (old_changelog == 0)
+ auto_rm.add (out_log_path);
+
+ log.exceptions (ifstream::badbit | ifstream::failbit);
+ log << changelog_xml;
+ }
+ }
+ catch (const ios_base::failure& e)
+ {
+ cerr << out_log_path << ": write failure" << endl;
+ throw generator_failed ();
+ }
+ catch (const xml::serialization& e)
+ {
+ cerr << e.what () << endl;
+ throw generator_failed ();
+ }
+ }
+
+ // Communicate the sloc count to the driver. This is necessary to
+ // correctly handle the total if we are compiling multiple files in
+ // one invocation.
+ //
+ if (ops.show_sloc () || ops.sloc_limit_specified ())
+ cout << "odb:sloc:" << sloc_total << endl;
+
+ auto_rm.cancel ();
+ }
+ catch (operation_failed const&)
+ {
+ // Code generation failed. Diagnostics has already been issued.
+ //
+ throw generator_failed ();
+ }
+ catch (semantics::invalid_path const& e)
+ {
+ cerr << "error: '" << e.path () << "' is not a valid filesystem path"
+ << endl;
+ throw generator_failed ();
+ }
+ catch (fs::error const&)
+ {
+ // Auto-removal of generated files failed. Ignore it.
+ //
+ throw generator_failed ();
+ }
+}