aboutsummaryrefslogtreecommitdiff
path: root/libbuild2/c/init.cxx
diff options
context:
space:
mode:
authorKaren Arutyunov <karen@codesynthesis.com>2019-08-24 22:10:09 +0300
committerKaren Arutyunov <karen@codesynthesis.com>2019-08-28 15:01:48 +0300
commitf7adb1ce7a13753a6acf5c9eeb9577ecdada630c (patch)
tree5bd0bb1488c0a6fb35788ea815cbd7533a0de72a /libbuild2/c/init.cxx
parent4bdf53837e010073de802070d4e6087410662d3e (diff)
Move c build system module to separate library
Diffstat (limited to 'libbuild2/c/init.cxx')
-rw-r--r--libbuild2/c/init.cxx375
1 files changed, 375 insertions, 0 deletions
diff --git a/libbuild2/c/init.cxx b/libbuild2/c/init.cxx
new file mode 100644
index 0000000..43d647f
--- /dev/null
+++ b/libbuild2/c/init.cxx
@@ -0,0 +1,375 @@
+// file : libbuild2/c/init.cxx -*- C++ -*-
+// copyright : Copyright (c) 2014-2019 Code Synthesis Ltd
+// license : MIT; see accompanying LICENSE file
+
+#include <libbuild2/c/init.hxx>
+
+#include <libbuild2/scope.hxx>
+#include <libbuild2/diagnostics.hxx>
+
+#include <libbuild2/cc/guess.hxx>
+#include <libbuild2/cc/module.hxx>
+
+#include <libbuild2/c/target.hxx>
+
+#ifndef BUILD2_DEFAULT_C
+# ifdef BUILD2_NATIVE_C
+# define BUILD2_DEFAULT_C BUILD2_NATIVE_C
+# else
+# define BUILD2_DEFAULT_C ""
+# endif
+#endif
+
+using namespace std;
+using namespace butl;
+
+namespace build2
+{
+ namespace c
+ {
+ using cc::compiler_id;
+ using cc::compiler_class;
+ using cc::compiler_info;
+
+ class config_module: public cc::config_module
+ {
+ public:
+ explicit
+ config_module (config_data&& d)
+ : config_data (move (d)), cc::config_module (move (d)) {}
+
+ virtual strings
+ translate_std (const compiler_info&,
+ scope&,
+ const string*) const override;
+ };
+
+ using cc::module;
+
+ strings config_module::
+ translate_std (const compiler_info& ci, scope& rs, const string* v) const
+ {
+ strings r;
+
+ switch (ci.class_)
+ {
+ case compiler_class::msvc:
+ {
+ // Standard-wise, with VC you get what you get. The question is
+ // whether we should verify that the requested standard is provided
+ // by this VC version. And if so, from which version should we say
+ // VC supports 90, 99, and 11? We should probably be as loose as
+ // possible here since the author will always be able to tighten
+ // (but not loosen) this in the buildfile (i.e., detect unsupported
+ // versions).
+ //
+ // The state of affairs seem to be (from Herb Sutter's blog):
+ //
+ // 10.0 - most of C95 plus a few C99 features
+ // 11.0 - partial support for the C++11 subset of C11
+ // 12.0 - more C11 features from the C++11 subset, most of C99
+ //
+ // So let's say C99 is supported from 10.0 and C11 from 11.0. And
+ // C90 is supported by everything we care to support.
+ //
+ // C17 is a bug-fix version of C11 so here we assume it is the same
+ // as C11.
+ //
+ if (v == nullptr)
+ ;
+ else if (*v != "90")
+ {
+ uint64_t cver (ci.version.major);
+
+ if ((*v == "99" && cver < 16) || // Since VS2010/10.0.
+ ((*v == "11" ||
+ *v == "17") && cver < 17)) // Since VS2012/11.0.
+ {
+ fail << "C" << *v << " is not supported by " << ci.signature <<
+ info << "required by " << project (rs) << '@' << rs;
+ }
+ }
+ break;
+ }
+ case compiler_class::gcc:
+ {
+ // 90 and 89 are the same standard. Translate 99 to 9x and 11 to 1x
+ // for compatibility with older versions of the compilers.
+ //
+ if (v == nullptr)
+ ;
+ else
+ {
+ string o ("-std=");
+
+ if (*v == "90") o += "c90";
+ else if (*v == "99") o += "c9x";
+ else if (*v == "11") o += "c1x";
+ else if (*v == "17") o += "c17"; // GCC 8, Clang 6.
+ else o += *v; // In case the user specifies e.g., 'gnu11'.
+
+ r.push_back (move (o));
+ }
+ break;
+ }
+ }
+
+ return r;
+ }
+
+ static const char* const hinters[] = {"cxx", nullptr};
+
+ // See cc::module for details on guess_init vs config_init.
+ //
+ bool
+ guess_init (scope& rs,
+ scope& bs,
+ const location& loc,
+ unique_ptr<module_base>& mod,
+ bool,
+ bool,
+ const variable_map& hints)
+ {
+ tracer trace ("c::guess_init");
+ l5 ([&]{trace << "for " << bs;});
+
+ // We only support root loading (which means there can only be one).
+ //
+ if (&rs != &bs)
+ fail (loc) << "c.guess module must be loaded in project root";
+
+ // Load cc.core.vars so that we can cache all the cc.* variables.
+ //
+ if (!cast_false<bool> (rs["cc.core.vars.loaded"]))
+ load_module (rs, rs, "cc.core.vars", loc);
+
+ // Enter all the variables and initialize the module data.
+ //
+ auto& v (rs.ctx.var_pool.rw (rs));
+
+ cc::config_data d {
+ cc::lang::c,
+
+ "c",
+ "c",
+ BUILD2_DEFAULT_C,
+ ".i",
+
+ hinters,
+
+ // Note: some overridable, some not.
+ //
+ v.insert<path> ("config.c", true),
+ v.insert<string> ("config.c.id", true),
+ v.insert<string> ("config.c.version", true),
+ v.insert<string> ("config.c.target", true),
+ v.insert<string> ("config.c.std", true),
+ v.insert<strings> ("config.c.poptions", true),
+ v.insert<strings> ("config.c.coptions", true),
+ v.insert<strings> ("config.c.loptions", true),
+ v.insert<strings> ("config.c.aoptions", true),
+ v.insert<strings> ("config.c.libs", true),
+ nullptr /* config.c.importable_headers */,
+
+ v.insert<process_path> ("c.path"),
+ v.insert<dir_paths> ("c.sys_lib_dirs"),
+ v.insert<dir_paths> ("c.sys_inc_dirs"),
+
+ v.insert<string> ("c.std", variable_visibility::project),
+
+ v.insert<strings> ("c.poptions"),
+ v.insert<strings> ("c.coptions"),
+ v.insert<strings> ("c.loptions"),
+ v.insert<strings> ("c.aoptions"),
+ v.insert<strings> ("c.libs"),
+
+ nullptr /* c.importable_headers */,
+
+ v["cc.poptions"],
+ v["cc.coptions"],
+ v["cc.loptions"],
+ v["cc.aoptions"],
+ v["cc.libs"],
+
+ v.insert<strings> ("c.export.poptions"),
+ v.insert<strings> ("c.export.coptions"),
+ v.insert<strings> ("c.export.loptions"),
+ v.insert<vector<name>> ("c.export.libs"),
+
+ v["cc.export.poptions"],
+ v["cc.export.coptions"],
+ v["cc.export.loptions"],
+ v["cc.export.libs"],
+
+ v.insert_alias (v["cc.stdlib"], "c.stdlib"), // Same as cc.stdlib.
+
+ v["cc.runtime"],
+ v["cc.stdlib"],
+
+ v["cc.type"],
+ v["cc.system"],
+ v["cc.module_name"],
+ v["cc.reprocess"],
+
+ v.insert<string> ("c.preprocessed"), // See cxx.preprocessed.
+ nullptr, // No __symexport (no modules).
+
+ v.insert<string> ("c.id"),
+ v.insert<string> ("c.id.type"),
+ v.insert<string> ("c.id.variant"),
+
+ v.insert<string> ("c.class"),
+
+ v.insert<string> ("c.version"),
+ v.insert<uint64_t> ("c.version.major"),
+ v.insert<uint64_t> ("c.version.minor"),
+ v.insert<uint64_t> ("c.version.patch"),
+ v.insert<string> ("c.version.build"),
+
+ v.insert<string> ("c.signature"),
+ v.insert<string> ("c.checksum"),
+
+ v.insert<string> ("c.pattern"),
+
+ v.insert<target_triplet> ("c.target"),
+
+ v.insert<string> ("c.target.cpu"),
+ v.insert<string> ("c.target.vendor"),
+ v.insert<string> ("c.target.system"),
+ v.insert<string> ("c.target.version"),
+ v.insert<string> ("c.target.class")
+ };
+
+ // Alias some cc. variables as c.
+ //
+ v.insert_alias (d.c_runtime, "c.runtime");
+
+ assert (mod == nullptr);
+ config_module* m (new config_module (move (d)));
+ mod.reset (m);
+ m->guess (rs, loc, hints);
+ return true;
+ }
+
+ bool
+ config_init (scope& rs,
+ scope& bs,
+ const location& loc,
+ unique_ptr<module_base>&,
+ bool,
+ bool,
+ const variable_map& hints)
+ {
+ tracer trace ("c::config_init");
+ l5 ([&]{trace << "for " << bs;});
+
+ // We only support root loading (which means there can only be one).
+ //
+ if (&rs != &bs)
+ fail (loc) << "c.config module must be loaded in project root";
+
+ // Load c.guess.
+ //
+ if (!cast_false<bool> (rs["c.guess.loaded"]))
+ load_module (rs, rs, "c.guess", loc, false, hints);
+
+ config_module& cm (*rs.lookup_module<config_module> ("c.guess"));
+ cm.init (rs, loc, hints);
+ return true;
+ }
+
+ static const target_type* const hdr[] =
+ {
+ &h::static_type,
+ nullptr
+ };
+
+ static const target_type* const inc[] =
+ {
+ &h::static_type,
+ &c::static_type,
+ nullptr
+ };
+
+ bool
+ init (scope& rs,
+ scope& bs,
+ const location& loc,
+ unique_ptr<module_base>& mod,
+ bool,
+ bool,
+ const variable_map& hints)
+ {
+ tracer trace ("c::init");
+ l5 ([&]{trace << "for " << bs;});
+
+ // We only support root loading (which means there can only be one).
+ //
+ if (&rs != &bs)
+ fail (loc) << "c module must be loaded in project root";
+
+ // Load c.config.
+ //
+ if (!cast_false<bool> (rs["c.config.loaded"]))
+ load_module (rs, rs, "c.config", loc, false, hints);
+
+ config_module& cm (*rs.lookup_module<config_module> ("c.guess"));
+
+ cc::data d {
+ cm,
+
+ "c.compile",
+ "c.link",
+ "c.install",
+ "c.uninstall",
+
+ cm.ci_->id.type,
+ cm.ci_->id.variant,
+ cm.ci_->class_,
+ cm.ci_->version.major,
+ cm.ci_->version.minor,
+ cast<process_path> (rs[cm.x_path]),
+ cast<target_triplet> (rs[cm.x_target]),
+
+ cm.tstd,
+
+ false, // No C modules yet.
+ false, // No __symexport support since no modules.
+
+ cast<dir_paths> (rs[cm.x_sys_lib_dirs]),
+ cast<dir_paths> (rs[cm.x_sys_inc_dirs]),
+
+ cm.sys_lib_dirs_extra,
+ cm.sys_inc_dirs_extra,
+
+ c::static_type,
+ nullptr, // No C modules yet.
+ hdr,
+ inc
+ };
+
+ assert (mod == nullptr);
+ module* m;
+ mod.reset (m = new module (move (d)));
+ m->init (rs, loc, hints);
+ return true;
+ }
+
+ static const module_functions mod_functions[] =
+ {
+ // NOTE: don't forget to also update the documentation in init.hxx if
+ // changing anything here.
+
+ {"c.guess", nullptr, guess_init},
+ {"c.config", nullptr, config_init},
+ {"c", nullptr, init},
+ {nullptr, nullptr, nullptr}
+ };
+
+ const module_functions*
+ build2_c_load ()
+ {
+ return mod_functions;
+ }
+ }
+}