aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBoris Kolpackov <boris@codesynthesis.com>2015-01-26 10:57:53 +0200
committerBoris Kolpackov <boris@codesynthesis.com>2015-01-26 10:57:53 +0200
commit099385af3eed20549ced40b3e76644ad57fe31d5 (patch)
tree2e08f9f6b73d108f7bb1a11e27cbc440beffa1fb
parentc67f2b63a9769d8ae3a07f78afd0c7dd2873563a (diff)
Make #pragma once work
-rw-r--r--NEWS3
-rw-r--r--odb/include.cxx1
-rw-r--r--odb/plugin.cxx86
3 files changed, 86 insertions, 4 deletions
diff --git a/NEWS b/NEWS
index ccbedde..2aec85e 100644
--- a/NEWS
+++ b/NEWS
@@ -52,6 +52,9 @@ Version 2.4.0
names that are used by PostgreSQL. This can be useful, for example, to
shorten names that exceed the PostgreSQL name limit.
+ * ODB can now compile headers that use #pragma once instead of include
+ guards.
+
Version 2.3.0
* Support for database schema evolution, including schema migration, data
diff --git a/odb/include.cxx b/odb/include.cxx
index 3d3beb8..44c9ad6 100644
--- a/odb/include.cxx
+++ b/odb/include.cxx
@@ -67,7 +67,6 @@ namespace
std::multiset<include_directive const*, include_comparator>
include_set;
-
struct class_: traversal::class_, context
{
class_ (include_map& map)
diff --git a/odb/plugin.cxx b/odb/plugin.cxx
index 20c3fb6..92e0a71 100644
--- a/odb/plugin.cxx
+++ b/odb/plugin.cxx
@@ -4,10 +4,14 @@
#include <odb/gcc.hxx> // Keep it first.
+#include <unistd.h> // stat()
+#include <sys/types.h> // stat
+#include <sys/stat.h> // stat
+
#include <memory> // std::auto_ptr
#include <string>
#include <vector>
-#include <cstring> // std::strcpy
+#include <cstring> // std::strcpy, std::strstr
#include <cassert>
#include <iostream>
@@ -40,6 +44,30 @@ paths profile_paths_;
path file_; // File being compiled.
paths inputs_; // List of input files in at-once mode or just file_.
+bool (*cpp_error_prev) (
+ cpp_reader*, int, int, location_t, unsigned int, const char*, va_list*);
+
+static bool
+cpp_error_filter (cpp_reader* r,
+ int level,
+ int reason,
+ location_t l,
+ unsigned int column_override,
+ const char* msg,
+ va_list* ap)
+{
+ // #pragma once in the main file. Note that the message that we get is
+ // potentially translated so we search for the substring (there is
+ // currently only one warning in libcpp that mentions #pragma once).
+ // Unfortunately, some translations translate the 'once' word (e.g,
+ // #pragma uno; I wonder if one can actually specify it like that in
+ // the source code). Oh, well, there is only so much we can do.
+ //
+ if (strstr (msg, "#pragma once") != 0)
+ return true;
+
+ return cpp_error_prev (r, level, reason, l, column_override, msg, ap);
+}
// 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
@@ -51,26 +79,52 @@ struct cpp_file_prefix
char const* path;
char const* pchname;
char const* dir_name;
+ _cpp_file* next_file;
+ const uchar* buffer;
+ const uchar* buffer_start;
+ const cpp_hashnode *cmacro;
+ cpp_dir *dir;
+ struct stat st;
};
extern "C" void
start_unit_callback (void*, void*)
{
+ // Set the preprocessor error callback to filter out useless diagnostics.
+ //
+ cpp_callbacks* cb (cpp_get_callbacks (parse_in));
+
+ if (cb->error == 0)
+ {
+ cerr << "ice: expected cpp error callback to be set" << endl;
+ exit (1);
+ }
+
+ cpp_error_prev = cb->error;
+ cb->error = &cpp_error_filter;
+
// Set the directory of the main file (stdin) to that of the orginal
- // file.
+ // file so that relative inclusion works. Also adjust the path and
+ // re-stat the file so that #pragma once works.
//
cpp_buffer* b (cpp_get_buffer (parse_in));
_cpp_file* f (cpp_get_file (b));
+ cpp_dir* d (cpp_get_dir (f));
char const* p (cpp_get_path (f));
+
cpp_file_prefix* fp (reinterpret_cast<cpp_file_prefix*> (f));
- // Perform sanity checks.
+ // Perform some sanity checks.
//
if (p != 0 && *p == '\0' // The path should be empty (stdin).
&& cpp_get_prev (b) == 0 // This is the only buffer (main file).
&& fp->path == p // Our prefix corresponds to the actual type.
+ && fp->dir == d // Our prefix corresponds to the actual type.
&& fp->dir_name == 0) // The directory part hasn't been initialized.
{
+ // The dir_name is initialized by libcpp lazily so we can preemptively
+ // set it to what we need.
+ //
path d (file_.directory ());
char* s;
@@ -89,6 +143,32 @@ start_unit_callback (void*, void*)
}
fp->dir_name = s;
+
+ // Unless we are in the at-once mode (where input files are actually
+ // #include'ed into the synthesized stdin), pretend that we are the
+ // actual input file. This is necessary for the #pragma once to work.
+ //
+ // All this relies on the way things are implemented in libcpp. In
+ // particular, the #pragma once code first checks if the mtime and
+ // size of files match (that's why we need stat() below). If they
+ // do, then it goes ahead and compares their contents. To re-load
+ // the contents of the file libcpp uses the path (that's why we
+ // need to adjust that as well).
+ //
+ if (inputs_.size () == 1)
+ {
+ string const& f (file_.string ());
+
+ XDELETEVEC (fp->path);
+ size_t n (f.size ());
+ char* p (XNEWVEC (char, n + 1));
+ strcpy (p, f.c_str ());
+ p[n] = '\0';
+ fp->path = p;
+
+ // This call shouldn't fail since we've just opened it in the driver.
+ stat (fp->path, &fp->st);
+ }
}
else
{