From c0931400a1c5f02cf145c90fd7e34216836efd88 Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Wed, 12 Sep 2012 14:28:03 +0200 Subject: Implement --output-name, --generate-schema-only, and --at-once options --- NEWS | 13 +++++ odb/common.cxx | 2 +- odb/context.cxx | 25 -------- odb/context.hxx | 8 --- odb/generator.cxx | 120 ++++++++++++++++++++++++++------------- odb/generator.hxx | 5 +- odb/header.cxx | 8 ++- odb/odb.cxx | 86 ++++++++++++++++++++-------- odb/option-functions.cxx | 6 ++ odb/options.cli | 26 +++++++++ odb/plugin.cxx | 18 +++++- odb/relational/header.hxx | 4 +- odb/relational/inline.hxx | 2 +- odb/relational/model.hxx | 2 +- odb/relational/schema-source.hxx | 2 +- odb/relational/source.hxx | 2 +- odb/validator.cxx | 19 ++++++- 17 files changed, 236 insertions(+), 112 deletions(-) diff --git a/NEWS b/NEWS index 1e06550..ad93c1f 100644 --- a/NEWS +++ b/NEWS @@ -68,6 +68,19 @@ Version 2.1.0 "PostgreSQL Type Mapping" and 17.1, "SQL Server Type Mapping" in the ODB manual. + * New option, --output-name, specifies the alternative base name used to + construct the names of the generated files. Refer to the ODB compiler + command line interface documentation (man pages) for details. + + * New option, --generate-schema-only, instructs the ODB compiler to + generate the database schema only. Refer to the ODB compiler command + line interface documentation (man pages) for details. + + * New option, --at-once, triggers the generation of code for all the input + files as well as for all the files that they include at once. Refer to + the ODB compiler command line interface documentation (man pages) for + details. + * The id() pragma that was used to declare a persistent class without an object id has been renamed to no_id. diff --git a/odb/common.cxx b/odb/common.cxx index 667afc0..be8e6c5 100644 --- a/odb/common.cxx +++ b/odb/common.cxx @@ -599,7 +599,7 @@ check (semantics::typedefs& t) // if (!included_) { - if (class_file (*ci) != unit.file ()) + if (!options.at_once () && class_file (*ci) != unit.file ()) return false; } diff --git a/odb/context.cxx b/odb/context.cxx index fab5432..099b10b 100644 --- a/odb/context.cxx +++ b/odb/context.cxx @@ -2100,28 +2100,3 @@ process_include_path (string const& ip, bool prefix, char open) return r; } - -// namespace -// - -void namespace_:: -traverse (type& ns) -{ - // Only traverse namespaces from the main file. - // - if (ns.file () == unit.file ()) - { - string name (ns.name ()); - - if (name.empty ()) - os << "namespace"; - else - os << "namespace " << name; - - os << "{"; - - traversal::namespace_::traverse (ns); - - os << "}"; - } -} diff --git a/odb/context.hxx b/odb/context.hxx index dc5eb05..8ae110a 100644 --- a/odb/context.hxx +++ b/odb/context.hxx @@ -1050,12 +1050,4 @@ has (Y& y) return false; } -// Standard namespace traverser. -// -struct namespace_: traversal::namespace_, context -{ - virtual void - traverse (type&); -}; - #endif // ODB_CONTEXT_HXX diff --git a/odb/generator.cxx b/odb/generator.cxx index 26efedf..630d320 100644 --- a/odb/generator.cxx +++ b/odb/generator.cxx @@ -25,6 +25,7 @@ using namespace std; using namespace cutl; using semantics::path; +typedef vector paths; namespace { @@ -67,7 +68,8 @@ void generator:: generate (options const& ops, features& fts, semantics::unit& unit, - path const& p) + path const& p, + paths const& inputs) { try { @@ -95,7 +97,9 @@ generate (options const& ops, // Output files. // - path file (p.leaf ()); + path file (ops.output_name ().empty () + ? p.leaf () + : path (ops.output_name ()).leaf ()); string base (file.base ().string ()); fs::auto_removes auto_rm; @@ -122,52 +126,69 @@ generate (options const& ops, sql_path = dir / sql_path; } + bool gen_cxx (!ops.generate_schema_only ()); + // // - ofstream hxx (hxx_path.string ().c_str ()); + ofstream hxx; - if (!hxx.is_open ()) + if (gen_cxx) { - cerr << "error: unable to open '" << hxx_path << "' in write mode" - << endl; - throw failed (); - } + hxx.open (hxx_path.string ().c_str (), ios_base::out); + + if (!hxx.is_open ()) + { + cerr << "error: unable to open '" << hxx_path << "' in write mode" + << endl; + throw failed (); + } - auto_rm.add (hxx_path); + auto_rm.add (hxx_path); + } // // - ofstream ixx (ixx_path.string ().c_str ()); + ofstream ixx; - if (!ixx.is_open ()) + if (gen_cxx) { - cerr << "error: unable to open '" << ixx_path << "' in write mode" - << endl; - throw failed (); - } + ixx.open (ixx_path.string ().c_str (), ios_base::out); + + if (!ixx.is_open ()) + { + cerr << "error: unable to open '" << ixx_path << "' in write mode" + << endl; + throw failed (); + } - auto_rm.add (ixx_path); + auto_rm.add (ixx_path); + } // // - ofstream cxx (cxx_path.string ().c_str ()); + ofstream cxx; - if (!cxx.is_open ()) + if (gen_cxx) { - cerr << "error: unable to open '" << cxx_path << "' in write mode" - << endl; - throw failed (); - } + cxx.open (cxx_path.string ().c_str (), ios_base::out); - auto_rm.add (cxx_path); + if (!cxx.is_open ()) + { + cerr << "error: unable to open '" << cxx_path << "' in write mode" + << endl; + throw failed (); + } + + auto_rm.add (cxx_path); + } // // - bool sql_schema (ops.generate_schema () && - ops.schema_format ().count (schema_format::sql)); + bool gen_sql_schema (ops.generate_schema () && + ops.schema_format ().count (schema_format::sql)); ofstream sql; - if (sql_schema) + if (gen_sql_schema) { sql.open (sql_path.string ().c_str (), ios_base::out); @@ -183,12 +204,13 @@ generate (options const& ops, // // - bool sep_schema (ops.generate_schema () && - ops.schema_format ().count (schema_format::separate)); + bool gen_sep_schema (gen_cxx && + ops.generate_schema () && + ops.schema_format ().count (schema_format::separate)); ofstream sch; - if (sep_schema) + if (gen_sep_schema) { sch.open (sch_path.string ().c_str (), ios_base::out); @@ -204,11 +226,14 @@ generate (options const& ops, // Print C++ headers. // - hxx << file_header; - ixx << file_header; - cxx << file_header; + if (gen_cxx) + { + hxx << file_header; + ixx << file_header; + cxx << file_header; + } - if (sep_schema) + if (gen_sep_schema) sch << file_header; typedef compiler::ostream_filter ind_filter; @@ -224,6 +249,7 @@ generate (options const& ops, // HXX // + if (gen_cxx) { auto_ptr ctx ( create_context (hxx, unit, ops, fts, model.get ())); @@ -257,15 +283,25 @@ generate (options const& ops, << "// End prologue." << endl << endl; - hxx << "#include " << ctx->process_include_path (file.string ()) << endl - << endl; + // Include main file(s). + // + for (paths::const_iterator i (inputs.begin ()); i != inputs.end (); ++i) + hxx << "#include " << ctx->process_include_path (i->string ()) << endl; + + hxx << endl; + { // We don't want to indent prologues/epilogues. // ind_filter ind (ctx->os); - include::generate (true); + // There are no -odb.hxx includes if we are generating code for + // everything. + // + if (!ops.at_once ()) + include::generate (true); + header::generate (); switch (ops.database ()) @@ -307,6 +343,7 @@ generate (options const& ops, // IXX // + if (gen_cxx) { auto_ptr ctx ( create_context (ixx, unit, ops, fts, model.get ())); @@ -359,6 +396,7 @@ generate (options const& ops, // CXX // + if (gen_cxx) { auto_ptr ctx ( create_context (cxx, unit, ops, fts, model.get ())); @@ -385,7 +423,11 @@ generate (options const& ops, // ind_filter ind (ctx->os); - include::generate (false); + // There are no -odb.hxx includes if we are generating code for + // everything. + // + if (!ops.at_once ()) + include::generate (false); switch (ops.database ()) { @@ -420,7 +462,7 @@ generate (options const& ops, // SCH // - if (sep_schema) + if (gen_sep_schema) { auto_ptr ctx ( create_context (sch, unit, ops, fts, model.get ())); @@ -482,7 +524,7 @@ generate (options const& ops, // SQL // - if (sql_schema) + if (gen_sql_schema) { auto_ptr ctx ( create_context (sql, unit, ops, fts, model.get ())); diff --git a/odb/generator.hxx b/odb/generator.hxx index 5a1bbdf..98454a3 100644 --- a/odb/generator.hxx +++ b/odb/generator.hxx @@ -5,6 +5,8 @@ #ifndef ODB_GENERATOR_HXX #define ODB_GENERATOR_HXX +#include + #include #include #include @@ -18,7 +20,8 @@ public: generate (options const&, features&, semantics::unit&, - semantics::path const&); + semantics::path const& file, + std::vector const& inputs); generator () {} diff --git a/odb/header.cxx b/odb/header.cxx index 8e6718d..8bf5453 100644 --- a/odb/header.cxx +++ b/odb/header.cxx @@ -17,8 +17,12 @@ namespace header ostream& os (ctx.os); os << "#include " << endl - << "#include " << endl // std::size_t - << endl; + << "#include " << endl; // std::size_t + + if (ctx.features.polymorphic_object) + os << "#include " << endl; // For discriminator. + + os << endl; os << "#include " << endl << "#include " << endl diff --git a/odb/odb.cxx b/odb/odb.cxx index d89cd3e..4f48359 100644 --- a/odb/odb.cxx +++ b/odb/odb.cxx @@ -56,7 +56,7 @@ typedef vector paths; // Escape backslashes in the path. // static string -escape_path (path const& p); +escape_path (string const&); // Search the PATH environment variable for the file. // @@ -664,6 +664,23 @@ main (int argc, char* argv[]) size_t svc_file_pos (args.size ()); args.push_back (""); + // If compiling multiple input files at once, pass them also with + // the --svc-file option. + // + bool at_once (ops.at_once () && plugin_args.size () - end > 1); + if (at_once) + { + if (ops.output_name ().empty ()) + { + e << "error: --output-name required when compiling multiple " << + "input files at once (--at-once)" << endl; + return 1; + } + + for (size_t i (end); i < plugin_args.size (); ++i) + args.push_back (encode_plugin_option ("svc-file", plugin_args[i])); + } + // Create an execvp-compatible argument array. // typedef vector cstrings; @@ -684,26 +701,31 @@ main (int argc, char* argv[]) for (; end < plugin_args.size (); ++end) { - path input (plugin_args[end]); + string name (at_once ? ops.output_name () : plugin_args[end]); // Set the --svc-file option. // - args[svc_file_pos] = encode_plugin_option ("svc-file", input.string ()); + args[svc_file_pos] = encode_plugin_option ("svc-file", name); exec_args[svc_file_pos] = args[svc_file_pos].c_str (); // // - ifstream ifs (input.string ().c_str (), ios_base::in | ios_base::binary); + ifstream ifs; - if (!ifs.is_open ()) + if (!at_once) { - e << input << ": error: unable to open in read mode" << endl; - return 1; + ifs.open (name.c_str (), ios_base::in | ios_base::binary); + + if (!ifs.is_open ()) + { + e << name << ": error: unable to open in read mode" << endl; + return 1; + } } if (v) { - e << "Compiling " << input << endl; + e << "Compiling " << name << endl; for (cstrings::const_iterator i (exec_args.begin ()); i != exec_args.end (); ++i) { @@ -796,21 +818,36 @@ main (int argc, char* argv[]) os << endl; } - // Write the synthesized translation unit to stdout. - // - os << "#line 1 \"" << escape_path (input) << "\"" << endl; - - if (!(os << ifs.rdbuf ())) + if (at_once) { - e << input << ": error: io failure" << endl; - fb.close (); - wait_process (pi, argv[0]); - return 1; + // Include all the input files (no need to escape). + // + os << "#line 1 \"\"" << endl; + + bool b (ops.include_with_brackets ()); + char op (b ? '<' : '"'), cl (b ? '>' : '"'); + + for (; end < plugin_args.size (); ++end) + os << "#include " << op << plugin_args[end] << cl << endl; } + else + { + // Write the synthesized translation unit to stdout. + // + os << "#line 1 \"" << escape_path (name) << "\"" << endl; - // Add a new line in case the input file doesn't end with one. - // - os << endl; + if (!(os << ifs.rdbuf ())) + { + e << name << ": error: io failure" << endl; + fb.close (); + wait_process (pi, argv[0]); + return 1; + } + + // Add a new line in case the input file doesn't end with one. + // + os << endl; + } // Add custom epilogue if any. // @@ -1187,17 +1224,16 @@ profile_paths (strings const& sargs, char const* name) // static string -escape_path (path const& p) +escape_path (string const& p) { string r; - string const& s (p.string ()); - for (size_t i (0); i < s.size (); ++i) + for (size_t i (0); i < p.size (); ++i) { - if (s[i] == '\\') + if (p[i] == '\\') r += "\\\\"; else - r += s[i]; + r += p[i]; } return r; diff --git a/odb/option-functions.cxx b/odb/option-functions.cxx index 6d6f948..6b7442e 100644 --- a/odb/option-functions.cxx +++ b/odb/option-functions.cxx @@ -11,6 +11,12 @@ using namespace std; void process_options (options& o) { + // If --generate-schema-only was specified, then set --generate-schema + // as well. + // + if (o.generate_schema_only ()) + o.generate_schema (true); + // Set the default schema format depending on the database. // if (o.generate_schema () && o.schema_format ().empty ()) diff --git a/odb/options.cli b/odb/options.cli index 8705283..e9d76dd 100644 --- a/odb/options.cli +++ b/odb/options.cli @@ -81,6 +81,13 @@ class options Use the \cb{--schema-format} option to alter the default schema format." }; + bool --generate-schema-only + { + "Generate only the database schema. Note that this option is only valid + when generating schema as a standalone SQL file (see \cb{--schema-format} + for details)." + }; + std::set< ::schema_format> --schema-format { "", @@ -156,6 +163,18 @@ class options profile." }; + bool --at-once + { + "Generate code for all the input files as well as for all the files that + they include at once. The result is a single set of source/schema files + that contain all the generated code. If more than one input file is + specified together with this option, then the \cb{--output-name} option + must also be specified in order to provide the base name for the output + files. In this case, the directory part of such a base name is used as + the location of the combined file. This can be important for the + \cb{#include} directive resolution." + }; + qname --schema { "", @@ -193,6 +212,13 @@ class options "Write the generated files to instead of the current directory." }; + std::string --output-name + { + "", + "Use instead of the input file to construct the names of the + generated files." + }; + std::string --odb-file-suffix = "-odb" { "", diff --git a/odb/plugin.cxx b/odb/plugin.cxx index 8920521..1d73b7f 100644 --- a/odb/plugin.cxx +++ b/odb/plugin.cxx @@ -37,7 +37,9 @@ typedef vector paths; int plugin_is_GPL_compatible; auto_ptr options_; paths profile_paths_; -path file_; // File being compiled. +path file_; // File being compiled. +paths inputs_; // List of input files in at-once mode or just file_. + // A prefix of the _cpp_file struct. This struct is not part of the // public interface so we have to resort to this technique (based on @@ -136,7 +138,7 @@ gate_callback (void*, void*) // Generate. // generator g; - g.generate (*options_, f, *u, file_); + g.generate (*options_, f, *u, file_, inputs_); } catch (cutl::re::format const& e) { @@ -221,7 +223,14 @@ plugin_init (plugin_name_args* plugin_info, plugin_gcc_version*) if (strcmp (a.key, "svc-file") == 0) { - file_ = path (v); + // First is the main file. Subsequent are inputs in the at-once + // mode. + // + if (file_.empty ()) + file_ = path (v); + else + inputs_.push_back (path (v)); + continue; } @@ -238,6 +247,9 @@ plugin_init (plugin_name_args* plugin_info, plugin_gcc_version*) } } + if (inputs_.empty ()) + inputs_.push_back (file_); + // Two-phase options parsing, similar to the driver. // int argc (static_cast (argv.size ())); diff --git a/odb/relational/header.hxx b/odb/relational/header.hxx index c7a7445..25f9480 100644 --- a/odb/relational/header.hxx +++ b/odb/relational/header.hxx @@ -970,7 +970,7 @@ namespace relational virtual void traverse (type& c) { - if (class_file (c) != unit.file ()) + if (!options.at_once () && class_file (c) != unit.file ()) return; if (object (c)) @@ -1224,7 +1224,7 @@ namespace relational virtual void traverse (type& c) { - if (class_file (c) != unit.file ()) + if (!options.at_once () && class_file (c) != unit.file ()) return; if (object (c)) diff --git a/odb/relational/inline.hxx b/odb/relational/inline.hxx index ff97874..76c29f1 100644 --- a/odb/relational/inline.hxx +++ b/odb/relational/inline.hxx @@ -202,7 +202,7 @@ namespace relational virtual void traverse (type& c) { - if (class_file (c) != unit.file ()) + if (!options.at_once () && class_file (c) != unit.file ()) return; context::top_object = context::cur_object = &c; diff --git a/odb/relational/model.hxx b/odb/relational/model.hxx index b3edd93..4245c91 100644 --- a/odb/relational/model.hxx +++ b/odb/relational/model.hxx @@ -659,7 +659,7 @@ namespace relational virtual void traverse (type& c) { - if (class_file (c) != unit.file ()) + if (!options.at_once () && class_file (c) != unit.file ()) return; if (!object (c)) diff --git a/odb/relational/schema-source.hxx b/odb/relational/schema-source.hxx index 82b7e71..925e914 100644 --- a/odb/relational/schema-source.hxx +++ b/odb/relational/schema-source.hxx @@ -21,7 +21,7 @@ namespace relational virtual void traverse (type& c) { - if (class_file (c) != unit.file ()) + if (!options.at_once () && class_file (c) != unit.file ()) return; if (!object (c)) diff --git a/odb/relational/source.hxx b/odb/relational/source.hxx index 21954e0..1b5df6e 100644 --- a/odb/relational/source.hxx +++ b/odb/relational/source.hxx @@ -3340,7 +3340,7 @@ namespace relational virtual void traverse (type& c) { - if (class_file (c) != unit.file ()) + if (!options.at_once () && class_file (c) != unit.file ()) return; context::top_object = context::cur_object = &c; diff --git a/odb/validator.cxx b/odb/validator.cxx index 225a2a1..aba82a5 100644 --- a/odb/validator.cxx +++ b/odb/validator.cxx @@ -573,7 +573,7 @@ namespace // Update features set based on this object. // - if (class_file (c) == unit.file ()) + if (options.at_once () || class_file (c) == unit.file ()) { if (poly_root != 0) features.polymorphic_object = true; @@ -706,7 +706,7 @@ namespace // Update features set based on this view. // - if (class_file (c) == unit.file ()) + if (options.at_once () || class_file (c) == unit.file ()) { features.view = true; } @@ -1251,6 +1251,21 @@ validate (options const& ops, unsigned short pass) { bool valid (true); + + // Validate options. + // + if (ops.generate_schema_only () && + (ops.schema_format ().size () != 1 || + *ops.schema_format ().begin () != schema_format::sql)) + { + cerr << "error: --generate-schema-only is only valid when generating " << + "schema as a standalone SQL file" << endl; + valid = false; + } + + if (!valid) + throw failed (); + auto_ptr ctx (create_context (cerr, u, ops, f, 0)); if (pass == 1) -- cgit v1.1