summaryrefslogtreecommitdiff
path: root/odb/odb/include.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'odb/odb/include.cxx')
-rw-r--r--odb/odb/include.cxx738
1 files changed, 738 insertions, 0 deletions
diff --git a/odb/odb/include.cxx b/odb/odb/include.cxx
new file mode 100644
index 0000000..5fda7c0
--- /dev/null
+++ b/odb/odb/include.cxx
@@ -0,0 +1,738 @@
+// file : odb/include.cxx
+// license : GNU GPL v3; see accompanying LICENSE file
+
+#include <odb/gcc.hxx>
+
+#include <set>
+#include <map>
+#include <locale>
+#include <cassert>
+#include <fstream>
+#include <sstream>
+
+#include <odb/common.hxx>
+#include <odb/context.hxx>
+#include <odb/generate.hxx>
+
+#include <iostream>
+
+using namespace std;
+using semantics::path;
+
+namespace
+{
+ struct include_directive
+ {
+ enum type { quote, bracket };
+
+ type type_;
+ path path_;
+ };
+
+#if BUILDING_GCC_MAJOR >= 6
+ typedef line_map_ordinary line_map_type;
+#else
+ typedef line_map line_map_type;
+# ifndef linemap_check_ordinary
+# define linemap_check_ordinary(X) (X)
+# endif
+#endif
+
+ struct includes
+ {
+ typedef std::map<line_map_type const*, include_directive> map_type;
+ bool trailing; // Included at the beginning or at the end of the main file.
+ map_type map;
+ };
+ typedef std::map<path, includes> include_map;
+
+ // Map of files to the lines which contain include directives
+ // that we are interested in.
+ //
+ typedef std::map<size_t, include_directive*> include_lines;
+ typedef std::map<string, include_lines> file_map;
+
+ // Set of include directives sorted in the preference order.
+ //
+ struct include_comparator
+ {
+ bool
+ operator() (include_directive const* x, include_directive const* y) const
+ {
+ // Prefer <> over "".
+ //
+ if (x->type_ != y->type_)
+ return x->type_ < y->type_;
+
+ // Otherwise, prefer longer (more qualified) paths over the
+ // shorter ones.
+ //
+ return x->path_.string ().size () < y->path_.string ().size ();
+ }
+ };
+
+ typedef
+ std::multiset<include_directive const*, include_comparator>
+ include_set;
+
+ struct class_: traversal::class_, context
+ {
+ class_ (include_map& map)
+ : typedefs_ (true), main_file_loc_ (0), map_ (map)
+ {
+ *this >> defines_ >> *this;
+ *this >> typedefs_ >> *this;
+ }
+
+ virtual void
+ traverse (type& c)
+ {
+ class_kind_type ck (class_kind (c));
+
+ if (ck == class_other)
+ return;
+
+ names (c); // Check nested classes.
+
+ // We only generate things for objects and composite value types. In
+ // particular, we don't care about views since they cannot be used in
+ // definitions of other views, objects, or composite values.
+ //
+ if (ck != class_object && ck != class_composite)
+ return;
+
+ // Not interested in classes that we are generating.
+ //
+ // If we have an explicit definition location, use that. Otherwise,
+ // if this is a class template instantiation, then get the file
+ // corresponding to the pragma, not the instantiation itself,
+ // since that's where we are generation the code for this class.
+ // While at it, also get the location.
+ //
+ using semantics::path;
+
+ path f;
+ location_t l;
+
+ // Pretty much the same code as in context::class_location().
+ //
+ if (c.count ("definition"))
+ {
+ l = c.get<location_t> ("definition");
+ f = path (LOCATION_FILE (l));
+ }
+ else if (c.is_a<semantics::class_instantiation> ())
+ {
+ l = c.get<location_t> ("location");
+ f = path (LOCATION_FILE (l));
+ }
+ else
+ {
+ f = c.file ();
+ tree decl (TYPE_NAME (c.tree_node ()));
+ l = real_source_location (decl);
+
+ // Any include directives that follow are trailing (specified at
+ // the end of the main file). Note that we ignore views in this
+ // test so if a file defines only views, then all includes will
+ // be treated as leading. This is ok since views cannot have
+ // circular dependencies. We also ignore overridden locations for
+ // the purpose of this test since they are not really in the file
+ // being compiled. We assume that any includes that come after
+ // such classes are still leading.
+ //
+ if (f == unit.file ())
+ {
+ if (main_file_loc_ == 0)
+ main_file_loc_ = l;
+ return;
+ }
+ }
+
+ // This is a persistent object or composite value type declared in
+ // another header file. Include its -odb header.
+ //
+ if (l > BUILTINS_LOCATION)
+ {
+ line_map_type const* lm (
+ linemap_check_ordinary (
+ linemap_lookup (line_table, l)));
+
+ if (lm != 0 && !MAIN_FILE_P (lm))
+ {
+ lm = INCLUDED_FROM (line_table, lm);
+
+ f.complete ();
+ f.normalize ();
+
+ if (map_.find (f) == map_.end ())
+ {
+ includes& i (map_[f]);
+ i.trailing = (main_file_loc_ != 0 && l > main_file_loc_);
+ i.map[lm] = include_directive ();
+ }
+ }
+ }
+ }
+
+ private:
+ traversal::defines defines_;
+ typedefs typedefs_;
+
+ location_t main_file_loc_;
+ include_map& map_;
+ };
+
+ class include_parser
+ {
+ public:
+ include_parser (options const& options)
+ : loc_ ("C"), options_ (options)
+ {
+ }
+
+ void
+ parse_file (string const& file, include_lines& lines)
+ {
+ string f (file);
+ size_t n (f.size ());
+ database db (options_.database ()[0]);
+
+ // Check if we have a synthesized prologue/epilogue fragment.
+ //
+ if (n != 0 && f[0] == '<' && f[n - 1] == '>')
+ {
+ size_t p (f.rfind ('-'));
+
+ if (p != string::npos)
+ {
+ string name (f, 1, p - 1);
+
+ if (name == "odb-prologue" || name == "odb-epilogue")
+ {
+ // Extract the fragment number.
+ //
+ {
+ istringstream istr (string (f, p + 1));
+ istr >> n;
+ }
+
+ n--; // Prologues/epilogues are counted from 1.
+
+ stringstream ss;
+ f.clear ();
+
+ // We don't need the #line part.
+ //
+ if (name == "odb-prologue")
+ {
+ size_t size (options_.odb_prologue ().size ());
+
+ if (n < size)
+ ss << options_.odb_prologue ()[db][n];
+ else
+ f = options_.odb_prologue_file ()[db][n - size];
+ }
+ else
+ {
+ size_t size (options_.odb_epilogue ().size ());
+
+ if (n < size)
+ ss << options_.odb_epilogue ()[db][n];
+ else
+ f = options_.odb_epilogue_file ()[db][n - size];
+ }
+
+ if (f.empty ())
+ {
+ parse_stream (ss, file, lines);
+ return;
+ }
+ // Otherwise use the code below to parse the file.
+ }
+ }
+ }
+
+ ifstream is (f.c_str ());
+
+ if (!is.is_open ())
+ {
+ cerr << "error: unable to open '" << f << "' in read mode" << endl;
+ throw operation_failed ();
+ }
+
+ parse_stream (is, f, lines);
+ }
+
+ void
+ parse_stream (istream& is, string const& name, include_lines& lines)
+ {
+ typedef char_traits<char>::int_type int_type;
+
+ size_t lmax (lines.rbegin ()->first);
+
+ string line;
+ bool bslash (false);
+ size_t lb (1), le (1);
+ bool eof (false);
+
+ for (int_type c (is.get ()); !eof; c = is.get ())
+ {
+ if (is.fail ())
+ {
+ if (is.eof ())
+ {
+ // If we are still in the range, treat this as the last newline.
+ //
+ c = '\n';
+ eof = true;
+ }
+ else
+ break; // Some other failure -- bail out.
+ }
+
+ if (c == '\n')
+ {
+ le++;
+
+ if (!bslash)
+ {
+ //cerr << "line: " << lb << "-" << (le - 1) << " " << line << endl;
+
+ // See if we are interested in this range of physical lines.
+ //
+ include_lines::iterator li (lines.lower_bound (lb));
+ include_lines::iterator ui (lines.upper_bound (le - 1));
+
+ // We should have at most one entry per logical line.
+ //
+ for (; li != ui; ++li)
+ {
+ if (li->first >= lb && li->first <= (le - 1))
+ {
+ if (!parse_line (line, *li->second))
+ {
+ cerr << name << ":" << lb << ":1: error: "
+ << "unable to parse #include directive" << endl;
+ throw operation_failed ();
+ }
+ }
+ }
+
+ if (le > lmax)
+ break;
+
+ lb = le;
+ line.clear ();
+ }
+
+ bslash = false;
+ continue;
+ }
+
+ if (bslash)
+ {
+ line += '\\';
+ bslash = false;
+ }
+
+ if (c == '\\')
+ bslash = true;
+ else
+ {
+ line += char (c);
+ }
+ }
+
+ if (is.bad () || (is.fail () && !is.eof ()))
+ {
+ cerr << "error: input error while reading '" << name << "'" << endl;
+ throw operation_failed ();
+ }
+ }
+
+ private:
+ bool
+ parse_line (string const& l, include_directive& inc)
+ {
+ enum state
+ {
+ start_hash,
+ start_keyword,
+ parse_keyword,
+ start_path,
+ parse_path,
+ parse_done
+ };
+
+ bool com (false); // In C-style comment.
+ string lex;
+ char path_end ('\0');
+ state s (start_hash);
+
+ for (size_t i (0), n (l.size ()); i < n; ++i)
+ {
+ char c (l[i]);
+
+ if (com)
+ {
+ if (c == '*' && (i + 1) < n && l[i + 1] == '/')
+ {
+ ++i;
+ com = false;
+ c = ' '; // Replace a comment with a single space.
+ }
+ else
+ continue;
+ }
+
+ // We only ignore spaces in start states.
+ //
+ if (is_space (c))
+ {
+ switch (s)
+ {
+ case start_hash:
+ case start_keyword:
+ case start_path:
+ {
+ continue;
+ }
+ default:
+ {
+ break;
+ }
+ }
+ }
+
+ // C comment can be anywhere except in the path.
+ //
+ if (s != parse_path && c == '/' && (i + 1) < n && l[i + 1] == '*')
+ {
+ ++i;
+ com = true;
+ continue;
+ }
+
+ switch (s)
+ {
+ case start_hash:
+ {
+ if (c != '#')
+ return false;
+
+ s = start_keyword;
+ break;
+ }
+ case start_keyword:
+ {
+ lex.clear ();
+ s = parse_keyword;
+ }
+ // Fall through.
+ case parse_keyword:
+ {
+ if (is_alpha (c))
+ {
+ lex += c;
+ break;
+ }
+
+ if (lex != "include")
+ return false;
+
+ s = start_path;
+ --i; // Re-parse the same character again.
+ break;
+ }
+ case start_path:
+ {
+ if (c == '"')
+ {
+ path_end = '"';
+ inc.type_ = include_directive::quote;
+ }
+ else if (c == '<')
+ {
+ path_end = '>';
+ inc.type_ = include_directive::bracket;
+ }
+ else
+ return false;
+
+ lex.clear ();
+ s = parse_path;
+ break;
+ }
+ case parse_path:
+ {
+ if (c != path_end)
+ lex += c;
+ else
+ s = parse_done;
+
+ break;
+ }
+ default:
+ {
+ assert (false);
+ break;
+ }
+ }
+
+ if (s == parse_done)
+ break;
+ }
+
+ if (s != parse_done)
+ return false;
+
+ inc.path_ = path (lex);
+ return true;
+ }
+
+ private:
+ bool
+ is_alpha (char c) const
+ {
+ return isalpha (c, loc_);
+ }
+
+ bool
+ is_space (char c) const
+ {
+ return isspace (c, loc_);
+ }
+
+ private:
+ std::locale loc_;
+ options const& options_;
+ };
+
+ bool
+ generate_impl (bool header)
+ {
+ bool r (false);
+
+ // We do the same include directive collection and processing
+ // twice, once for the header file and once for the source file.
+ // If that proves to be too slow, we will need to do it only once
+ // and cache the result.
+ //
+ context ctx;
+ include_map imap;
+
+ // Collect all the files that we need to include.
+ //
+ {
+ traversal::unit unit;
+ traversal::defines unit_defines;
+ typedefs unit_typedefs (true);
+ traversal::namespace_ ns;
+ class_ c (imap);
+
+ unit >> unit_defines >> ns;
+ unit_defines >> c;
+ unit >> unit_typedefs >> c;
+
+ traversal::defines ns_defines;
+ typedefs ns_typedefs (true);
+
+ ns >> ns_defines >> ns;
+ ns_defines >> c;
+ ns >> ns_typedefs >> c;
+
+ unit.dispatch (ctx.unit);
+ }
+
+ // Add all the known include locations for each file in the map.
+ //
+#if BUILDING_GCC_MAJOR == 4 && BUILDING_GCC_MINOR <= 6
+ size_t used (line_table->used);
+ line_map_type const* maps (line_table->maps);
+#else
+ size_t used (line_table->info_ordinary.used);
+ line_map_type const* maps (line_table->info_ordinary.maps);
+#endif
+
+ for (size_t i (0); i < used; ++i)
+ {
+ line_map_type const* m (maps + i);
+
+ if (MAIN_FILE_P (m) || m->reason != LC_ENTER)
+ continue;
+
+ line_map_type const* ifm (INCLUDED_FROM (line_table, m));
+
+#if BUILDING_GCC_MAJOR == 4 && BUILDING_GCC_MINOR <= 6
+ path f (m->to_file);
+#else
+ path f (ORDINARY_MAP_FILE_NAME (m));
+#endif
+
+ f.complete ();
+ f.normalize ();
+
+ include_map::iterator it (imap.find (f));
+
+ if (it != imap.end ())
+ it->second.map[ifm] = include_directive ();
+ }
+
+ //
+ //
+ file_map fmap;
+
+ for (include_map::iterator i (imap.begin ()), e (imap.end ()); i != e; ++i)
+ {
+ // Note that the LAST_SOURCE_LINE value of a map that includes another
+ // map is the line of that include.
+
+ /*
+ cerr << endl
+ << i->first << " included from" << endl;
+
+ for (includes::iterator j (i->second.begin ());
+ j != i->second.end (); ++j)
+ {
+ line_map_type const* lm (j->first);
+ cerr << '\t' << lm->to_file << ":" << LAST_SOURCE_LINE (lm) << endl;
+ }
+ */
+
+ // First see if there is an include from the main file. If so, then
+ // it is preferred over all others. Use the first one if there are
+ // several.
+ //
+ line_map_type const* main_lm (0);
+ include_directive* main_inc (0);
+
+ for (includes::map_type::iterator j (i->second.map.begin ());
+ j != i->second.map.end (); ++j)
+ {
+ line_map_type const* lm (j->first);
+
+ if (MAIN_FILE_P (lm))
+ {
+ if (main_lm == 0 ||
+ LAST_SOURCE_LINE (main_lm) > LAST_SOURCE_LINE (lm))
+ {
+ main_lm = lm;
+ main_inc = &j->second;
+ }
+ }
+ }
+
+ if (main_lm != 0)
+ {
+#if BUILDING_GCC_MAJOR == 4 && BUILDING_GCC_MINOR <= 6
+ string f (main_lm->to_file);
+#else
+ string f (ORDINARY_MAP_FILE_NAME (main_lm));
+#endif
+ size_t n (f.size ());
+
+ // Check if this is a synthesized fragment.
+ //
+ if (!(n != 0 && f[0] == '<' && f[n - 1] == '>'))
+ {
+ path p (f);
+ p.complete ();
+ p.normalize ();
+ f = p.string ();
+ }
+
+ fmap[f][LAST_SOURCE_LINE (main_lm)] = main_inc;
+ continue;
+ }
+
+ // Otherwise, add all the entries.
+ //
+ for (includes::map_type::iterator j (i->second.map.begin ());
+ j != i->second.map.end (); ++j)
+ {
+ line_map_type const* lm (j->first);
+
+#if BUILDING_GCC_MAJOR == 4 && BUILDING_GCC_MINOR <= 6
+ string f (lm->to_file);
+#else
+ string f (ORDINARY_MAP_FILE_NAME (lm));
+#endif
+ size_t n (f.size ());
+
+ // Check if this is a synthesized fragment.
+ //
+ if (!(n != 0 && f[0] == '<' && f[n - 1] == '>'))
+ {
+ path p (f);
+ p.complete ();
+ p.normalize ();
+ f = p.string ();
+ }
+
+ fmap[f][LAST_SOURCE_LINE (lm)] = &j->second;
+ }
+ }
+
+ // Parse the collected include directives.
+ //
+ include_parser ip (ctx.options);
+
+ for (file_map::iterator i (fmap.begin ()), e (fmap.end ()); i != e; ++i)
+ {
+ ip.parse_file (i->first, i->second);
+ }
+
+ // Finally, output the include directives.
+ //
+ for (include_map::const_iterator i (imap.begin ()), e (imap.end ());
+ i != e; ++i)
+ {
+ includes const& is (i->second);
+
+ // In header we generate only leading includes. In source -- only
+ // trailing.
+ //
+ if (header == is.trailing)
+ continue;
+
+ include_directive const* inc (0);
+
+ if (is.map.size () == 1)
+ {
+ inc = &is.map.begin ()->second;
+ }
+ else
+ {
+ include_set set;
+
+ for (includes::map_type::const_iterator j (i->second.map.begin ());
+ j != i->second.map.end (); ++j)
+ {
+ if (!j->second.path_.empty ())
+ set.insert (&j->second);
+ }
+
+ assert (set.size () > 0);
+ inc = *set.rbegin ();
+ }
+
+ path f (inc->path_.base ());
+ f += ctx.options.odb_file_suffix ()[ctx.options.database ()[0]];
+ f += ctx.options.hxx_suffix ();
+
+ char o (inc->type_ == include_directive::quote ? '"' : '<');
+ ctx.os << "#include " << ctx.process_include_path (
+ f.string (), false, o) << endl;
+ r = true;
+ }
+
+ return r;
+ }
+}
+
+namespace include
+{
+ bool
+ generate (bool header) {return generate_impl (header);}
+}