aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBoris Kolpackov <boris@codesynthesis.com>2015-06-25 13:41:19 +0200
committerBoris Kolpackov <boris@codesynthesis.com>2015-06-25 13:41:19 +0200
commit95239b7c5404965d4f5ef997b5b75bf542a25192 (patch)
tree1b4fc6229723babffa7c5d56ee0c2eae24215535
parentf56b5a42b9aaaeb0c4e7dee894dea9686599b88c (diff)
Part one of dependency injection with auto-generation support
-rw-r--r--build/algorithm.cxx4
-rw-r--r--build/config/utility2
-rw-r--r--build/cxx/module.cxx6
-rw-r--r--build/cxx/rule.cxx264
-rw-r--r--build/rule2
-rw-r--r--build/target3
-rw-r--r--build/target.cxx10
-rw-r--r--tests/cli/build/bootstrap.build2
-rw-r--r--tests/cli/buildfile6
-rw-r--r--tests/cli/driver.cpp4
-rw-r--r--tests/cli/lib/libtest/build/bootstrap.build2
-rw-r--r--tests/cli/lib/libtest/build/export.build6
-rw-r--r--tests/cli/lib/libtest/build/root.build7
-rw-r--r--tests/cli/lib/libtest/buildfile2
-rw-r--r--tests/cli/lib/libtest/test/buildfile13
-rw-r--r--tests/cli/lib/libtest/test/extra/test.cli11
-rw-r--r--tests/cli/lib/libtest/test/test.cli8
-rw-r--r--tests/cli/lib/libtest/test/utility.cpp6
-rw-r--r--tests/cli/lib/test/build/bootstrap.build2
-rw-r--r--tests/cli/lib/test/build/root.build7
-rw-r--r--tests/cli/lib/test/buildfile6
-rw-r--r--tests/cli/lib/test/driver.C8
-rw-r--r--tests/cli/lib/test/test.cli (renamed from tests/cli/test.cli)0
-rw-r--r--tests/cli/simple/build/bootstrap.build2
-rw-r--r--tests/cli/simple/build/root.build (renamed from tests/cli/build/root.build)0
-rw-r--r--tests/cli/simple/buildfile8
-rw-r--r--tests/cli/simple/driver.cpp6
-rw-r--r--tests/cli/simple/test.cli5
28 files changed, 344 insertions, 58 deletions
diff --git a/build/algorithm.cxx b/build/algorithm.cxx
index c438fb8..f4da3bd 100644
--- a/build/algorithm.cxx
+++ b/build/algorithm.cxx
@@ -213,9 +213,9 @@ namespace build
search_and_match (action a, target& t)
{
group_prerequisites gp (t);
- t.prerequisite_targets.resize (gp.size ());
+ size_t i (t.prerequisite_targets.size ());
+ t.prerequisite_targets.resize (gp.size () + i);
- size_t i (0);
for (prerequisite& p: gp)
{
target& pt (search (p));
diff --git a/build/config/utility b/build/config/utility
index 3da990f..403a91e 100644
--- a/build/config/utility
+++ b/build/config/utility
@@ -53,7 +53,7 @@ namespace build
{
for (const name& n: val.template as<const list_value&> ())
{
- if (!n.type.empty () || !n.dir.empty ())
+ if (!n.simple ())
fail << "expected option instead of " << n <<
info << "in variable " << var;
diff --git a/build/cxx/module.cxx b/build/cxx/module.cxx
index 495819d..7e6a211 100644
--- a/build/cxx/module.cxx
+++ b/build/cxx/module.cxx
@@ -71,11 +71,11 @@ namespace build
bool r (getline (is, ver));
- if (!pr.wait ())
- throw failed ();
-
if (!r)
fail << "unexpected output from " << cxx;
+
+ if (!pr.wait ())
+ throw failed ();
}
catch (const process_error& e)
{
diff --git a/build/cxx/rule.cxx b/build/cxx/rule.cxx
index 79b6072..47f925d 100644
--- a/build/cxx/rule.cxx
+++ b/build/cxx/rule.cxx
@@ -15,6 +15,7 @@
#include <butl/utility> // reverse_iterate
#include <butl/fdstream>
#include <butl/optional>
+#include <butl/path-map>
#include <build/scope>
#include <build/variable>
@@ -69,8 +70,10 @@ namespace build
{
for (target* t: l.prerequisite_targets)
{
- if (t != nullptr &&
- (t->is_a<lib> () || t->is_a<liba> () || t->is_a<libso> ()))
+ if (t == nullptr)
+ continue;
+
+ if (t->is_a<lib> () || t->is_a<liba> () || t->is_a<libso> ())
append_lib_options (args, *t, var);
}
@@ -170,6 +173,161 @@ namespace build
}
}
+ // The strings used as the map key should be from the extension_pool.
+ // This way we can just compare pointers.
+ //
+ using ext_map = map<const string*, const target_type*>;
+
+ static ext_map
+ build_ext_map (scope& r)
+ {
+ ext_map m;
+
+ if (auto val = r["h.ext"])
+ m[&extension_pool.find (val.as<const string&> ())] = &h::static_type;
+
+ if (auto val = r["c.ext"])
+ m[&extension_pool.find (val.as<const string&> ())] = &c::static_type;
+
+ if (auto val = r["hxx.ext"])
+ m[&extension_pool.find (val.as<const string&> ())] = &hxx::static_type;
+
+ if (auto val = r["ixx.ext"])
+ m[&extension_pool.find (val.as<const string&> ())] = &ixx::static_type;
+
+ if (auto val = r["txx.ext"])
+ m[&extension_pool.find (val.as<const string&> ())] = &txx::static_type;
+
+ if (auto val = r["cxx.ext"])
+ m[&extension_pool.find (val.as<const string&> ())] = &cxx::static_type;
+
+ return m;
+ }
+
+ // Mapping of include prefixes (e.g., foo in <foo/bar>) for auto-
+ // generated headers to directories where they will be generated.
+ //
+ // We are using a prefix map of directories (dir_path_map) instead
+ // of just a map in order also cover sub-paths (e.g., <foo/more/bar>
+ // if we continue with the example). Specifically, we need to make
+ // sure we don't treat foobar as a sub-directory of foo.
+ //
+ // @@ The keys should be canonicalized.
+ //
+ using prefix_map = dir_path_map<dir_path>;
+
+ static void
+ append_prefixes (prefix_map& m, target& t, const char* var)
+ {
+ tracer trace ("cxx::append_prefixes");
+
+ const dir_path& out_base (t.dir);
+ const dir_path& out_root (t.root_scope ().path ());
+
+ if (auto val = t[var])
+ {
+ const list_value& l (val.template as<const list_value&> ());
+
+ // Assume the names have already been vetted by append_options().
+ //
+ for (auto i (l.begin ()), e (l.end ()); i != e; ++i)
+ {
+ // -I can either be in the -Ifoo or -I foo form.
+ //
+ dir_path d;
+ if (i->value == "-I")
+ {
+ if (++i == e)
+ break; // Let the compiler complain.
+
+ d = dir_path (i->value);
+ }
+ else if (i->value.compare (0, 2, "-I") == 0)
+ d = dir_path (i->value, 2, string::npos);
+ else
+ continue;
+
+ level5 ([&]{trace << "-I '" << d << "'";});
+
+ // If we are relative or not inside our project root, then
+ // ignore.
+ //
+ if (d.relative () || !d.sub (out_root))
+ continue;
+
+ // If the target directory is a sub-directory of the include
+ // directory, then the prefix is the difference between the
+ // two. Otherwise, leave it empty.
+ //
+ // The idea here is to make this "canonical" setup work auto-
+ // magically:
+ //
+ // 1. We include all files with a prefix, e.g., <foo/bar>.
+ // 2. The library target is in the foo/ sub-directory, e.g.,
+ // /tmp/foo/.
+ // 3. The poptions variable contains -I/tmp.
+ //
+ dir_path p (out_base.sub (d) ? out_base.leaf (d) : dir_path ());
+
+ auto j (m.find (p));
+
+ if (j != m.end ())
+ {
+ if (j->second != d)
+ fail << "duplicate generated dependency prefix '" << p << "'" <<
+ info << "old mapping to " << j->second <<
+ info << "new mapping to " << d;
+ }
+ else
+ {
+ level5 ([&]{trace << "'" << p << "' = '" << d << "'";});
+ m.emplace (move (p), move (d));
+ }
+ }
+ }
+ }
+
+ // Append library prefixes based on the cxx.export.poptions variables
+ // recursively, prerequisite libraries first.
+ //
+ static void
+ append_lib_prefixes (prefix_map& m, target& l)
+ {
+ for (target* t: l.prerequisite_targets)
+ {
+ if (t == nullptr)
+ continue;
+
+ if (t->is_a<lib> () || t->is_a<liba> () || t->is_a<libso> ())
+ append_lib_prefixes (m, *t);
+ }
+
+ append_prefixes (m, l, "cxx.export.poptions");
+ }
+
+ static prefix_map
+ build_prefix_map (target& t)
+ {
+ prefix_map m;
+
+ // First process the include directories from prerequsite
+ // libraries.
+ //
+ for (prerequisite& p: group_prerequisites (t))
+ {
+ target& pt (*p.target); // Already searched and matched.
+
+ if (pt.is_a<lib> () || pt.is_a<liba> () || pt.is_a<libso> ())
+ append_lib_prefixes (m, pt);
+ }
+
+ // Then process our own.
+ //
+ append_prefixes (m, t, "cxx.poptions");
+
+ return m;
+ }
+
// Return the next make prerequisite starting from the specified
// position and update position to point to the start of the
// following prerequisite or l.size() if there are none left.
@@ -212,37 +370,6 @@ namespace build
return r;
}
- // The strings used as the map key should be from the extension_pool.
- // This way we can just compare pointers.
- //
- using ext_map = map<const string*, const target_type*>;
-
- static ext_map
- build_ext_map (scope& r)
- {
- ext_map m;
-
- if (auto val = r["h.ext"])
- m[&extension_pool.find (val.as<const string&> ())] = &h::static_type;
-
- if (auto val = r["c.ext"])
- m[&extension_pool.find (val.as<const string&> ())] = &c::static_type;
-
- if (auto val = r["hxx.ext"])
- m[&extension_pool.find (val.as<const string&> ())] = &hxx::static_type;
-
- if (auto val = r["ixx.ext"])
- m[&extension_pool.find (val.as<const string&> ())] = &ixx::static_type;
-
- if (auto val = r["txx.ext"])
- m[&extension_pool.find (val.as<const string&> ())] = &txx::static_type;
-
- if (auto val = r["cxx.ext"])
- m[&extension_pool.find (val.as<const string&> ())] = &cxx::static_type;
-
- return m;
- }
-
void compile::
inject_prerequisites (action a, target& t, const cxx& s, scope& ds) const
{
@@ -276,7 +403,7 @@ namespace build
if (t.is_a<objso> ())
args.push_back ("-fPIC");
- args.push_back ("-MM"); // @@ Change to -M
+ args.push_back ("-M"); // Note: -MM -MG skips missing <>-included.
args.push_back ("-MG"); // Treat missing headers as generated.
args.push_back ("-MQ"); // Quoted target name.
args.push_back ("*"); // Old versions can't handle empty target name.
@@ -301,8 +428,9 @@ namespace build
{
process pr (args.data (), false, false, true);
ifdstream is (pr.in_ofd);
+ prefix_map pm; // Build it lazily.
- for (bool first (true); !is.eof (); )
+ for (bool first (true), second (true); !is.eof (); )
{
string l;
getline (is, l);
@@ -321,11 +449,35 @@ namespace build
break;
assert (l[0] == '*' && l[1] == ':' && l[2] == ' ');
- next (l, (pos = 3)); // Skip the source file.
first = false;
+
+ // While normally we would have the source file on the
+ // first line, if too long, it will be move to the next
+ // line and all we will have on this line is "*: \".
+ //
+ if (l.size () == 4 && l[3] == '\\')
+ continue;
+ else
+ pos = 3; // Skip "*: ".
+
+ // Fall through to the 'second' block.
}
+ if (second)
+ {
+ second = false;
+ next (l, pos); // Skip the source file.
+ }
+
+ auto g (
+ make_exception_guard (
+ [](const target& s)
+ {
+ info << "while extracting dependencies from " << s;
+ },
+ s));
+
while (pos != l.size ())
{
path f (next (l, pos));
@@ -333,8 +485,44 @@ namespace build
if (!f.absolute ())
{
- level5 ([&]{trace << "skipping generated/non-existent " << f;});
- continue;
+ // This is probably as often an error as an auto-generated
+ // file, so trace at level 3.
+ //
+ level3 ([&]{trace << "non-existent header '" << f << "'";});
+
+ // If we already did it and build_prefix_map() returned empty,
+ // then we would have failed below.
+ //
+ if (pm.empty ())
+ pm = build_prefix_map (t);
+
+ // First try the whole file. Then just the directory.
+ //
+ // @@ Has to be a separate map since the prefix can be
+ // the same as the file name.
+ //
+ // auto i (pm.find (f));
+
+ // Find the most qualified prefix of which we are a
+ // sub-path.
+ //
+ auto i (pm.end ());
+
+ if (!pm.empty ())
+ {
+ const dir_path& d (f.directory ());
+ i = pm.upper_bound (d);
+ --i; // Greatest less than.
+
+ if (!d.sub (i->first)) // We might still not be a sub.
+ i = pm.end ();
+ }
+
+ if (i == pm.end ())
+ fail << "unable to map presumably auto-generated header '"
+ << f << "' to a project";
+
+ f = i->second / f;
}
level5 ([&]{trace << "injecting " << f;});
diff --git a/build/rule b/build/rule
index c7134e1..7a4d4d7 100644
--- a/build/rule
+++ b/build/rule
@@ -30,7 +30,7 @@ namespace build
using target_rule_map = std::unordered_map<
std::type_index,
- butl::prefix_multimap<std::string, std::reference_wrapper<rule>, '.'>>;
+ butl::prefix_map<std::string, std::reference_wrapper<rule>, '.'>>;
using operation_rule_map = std::unordered_map<operation_id, target_rule_map>;
diff --git a/build/target b/build/target
index ddf5a2e..1de521e 100644
--- a/build/target
+++ b/build/target
@@ -552,6 +552,9 @@ namespace build
// the directory. Similarly, if name_suffix is not NULL, add it after
// the name part and before the extension.
//
+ // Finally, if the path was already assigned to this target, then
+ // this function verifies that the two are the same.
+ //
void
derive_path (const char* default_ext = nullptr,
const char* name_prefix = nullptr,
diff --git a/build/target.cxx b/build/target.cxx
index 87194fb..3b030ee 100644
--- a/build/target.cxx
+++ b/build/target.cxx
@@ -285,7 +285,15 @@ namespace build
n += *ext;
}
- path (dir / path_type (move (n)));
+ path_type p (dir / path_type (move (n)));
+ const path_type& ep (path ());
+
+ if (ep.empty ())
+ path (p);
+ else if (p != ep)
+ fail << "path mismatch for target " << *this <<
+ info << "assigned '" << ep << "'" <<
+ info << "derived '" << p << "'";
}
// file_target
diff --git a/tests/cli/build/bootstrap.build b/tests/cli/build/bootstrap.build
deleted file mode 100644
index 9e91c9a..0000000
--- a/tests/cli/build/bootstrap.build
+++ /dev/null
@@ -1,2 +0,0 @@
-project = cli-test
-using config
diff --git a/tests/cli/buildfile b/tests/cli/buildfile
deleted file mode 100644
index d71a677..0000000
--- a/tests/cli/buildfile
+++ /dev/null
@@ -1,6 +0,0 @@
-hxx.ext =
-cxx.ext = cpp
-ixx.ext = ipp
-
-exe{driver}: cxx{driver test}
-cxx{test}: cli{test}
diff --git a/tests/cli/driver.cpp b/tests/cli/driver.cpp
deleted file mode 100644
index 70b4146..0000000
--- a/tests/cli/driver.cpp
+++ /dev/null
@@ -1,4 +0,0 @@
-int
-main ()
-{
-}
diff --git a/tests/cli/lib/libtest/build/bootstrap.build b/tests/cli/lib/libtest/build/bootstrap.build
new file mode 100644
index 0000000..67a5f1a
--- /dev/null
+++ b/tests/cli/lib/libtest/build/bootstrap.build
@@ -0,0 +1,2 @@
+project = cli-lib-libtest
+using config
diff --git a/tests/cli/lib/libtest/build/export.build b/tests/cli/lib/libtest/build/export.build
new file mode 100644
index 0000000..e8b12b3
--- /dev/null
+++ b/tests/cli/lib/libtest/build/export.build
@@ -0,0 +1,6 @@
+$out_root/:
+{
+ include test/
+}
+
+export $out_root/test/lib{test}
diff --git a/tests/cli/lib/libtest/build/root.build b/tests/cli/lib/libtest/build/root.build
new file mode 100644
index 0000000..f2f5ca6
--- /dev/null
+++ b/tests/cli/lib/libtest/build/root.build
@@ -0,0 +1,7 @@
+using cxx
+
+hxx.ext =
+ixx.ext = ipp
+cxx.ext = cpp
+
+using cli
diff --git a/tests/cli/lib/libtest/buildfile b/tests/cli/lib/libtest/buildfile
new file mode 100644
index 0000000..d00a137
--- /dev/null
+++ b/tests/cli/lib/libtest/buildfile
@@ -0,0 +1,2 @@
+.: test/
+include test/
diff --git a/tests/cli/lib/libtest/test/buildfile b/tests/cli/lib/libtest/test/buildfile
new file mode 100644
index 0000000..5325f14
--- /dev/null
+++ b/tests/cli/lib/libtest/test/buildfile
@@ -0,0 +1,13 @@
+lib{test}: cxx{utility} cxx{test} extra/cxx{test}
+cxx{test} hxx{test}: cli{test}
+
+extra/:
+{
+ cxx{test} hxx{test}: cli{test}
+ cli.options += --cli-namespace test::extra::cli
+}
+
+cxx.poptions += -I$out_root -I$src_root
+lib{test}: cxx.export.poptions = -I$out_root -I$src_root
+
+cli.options += --cli-namespace test::cli
diff --git a/tests/cli/lib/libtest/test/extra/test.cli b/tests/cli/lib/libtest/test/extra/test.cli
new file mode 100644
index 0000000..8408402
--- /dev/null
+++ b/tests/cli/lib/libtest/test/extra/test.cli
@@ -0,0 +1,11 @@
+namespace test
+{
+ namespace extra
+ {
+ class options
+ {
+ bool --help;
+ bool --version;
+ };
+ }
+}
diff --git a/tests/cli/lib/libtest/test/test.cli b/tests/cli/lib/libtest/test/test.cli
new file mode 100644
index 0000000..8bc51e8
--- /dev/null
+++ b/tests/cli/lib/libtest/test/test.cli
@@ -0,0 +1,8 @@
+namespace test
+{
+ class options
+ {
+ bool --help;
+ bool --version;
+ };
+}
diff --git a/tests/cli/lib/libtest/test/utility.cpp b/tests/cli/lib/libtest/test/utility.cpp
new file mode 100644
index 0000000..7c59218
--- /dev/null
+++ b/tests/cli/lib/libtest/test/utility.cpp
@@ -0,0 +1,6 @@
+#include <test/test>
+
+void
+f ()
+{
+}
diff --git a/tests/cli/lib/test/build/bootstrap.build b/tests/cli/lib/test/build/bootstrap.build
new file mode 100644
index 0000000..e83e189
--- /dev/null
+++ b/tests/cli/lib/test/build/bootstrap.build
@@ -0,0 +1,2 @@
+project = cli-lib-test
+using config
diff --git a/tests/cli/lib/test/build/root.build b/tests/cli/lib/test/build/root.build
new file mode 100644
index 0000000..2dda614
--- /dev/null
+++ b/tests/cli/lib/test/build/root.build
@@ -0,0 +1,7 @@
+using cxx
+
+hxx.ext = h
+ixx.ext = inl
+cxx.ext = C
+
+using cli
diff --git a/tests/cli/lib/test/buildfile b/tests/cli/lib/test/buildfile
new file mode 100644
index 0000000..fc0e552
--- /dev/null
+++ b/tests/cli/lib/test/buildfile
@@ -0,0 +1,6 @@
+import libs += cli-lib-libtest
+
+exe{driver}: cxx{driver} cxx{test} $libs
+cxx{test} hxx{test}: cli{test}
+
+cxx.poptions = -I$out_root
diff --git a/tests/cli/lib/test/driver.C b/tests/cli/lib/test/driver.C
new file mode 100644
index 0000000..ee2171f
--- /dev/null
+++ b/tests/cli/lib/test/driver.C
@@ -0,0 +1,8 @@
+#include "test.h"
+#include <test/test>
+#include <test/extra/test>
+
+int
+main ()
+{
+}
diff --git a/tests/cli/test.cli b/tests/cli/lib/test/test.cli
index db3cfb8..db3cfb8 100644
--- a/tests/cli/test.cli
+++ b/tests/cli/lib/test/test.cli
diff --git a/tests/cli/simple/build/bootstrap.build b/tests/cli/simple/build/bootstrap.build
new file mode 100644
index 0000000..e2fd93f
--- /dev/null
+++ b/tests/cli/simple/build/bootstrap.build
@@ -0,0 +1,2 @@
+project = cli-simple
+using config
diff --git a/tests/cli/build/root.build b/tests/cli/simple/build/root.build
index 8e910cf..8e910cf 100644
--- a/tests/cli/build/root.build
+++ b/tests/cli/simple/build/root.build
diff --git a/tests/cli/simple/buildfile b/tests/cli/simple/buildfile
new file mode 100644
index 0000000..5a940ce
--- /dev/null
+++ b/tests/cli/simple/buildfile
@@ -0,0 +1,8 @@
+hxx.ext =
+cxx.ext = cpp
+ixx.ext = ipp
+
+cxx.poptions = -I$out_root
+
+exe{driver}: cxx{driver} cxx{test}
+cxx{test} hxx{test}: cli{test}
diff --git a/tests/cli/simple/driver.cpp b/tests/cli/simple/driver.cpp
new file mode 100644
index 0000000..ef9cc60
--- /dev/null
+++ b/tests/cli/simple/driver.cpp
@@ -0,0 +1,6 @@
+#include "test"
+
+int
+main ()
+{
+}
diff --git a/tests/cli/simple/test.cli b/tests/cli/simple/test.cli
new file mode 100644
index 0000000..db3cfb8
--- /dev/null
+++ b/tests/cli/simple/test.cli
@@ -0,0 +1,5 @@
+class options
+{
+ bool --help;
+ bool --version;
+};