aboutsummaryrefslogtreecommitdiff
path: root/build
diff options
context:
space:
mode:
authorBoris Kolpackov <boris@codesynthesis.com>2015-04-01 11:08:13 +0200
committerBoris Kolpackov <boris@codesynthesis.com>2015-04-01 11:08:13 +0200
commit0dee00f28e623830e816c4002c8004c86055df85 (patch)
tree14c2a2abe241df53821bc585b7adeaff16c768cd /build
parent276a0796a97b0a312c0071bba0bf924b5f5c6eee (diff)
Implement initial C++ configuration support
Diffstat (limited to 'build')
-rw-r--r--build/.gitignore1
-rw-r--r--build/b.cxx2
-rw-r--r--build/buildfile2
-rw-r--r--build/config/operation.cxx13
-rw-r--r--build/cxx/module19
-rw-r--r--build/cxx/module.cxx134
-rw-r--r--build/cxx/rule.cxx134
-rw-r--r--build/root.build5
-rw-r--r--build/variable50
9 files changed, 319 insertions, 41 deletions
diff --git a/build/.gitignore b/build/.gitignore
index 6dc02d1..9a7f491 100644
--- a/build/.gitignore
+++ b/build/.gitignore
@@ -1,4 +1,5 @@
b
b1
+config.build
diff --git a/build/b.cxx b/build/b.cxx
index e231aa3..d3628d9 100644
--- a/build/b.cxx
+++ b/build/b.cxx
@@ -128,6 +128,7 @@ namespace build
#include <build/cxx/target>
#include <build/cxx/rule>
+#include <build/cxx/module>
#include <build/config/module>
@@ -151,6 +152,7 @@ main (int argc, char* argv[])
// Register modules.
//
modules["config"] = &config::init;
+ modules["cxx"] = &cxx::init;
// Register target types.
//
diff --git a/build/buildfile b/build/buildfile
index 95899fd..3fcc9f4 100644
--- a/build/buildfile
+++ b/build/buildfile
@@ -1,4 +1,4 @@
-cxx = cxx/{target rule}
+cxx = cxx/{target rule module}
config = config/{operation module}
exe{b1}: cxx{b algorithm name operation spec scope variable target \
diff --git a/build/config/operation.cxx b/build/config/operation.cxx
index baafc97..c6a9444 100644
--- a/build/config/operation.cxx
+++ b/build/config/operation.cxx
@@ -133,10 +133,9 @@ namespace build
{
mkdir (out_root);
mkdir (out_root / build_dir);
+ mkdir (out_root / bootstrap_dir);
}
- mkdir (out_root / bootstrap_dir);
-
// We distinguish between a complete configure and operation-
// specific.
//
@@ -223,14 +222,14 @@ namespace build
level4 ([&]{trace << "completely disfiguring " << out_root;});
rmfile (out_root / config_file);
- rmfile (out_root / src_root_file);
-
- // Clean up the directories.
- //
- rmdir (out_root / bootstrap_dir);
if (out_root != src_root)
{
+ rmfile (out_root / src_root_file);
+
+ // Clean up the directories.
+ //
+ rmdir (out_root / bootstrap_dir);
rmdir (out_root / build_dir);
if (rmdir (out_root) == rmdir_status::not_empty)
diff --git a/build/cxx/module b/build/cxx/module
new file mode 100644
index 0000000..65cdcc6
--- /dev/null
+++ b/build/cxx/module
@@ -0,0 +1,19 @@
+// file : build/cxx/module -*- C++ -*-
+// copyright : Copyright (c) 2014-2015 Code Synthesis Tools CC
+// license : MIT; see accompanying LICENSE file
+
+#ifndef BUILD_CXX_MODULE
+#define BUILD_CXX_MODULE
+
+#include <build/module>
+
+namespace build
+{
+ namespace cxx
+ {
+ void
+ init (scope&, scope&, const location&);
+ }
+}
+
+#endif // BUILD_CXX_MODULE
diff --git a/build/cxx/module.cxx b/build/cxx/module.cxx
new file mode 100644
index 0000000..eb7ca40
--- /dev/null
+++ b/build/cxx/module.cxx
@@ -0,0 +1,134 @@
+// file : build/cxx/module.cxx -*- C++ -*-
+// copyright : Copyright (c) 2014-2015 Code Synthesis Tools CC
+// license : MIT; see accompanying LICENSE file
+
+#include <build/cxx/module>
+
+#include <istream>
+#include <ext/stdio_filebuf.h>
+
+#include <build/path>
+#include <build/scope>
+#include <build/process>
+#include <build/diagnostics>
+
+using namespace std;
+
+namespace build
+{
+ namespace cxx
+ {
+ void
+ init (scope& root, scope& base, const location& l)
+ {
+ //@@ TODO: avoid multiple inits (generally, for modules).
+ //
+
+ tracer trace ("cxx::init");
+
+ //@@ Should it be this way?
+ //
+ if (&root != &base)
+ fail (l) << "cxx module must be initialized in project root scope";
+
+ //@@ TODO: need to register target types, rules here instead of main().
+
+ const path& out_root (root.path ());
+ level4 ([&]{trace << "for " << out_root << '/';});
+
+ // Configure.
+ //
+
+ // config.cxx
+ //
+ for (bool f (true); f; f = false)
+ {
+ auto val (root["config.cxx"]);
+
+ string v;
+
+ if (val)
+ {
+ if (&val.scope () != global_scope)
+ break; // A value from config.build.
+
+ v = val.as<const string&> ();
+ }
+ else
+ v = "g++"; // Default.
+
+ // Test it by trying to execute.
+ //
+ const char* args[] = {v.c_str (), "-dumpversion", nullptr};
+
+ if (verb >= 1)
+ print_process (args);
+ else
+ text << "test " << v;
+
+ string ver;
+ try
+ {
+ process pr (args, false, false, true);
+
+ __gnu_cxx::stdio_filebuf<char> fb (pr.in_ofd, ios_base::in);
+ istream is (&fb);
+
+ bool r (getline (is, ver));
+
+ if (!pr.wait ())
+ throw failed ();
+
+ if (!r)
+ fail << "unexpected output from " << v;
+ }
+ catch (const process_error& e)
+ {
+ error << "unable to execute " << v << ": " << e.what ();
+
+ if (e.child ())
+ exit (1);
+
+ throw failed ();
+ }
+
+ text << "toolchain version " << ver;
+
+ // Set on the project root.
+ //
+ root.variables["config.cxx"] = move (v);
+ }
+
+ // config.cxx.{p,c,l}options
+ // config.cxx.libs
+ //
+ // These are optional so all we need to do is "import" them
+ // into the root scope if they were specified on the command
+ // line.
+ //
+ if (auto val = root["config.cxx.poptions"])
+ {
+ if (&val.scope () == global_scope)
+ root.variables["config.cxx.poptions"] = val;
+ }
+
+ if (auto val = root["config.cxx.coptions"])
+ {
+ if (&val.scope () == global_scope)
+ root.variables["config.cxx.coptions"] = val;
+ }
+
+ if (auto val = root["config.cxx.loptions"])
+ {
+ if (&val.scope () == global_scope)
+ root.variables["config.cxx.loptions"] = val;
+ }
+
+ if (auto val = root["config.cxx.libs"])
+ {
+ if (&val.scope () == global_scope)
+ root.variables["config.cxx.libs"] = val;
+ }
+ }
+ }
+}
diff --git a/build/cxx/rule.cxx b/build/cxx/rule.cxx
index 549d987..8d7f3c3 100644
--- a/build/cxx/rule.cxx
+++ b/build/cxx/rule.cxx
@@ -9,6 +9,7 @@
#include <cstddef> // size_t
#include <cstdlib> // exit
#include <utility> // move()
+#include <istream>
#include <ext/stdio_filebuf.h>
@@ -25,6 +26,36 @@ namespace build
{
namespace cxx
{
+ static void
+ append_options (vector<const char*>& args, scope& s, const char* var)
+ {
+ if (auto val = s[var])
+ {
+ for (const name& n: val.as<const list_value&> ().data)
+ {
+ if (!n.type.empty () || !n.dir.empty ())
+ fail << "expected option instead of " << n <<
+ info << "in variable " << var;
+
+ args.push_back (n.value.c_str ());
+ }
+ }
+ }
+
+ static void
+ append_std (vector<const char*>& args, scope& s, string& opt)
+ {
+ if (auto val = s["cxx.std"])
+ {
+ const string& v (val.as<const string&> ());
+
+ // @@ Need to translate 11 to 0x for older versions.
+ //
+ opt = "-std=c++" + v;
+ args.push_back (opt.c_str ());
+ }
+ }
+
// compile
//
void* compile::
@@ -151,19 +182,39 @@ namespace build
{
tracer trace ("cxx::compile::inject_prerequisites");
+ scope& ts (scopes.find (o.path ()));
+ const string& cxx (ts["config.cxx"].as<const string&> ());
+
+ vector<const char*> args {cxx.c_str ()};
+
+ append_options (args, ts, "config.cxx.poptions");
+ append_options (args, ts, "cxx.poptions");
+
+ // @@ Some C++ options (e.g., -std, -m) affect the preprocessor.
+ // Or maybe they are not C++ options? Common options?
+ //
+ append_options (args, ts, "config.cxx.coptions");
+
+ string std; // Storage.
+ append_std (args, ts, std);
+
+ append_options (args, ts, "cxx.coptions");
+
+ args.push_back ("-MM"); // @@ Change to -M
+ 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.
+
// We are using absolute source file path in order to get
- // absolute paths in the result. @@ We will also have to
- // use absolute -I paths to guarantee that.
- //
- const char* args[] = {
- "g++-4.9",
- "-std=c++14",
- "-I", ds["src_root"].as<const path&> ().string ().c_str (),
- "-MM", //@@ -M
- "-MG", // Treat missing headers as generated.
- "-MQ", "*", // Quoted target (older version can't handle empty name).
- s.path ().string ().c_str (),
- nullptr};
+ // absolute paths in the result. Any relative paths in the
+ // result are non-existent generated headers.
+ //
+ // @@ We will also have to use absolute -I paths to guarantee
+ // that.
+ //
+ args.push_back (s.path ().string ().c_str ());
+
+ args.push_back (nullptr);
if (verb >= 2)
print_process (args);
@@ -172,7 +223,7 @@ namespace build
try
{
- process pr (args, false, false, true);
+ process pr (args.data (), false, false, true);
__gnu_cxx::stdio_filebuf<char> fb (pr.in_ofd, ios_base::in);
istream is (&fb);
@@ -285,15 +336,28 @@ namespace build
path ro (relative (o.path ()));
path rs (relative (s->path ()));
- const char* args[] = {
- "g++-4.9",
- "-std=c++14",
- "-g",
- "-I", o.prerequisites[0].get ().scope["src_root"].as<const path&> ().string ().c_str (),
- "-c",
- "-o", ro.string ().c_str (),
- rs.string ().c_str (),
- nullptr};
+ scope& ts (scopes.find (o.path ()));
+ const string& cxx (ts["config.cxx"].as<const string&> ());
+
+ vector<const char*> args {cxx.c_str ()};
+
+ append_options (args, ts, "config.cxx.poptions");
+ append_options (args, ts, "cxx.poptions");
+
+ append_options (args, ts, "config.cxx.coptions");
+
+ string std; // Storage.
+ append_std (args, ts, std);
+
+ append_options (args, ts, "cxx.coptions");
+
+ args.push_back ("-o");
+ args.push_back (ro.string ().c_str ());
+
+ args.push_back ("-c");
+ args.push_back (rs.string ().c_str ());
+
+ args.push_back (nullptr);
if (verb >= 1)
print_process (args);
@@ -302,7 +366,7 @@ namespace build
try
{
- process pr (args);
+ process pr (args.data ());
if (!pr.wait ())
throw failed ();
@@ -509,9 +573,10 @@ namespace build
prerequisite* cp1 (nullptr);
for (prerequisite& p: ot.prerequisites)
{
- // Ignore some known target types (headers).
+ // Ignore some known target types (fsdir, headers).
//
- if (p.type.id == typeid (h) ||
+ if (p.type.id == typeid (fsdir) ||
+ p.type.id == typeid (h) ||
(cp.type.id == typeid (cxx) && (p.type.id == typeid (hxx) ||
p.type.id == typeid (ixx) ||
p.type.id == typeid (txx))))
@@ -583,10 +648,24 @@ namespace build
path re (relative (e.path ()));
vector<path> ro;
- vector<const char*> args {"g++-4.9", "-std=c++14", "-g", "-o"};
+ scope& ts (scopes.find (e.path ()));
+ const string& cxx (ts["config.cxx"].as<const string&> ());
+
+ vector<const char*> args {cxx.c_str ()};
+ append_options (args, ts, "config.cxx.coptions");
+
+ string std; // Storage.
+ append_std (args, ts, std);
+
+ append_options (args, ts, "cxx.coptions");
+
+ args.push_back ("-o");
args.push_back (re.string ().c_str ());
+ append_options (args, ts, "config.cxx.loptions");
+ append_options (args, ts, "cxx.loptions");
+
for (const prerequisite& p: t.prerequisites)
{
if (const obj* o = dynamic_cast<const obj*> (p.target))
@@ -596,6 +675,9 @@ namespace build
}
}
+ append_options (args, ts, "config.cxx.libs");
+ append_options (args, ts, "cxx.libs");
+
args.push_back (nullptr);
if (verb >= 1)
diff --git a/build/root.build b/build/root.build
index 5199200..7321d32 100644
--- a/build/root.build
+++ b/build/root.build
@@ -1 +1,6 @@
source $out_root/build/config.build
+
+using cxx
+
+cxx.std = 14
+cxx.poptions += -I$src_root
diff --git a/build/variable b/build/variable
index dfdcaff..0688a3e 100644
--- a/build/variable
+++ b/build/variable
@@ -48,21 +48,30 @@ namespace build
// value
//
+ struct value;
+ typedef std::unique_ptr<value> value_ptr;
+
struct value
{
typedef build::scope scope_type;
- virtual
- ~value () = default;
+ scope_type& scope; // Scope to which this value belongs.
+ public:
value (scope_type& s): scope (s) {}
- scope_type& scope; // Scope to which this value belongs.
+ virtual value_ptr
+ clone (scope_type& s) const = 0;
+
+ virtual
+ ~value () = default;
};
- typedef std::unique_ptr<value> value_ptr;
struct list_value: value
{
+ names data;
+
+ public:
list_value (scope_type& s, names d): value (s), data (std::move (d)) {}
list_value (scope_type& s, std::string d)
@@ -79,7 +88,8 @@ namespace build
data.emplace_back (std::move (d));
}
- names data;
+ value_ptr
+ clone (scope_type& s) const {return value_ptr (new list_value (s, data));}
};
typedef std::unique_ptr<list_value> list_value_ptr;
@@ -87,9 +97,14 @@ namespace build
//
struct value_proxy
{
+ typedef build::scope scope_type;
+
explicit operator bool () const {return p != nullptr && *p != nullptr;}
explicit operator value_ptr& () const {return *p;}
+ scope_type&
+ scope () const {return *s;}
+
// Get interface. See available specializations below.
//
template <typename T>
@@ -107,6 +122,23 @@ namespace build
}
const value_proxy&
+ operator= (const value_proxy& v) const
+ {
+ if (this != &v)
+ {
+ if (v)
+ {
+ const value_ptr& vp (v);
+ *p = vp->clone (*s);
+ }
+ else
+ p->reset ();
+ }
+
+ return *this;
+ }
+
+ const value_proxy&
operator= (std::string v) const
{
// In most cases this is used to initialize a new variable, so
@@ -129,11 +161,11 @@ namespace build
// Implementation details.
//
explicit
- value_proxy (value_ptr* p, scope* s): p (p), s (s) {}
+ value_proxy (value_ptr* p, scope_type* s): p (p), s (s) {}
protected:
value_ptr* p;
- scope* s;
+ scope_type* s;
};
template <>
@@ -141,6 +173,10 @@ namespace build
as<list_value&> () const;
template <>
+ inline const list_value& value_proxy::
+ as<const list_value&> () const {return as<list_value&> ();}
+
+ template <>
const std::string& value_proxy::
as<const std::string&> () const;