From 0dee00f28e623830e816c4002c8004c86055df85 Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Wed, 1 Apr 2015 11:08:13 +0200 Subject: Implement initial C++ configuration support --- build/.gitignore | 1 + build/b.cxx | 2 + build/buildfile | 2 +- build/config/operation.cxx | 13 ++--- build/cxx/module | 19 +++++++ build/cxx/module.cxx | 134 +++++++++++++++++++++++++++++++++++++++++++++ build/cxx/rule.cxx | 134 ++++++++++++++++++++++++++++++++++++--------- build/root.build | 5 ++ build/variable | 50 ++++++++++++++--- 9 files changed, 319 insertions(+), 41 deletions(-) create mode 100644 build/cxx/module create mode 100644 build/cxx/module.cxx 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 #include +#include #include @@ -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 + +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 + +#include +#include + +#include +#include +#include +#include + +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 (); + } + 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 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 // size_t #include // exit #include // move() +#include #include @@ -25,6 +26,36 @@ namespace build { namespace cxx { + static void + append_options (vector& args, scope& s, const char* var) + { + if (auto val = s[var]) + { + for (const name& n: val.as ().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& args, scope& s, string& opt) + { + if (auto val = s["cxx.std"]) + { + const string& v (val.as ()); + + // @@ 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 ()); + + vector 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 ().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 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 ().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 ()); + + vector 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 ro; - vector args {"g++-4.9", "-std=c++14", "-g", "-o"}; + scope& ts (scopes.find (e.path ())); + const string& cxx (ts["config.cxx"].as ()); + + vector 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 (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_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_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_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 @@ -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 () const; template <> + inline const list_value& value_proxy:: + as () const {return as ();} + + template <> const std::string& value_proxy:: as () const; -- cgit v1.1