aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--build2/cc/compile-rule.cxx123
-rw-r--r--tests/cc/modules/headers.testscript35
2 files changed, 110 insertions, 48 deletions
diff --git a/build2/cc/compile-rule.cxx b/build2/cc/compile-rule.cxx
index a957553..f30ea3b 100644
--- a/build2/cc/compile-rule.cxx
+++ b/build2/cc/compile-rule.cxx
@@ -1736,12 +1736,15 @@ namespace build2
//
else if (command ("INCLUDE") || command ("IMPORT"))
{
- // INCLUDE [<"]<name>[>"] <path>
- // IMPORT [<"]<name>[>"] <path>
+ // INCLUDE [<"']<name>[>"'] <path>
+ // IMPORT [<"']<name>[>"'] <path>
+ // IMPORT '<path>'
//
// <path> is the resolved path or empty if the header is not found.
// It can be relative if it is derived from a relative path (either
- // via -I or includer).
+ // via -I or includer). If <name> is single-quoted, then it cannot
+ // be re-searched (e.g., implicitly included stdc-predef.h) and in
+ // this case <path> is never empty.
//
// In case of re-search or include translation we may have to split
// handling the same include or import across multiple commands.
@@ -1794,43 +1797,59 @@ namespace build2
// compiler resolves to the expected target. So we will also do the
// correlation via <name>.
//
- bool im (cmd[1] == 'M');
+ bool imp (cmd[1] == 'M');
- path f; // <path> or <name> if doesn't exist
- string n; // [<"]<name>[>"]
- bool exists;
- if (im) // @@ MODHDR TODO: GCC IMPORT command improvements
+ path f; // <path> or <name> if doesn't exist
+ string n; // [<"']<name>[>"']
+ bool exists; // <path> is not empty
+ bool searchable; // <name> is not single-quoted
{
- exists = true;
- n = rq;
- f = path (move (rq));
- }
- else
- {
- size_t p (rq.find (rq[0] == '<' ? '>' : '"', 1));
- if (p == string::npos || rq[p + 1] != ' ')
- break; // Malformed command.
+ char q (rq[0]); // Opening quote.
+ q = (q == '<' ? '>' :
+ q == '"' ? '"' :
+ q == '\'' ? '\'' : '\0'); // Closing quote.
- n.assign (rq, 0, p + 1);
+ size_t s (rq.size ()), qp; // Quote position.
+ if (q == '\0' || (qp = rq.find (q, 1)) == string::npos)
+ break; // Malformed command.
- exists = (p + 2 != rq.size ()); // [>"] and space.
+ n.assign (rq, 0, qp + 1);
- if (exists)
+ size_t p (qp + 1);
+ if (imp && q == '\'' && p == s) // IMPORT '<path>'
{
- rq.erase (0, p + 2); // <path>
- f = path (move (rq));
-
- // Complete the relative path not to confuse with non-existent.
- //
- if (!f.absolute ())
- f.complete ();
+ exists = true;
+ // Leave f empty and fall through.
}
else
{
- rq.erase (p);
- rq.erase (0, 1); // <name>
+ if (p != s && rq[p++] != ' ') // Skip following space, if any.
+ break;
+
+ exists = (p != s);
+
+ if (exists)
+ {
+ rq.erase (0, p);
+ f = path (move (rq));
+ assert (!f.empty ());
+ }
+ //else // Leave f empty and fall through.
+ }
+
+ if (f.empty ())
+ {
+ rq.erase (0, 1); // Opening quote.
+ rq.erase (qp - 1); // Closing quote and trailing space, if any.
f = path (move (rq));
}
+
+ // Complete relative paths not to confuse with non-existent.
+ //
+ if (exists && !f.absolute ())
+ f.complete ();
+
+ searchable = (q != '\'');
}
// The skip_count logic: in a nutshell (and similar to the non-
@@ -1895,6 +1914,13 @@ namespace build2
ht = er.first;
remapped = er.second;
+ if (remapped && !searchable)
+ {
+ rs = "ERROR remapping non-re-searchable header " + n;
+ bad_error = true;
+ break;
+ }
+
// If we couldn't enter this header as a target (as opposed to
// not finding a rule to update it), then our diagnostics won't
// really add anything to the compiler's.
@@ -1923,7 +1949,7 @@ namespace build2
assert (exists);
}
else
- assert (exists && !remapped); // Maybe this should be error.
+ assert (exists && !remapped); // Maybe this should be an error.
}
catch (const failed&)
{
@@ -1935,7 +1961,7 @@ namespace build2
// ours after the compiler's but that isn't easy).
//
rs = !exists
- ? string ("TEXT")
+ ? string ("INCLUDE")
: ("ERROR unable to update header '" +
(ht != nullptr ? ht->path () : f).string () + "'");
@@ -1943,7 +1969,7 @@ namespace build2
break;
}
- if (!im) // Indirect prerequisite (see above).
+ if (!imp) // Indirect prerequisite (see above).
update = updated || update;
// A mere update is not enough to cause a re-search. It either had
@@ -1963,7 +1989,7 @@ namespace build2
//
const string& hp (ht->path ().string ());
- if (im)
+ if (imp)
{
try
{
@@ -1986,12 +2012,12 @@ namespace build2
if (!skip)
{
st.headers++;
- dd.expect ("@ " + hp + ' ' + bp);
+ dd.expect ("@ '" + hp + "' " + bp);
}
else
st.skip--;
- rs = "OK " + bp;
+ rs = "IMPORT " + bp;
}
catch (const failed&)
{
@@ -2019,6 +2045,11 @@ namespace build2
{
// Doesn't seem there is much use in trying to correlate the
// followup in this case; what else can the compiler import?
+ // Plus, the followup IMPORT will use a different quoting for
+ // <name>.
+ //
+ // @@ MODHDR TODO: why not reduce this to as-if IMPORT with
+ // immediate BMI?
//
rs = "IMPORT";
break;
@@ -2030,7 +2061,7 @@ namespace build2
else
st.skip--;
- rs = "TEXT";
+ rs = "INCLUDE";
}
}
@@ -3224,21 +3255,21 @@ namespace build2
: make_pair (auto_rmfile (), false);
}
- // This can be a header or a header unit (mapping).
+ // This can be a header or a header unit (mapping). The latter
+ // is single-quoted.
//
// If this header (unit) came from the depdb, make sure it is no
// older than the target (if it has changed since the target was
// updated, then the cached data is stale).
//
- //
if ((*l)[0] == '@')
{
- size_t p (l->find (' ', 2));
+ size_t p (l->find ('\'', 3));
if (p != string::npos)
{
- path h (*l, 2, p - 2);
- path b (move (l->erase (0, p + 1)));
+ path h (*l, 3, p - 3);
+ path b (move (l->erase (0, p + 2)));
restart = add_unit (move (h), move (b), mt);
}
@@ -4237,10 +4268,12 @@ namespace build2
//
if (dd.writing () || !dd.skip ())
{
- auto write = [&dd] (const string& name, const path& file)
+ auto write = [&dd] (const string& name, const path& file, bool q)
{
dd.write ("@ ", false);
+ if (q) dd.write ('\'', false);
dd.write (name, false);
+ if (q) dd.write ('\'', false);
dd.write (' ', false);
dd.write (file);
};
@@ -4249,7 +4282,7 @@ namespace build2
//
if (ut == unit_type::module_iface ||
ut == unit_type::module_header)
- write (mi.name, t.path ());
+ write (mi.name, t.path (), ut == unit_type::module_header);
if (size_t start = md.modules.start)
{
@@ -4265,7 +4298,9 @@ namespace build2
// Save a variable lookup by getting the module name from
// the import list (see search_modules()).
//
- write (is[i - start].name, m->as<file> ().path ());
+ // Note: all real modules (not header units).
+ //
+ write (is[i - start].name, m->as<file> ().path (), false);
}
}
}
diff --git a/tests/cc/modules/headers.testscript b/tests/cc/modules/headers.testscript
index 4b8067f..4fb9a42 100644
--- a/tests/cc/modules/headers.testscript
+++ b/tests/cc/modules/headers.testscript
@@ -9,15 +9,36 @@
+$headers || exit
-: include-translation
-:
-cat <<EOI >=core.hxx;
+# Common source files that are symlinked in the test directories if used.
+#
++cat <<EOI >=core.hxx
#ifdef CORE_IN
# error macro isolation
#endif
#define CORE_OUT 1
inline int f () {return 1;}
EOI
+
+: import
+:
+#ln -s ../core.hxx ./; @@ why isn't working?
+cp ../core.hxx ./;
+cat <<EOI >=driver.cxx;
+ #define CORE_IN 1
+ import "core.hxx";
+ #ifndef CORE_OUT
+ # error macro export
+ #endif
+ int main () {return f () - CORE_OUT;}
+ EOI
+$* test clean <<EOI
+ exe{test}: cxx{driver}
+ EOI
+
+: include-translation
+:
+#ln -s ../core.hxx ./; @@ why isn't working?
+cp ../core.hxx ./;
cat <<EOI >=driver.cxx;
#define CORE_IN 1
#include "core.hxx"
@@ -26,6 +47,12 @@ cat <<EOI >=driver.cxx;
#endif
int main () {return f () - CORE_OUT;}
EOI
-$* test clean config.cxx.importable_headers="$~/core.hxx" &$~/../build/cc/*** <<EOI
+$* test clean config.cxx.importable_headers="$~/core.hxx" <<EOI
exe{test}: cxx{driver}
EOI
+
+# Clean module sidebuilds.
+#
+-$* clean <<EOI
+./:
+EOI