aboutsummaryrefslogtreecommitdiff
path: root/libbuild2/cc/init.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'libbuild2/cc/init.cxx')
-rw-r--r--libbuild2/cc/init.cxx493
1 files changed, 493 insertions, 0 deletions
diff --git a/libbuild2/cc/init.cxx b/libbuild2/cc/init.cxx
new file mode 100644
index 0000000..f45a1bf
--- /dev/null
+++ b/libbuild2/cc/init.cxx
@@ -0,0 +1,493 @@
+// file : libbuild2/cc/init.cxx -*- C++ -*-
+// copyright : Copyright (c) 2014-2019 Code Synthesis Ltd
+// license : MIT; see accompanying LICENSE file
+
+#include <libbuild2/cc/init.hxx>
+
+#include <libbuild2/file.hxx>
+#include <libbuild2/scope.hxx>
+#include <libbuild2/filesystem.hxx>
+#include <libbuild2/diagnostics.hxx>
+
+#include <libbuild2/config/utility.hxx>
+
+#include <libbuild2/cc/target.hxx>
+#include <libbuild2/cc/utility.hxx>
+
+using namespace std;
+using namespace butl;
+
+namespace build2
+{
+ namespace cc
+ {
+ // Scope operation callback that cleans up module sidebuilds.
+ //
+ static target_state
+ clean_module_sidebuilds (action, const scope& rs, const dir&)
+ {
+ context& ctx (rs.ctx);
+
+ const dir_path& out_root (rs.out_path ());
+
+ dir_path d (out_root / rs.root_extra->build_dir / modules_sidebuild_dir);
+
+ if (exists (d))
+ {
+ if (rmdir_r (ctx, d))
+ {
+ // Clean up cc/ if it became empty.
+ //
+ d = out_root / rs.root_extra->build_dir / module_dir;
+ if (empty (d))
+ {
+ rmdir (ctx, d);
+
+ // And build/ if it also became empty (e.g., in case of a build
+ // with a transient configuration).
+ //
+ d = out_root / rs.root_extra->build_dir;
+ if (empty (d))
+ rmdir (ctx, d);
+ }
+
+ return target_state::changed;
+ }
+ }
+
+ return target_state::unchanged;
+ }
+
+ bool
+ core_vars_init (scope& rs,
+ scope&,
+ const location& loc,
+ unique_ptr<module_base>&,
+ bool first,
+ bool,
+ const variable_map&)
+ {
+ tracer trace ("cc::core_vars_init");
+ l5 ([&]{trace << "for " << rs;});
+
+ assert (first);
+
+ // Load bin.vars (we need its config.bin.target/pattern for hints).
+ //
+ if (!cast_false<bool> (rs["bin.vars.loaded"]))
+ load_module (rs, rs, "bin.vars", loc);
+
+ // Enter variables. Note: some overridable, some not.
+ //
+ auto& v (rs.ctx.var_pool.rw (rs));
+
+ auto v_t (variable_visibility::target);
+
+ v.insert<strings> ("config.cc.poptions", true);
+ v.insert<strings> ("config.cc.coptions", true);
+ v.insert<strings> ("config.cc.loptions", true);
+ v.insert<strings> ("config.cc.aoptions", true);
+ v.insert<strings> ("config.cc.libs", true);
+
+ v.insert<strings> ("cc.poptions");
+ v.insert<strings> ("cc.coptions");
+ v.insert<strings> ("cc.loptions");
+ v.insert<strings> ("cc.aoptions");
+ v.insert<strings> ("cc.libs");
+
+ v.insert<strings> ("cc.export.poptions");
+ v.insert<strings> ("cc.export.coptions");
+ v.insert<strings> ("cc.export.loptions");
+ v.insert<vector<name>> ("cc.export.libs");
+
+ // Hint variables (not overridable).
+ //
+ v.insert<string> ("config.cc.id");
+ v.insert<string> ("config.cc.hinter"); // Hinting module.
+ v.insert<string> ("config.cc.pattern");
+ v.insert<target_triplet> ("config.cc.target");
+
+ // Compiler runtime and C standard library.
+ //
+ v.insert<string> ("cc.runtime");
+ v.insert<string> ("cc.stdlib");
+
+ // Target type, for example, "C library" or "C++ library". Should be set
+ // on the target as a rule-specific variable by the matching rule to the
+ // name of the module (e.g., "c", "cxx"). Currenly only set for
+ // libraries and is used to decide which *.libs to use during static
+ // linking.
+ //
+ // It can also be the special "cc" value which means a C-common library
+ // but specific language is not known. Used in the import installed
+ // logic.
+ //
+ v.insert<string> ("cc.type", v_t);
+
+ // If set and is true, then this (imported) library has been found in a
+ // system library search directory.
+ //
+ v.insert<bool> ("cc.system", v_t);
+
+ // C++ module name. Set on the bmi*{} target as a rule-specific variable
+ // by the matching rule. Can also be set by the user (normally via the
+ // x.module_name alias) on the x_mod{} source.
+ //
+ v.insert<string> ("cc.module_name", v_t);
+
+ // Ability to disable using preprocessed output for compilation.
+ //
+ v.insert<bool> ("config.cc.reprocess", true);
+ v.insert<bool> ("cc.reprocess");
+
+ // Register scope operation callback.
+ //
+ // It feels natural to do clean up sidebuilds as a post operation but
+ // that prevents the (otherwise-empty) out root directory to be cleaned
+ // up (via the standard fsdir{} chain).
+ //
+ rs.operation_callbacks.emplace (
+ perform_clean_id,
+ scope::operation_callback {&clean_module_sidebuilds, nullptr /*post*/});
+
+ return true;
+ }
+
+ bool
+ core_guess_init (scope& rs,
+ scope&,
+ const location& loc,
+ unique_ptr<module_base>&,
+ bool first,
+ bool,
+ const variable_map& h)
+ {
+ tracer trace ("cc::core_guess_init");
+ l5 ([&]{trace << "for " << rs;});
+
+ assert (first);
+
+ // Load cc.core.vars.
+ //
+ if (!cast_false<bool> (rs["cc.core.vars.loaded"]))
+ load_module (rs, rs, "cc.core.vars", loc);
+
+ // config.cc.{id,hinter}
+ //
+ {
+ // These values must be hinted.
+ //
+ rs.assign<string> ("cc.id") = cast<string> (h["config.cc.id"]);
+ rs.assign<string> ("cc.hinter") = cast<string> (h["config.cc.hinter"]);
+ }
+
+ // config.cc.target
+ //
+ {
+ // This value must be hinted.
+ //
+ const auto& t (cast<target_triplet> (h["config.cc.target"]));
+
+ // Also enter as cc.target.{cpu,vendor,system,version,class} for
+ // convenience of access.
+ //
+ rs.assign<string> ("cc.target.cpu") = t.cpu;
+ rs.assign<string> ("cc.target.vendor") = t.vendor;
+ rs.assign<string> ("cc.target.system") = t.system;
+ rs.assign<string> ("cc.target.version") = t.version;
+ rs.assign<string> ("cc.target.class") = t.class_;
+
+ rs.assign<target_triplet> ("cc.target") = t;
+ }
+
+ // config.cc.pattern
+ //
+ {
+ // This value could be hinted.
+ //
+ rs.assign<string> ("cc.pattern") =
+ cast_empty<string> (h["config.cc.pattern"]);
+ }
+
+ // cc.runtime
+ // cc.stdlib
+ //
+ rs.assign ("cc.runtime") = cast<string> (h["cc.runtime"]);
+ rs.assign ("cc.stdlib") = cast<string> (h["cc.stdlib"]);
+
+ return true;
+ }
+
+ bool
+ core_config_init (scope& rs,
+ scope&,
+ const location& loc,
+ unique_ptr<module_base>&,
+ bool first,
+ bool,
+ const variable_map& hints)
+ {
+ tracer trace ("cc::core_config_init");
+ l5 ([&]{trace << "for " << rs;});
+
+ assert (first);
+
+ // Load cc.core.guess.
+ //
+ if (!cast_false<bool> (rs["cc.core.guess.loaded"]))
+ load_module (rs, rs, "cc.core.guess", loc);
+
+ // Configure.
+ //
+
+ // Adjust module priority (compiler).
+ //
+ config::save_module (rs, "cc", 250);
+
+ // Note that we are not having a config report since it will just
+ // duplicate what has already been printed by the hinting module.
+
+ // config.cc.{p,c,l}options
+ // config.cc.libs
+ //
+ // @@ Same nonsense as in module.
+ //
+ //
+ rs.assign ("cc.poptions") += cast_null<strings> (
+ config::optional (rs, "config.cc.poptions"));
+
+ rs.assign ("cc.coptions") += cast_null<strings> (
+ config::optional (rs, "config.cc.coptions"));
+
+ rs.assign ("cc.loptions") += cast_null<strings> (
+ config::optional (rs, "config.cc.loptions"));
+
+ rs.assign ("cc.aoptions") += cast_null<strings> (
+ config::optional (rs, "config.cc.aoptions"));
+
+ rs.assign ("cc.libs") += cast_null<strings> (
+ config::optional (rs, "config.cc.libs"));
+
+ if (lookup l = config::omitted (rs, "config.cc.reprocess").first)
+ rs.assign ("cc.reprocess") = *l;
+
+ // Load the bin.config module.
+ //
+ if (!cast_false<bool> (rs["bin.config.loaded"]))
+ {
+ // Prepare configuration hints. They are only used on the first load
+ // of bin.config so we only populate them on our first load.
+ //
+ variable_map h (rs.ctx);
+
+ if (first)
+ {
+ // Note that all these variables have already been registered.
+ //
+ h.assign ("config.bin.target") =
+ cast<target_triplet> (rs["cc.target"]).string ();
+
+ if (auto l = hints["config.bin.pattern"])
+ h.assign ("config.bin.pattern") = cast<string> (l);
+ }
+
+ load_module (rs, rs, "bin.config", loc, false, h);
+ }
+
+ // Verify bin's target matches ours (we do it even if we loaded it
+ // ourselves since the target can come from the configuration and not
+ // our hint).
+ //
+ if (first)
+ {
+ const auto& ct (cast<target_triplet> (rs["cc.target"]));
+ const auto& bt (cast<target_triplet> (rs["bin.target"]));
+
+ if (bt != ct)
+ {
+ const auto& h (cast<string> (rs["cc.hinter"]));
+
+ fail (loc) << h << " and bin module target mismatch" <<
+ info << h << " target is " << ct <<
+ info << "bin target is " << bt;
+ }
+ }
+
+ // Load bin.*.config for bin.* modules we may need (see core_init()
+ // below).
+ //
+ const string& tsys (cast<string> (rs["cc.target.system"]));
+
+ if (!cast_false<bool> (rs["bin.ar.config.loaded"]))
+ load_module (rs, rs, "bin.ar.config", loc);
+
+ if (tsys == "win32-msvc")
+ {
+ if (!cast_false<bool> (rs["bin.ld.config.loaded"]))
+ load_module (rs, rs, "bin.ld.config", loc);
+ }
+
+ if (tsys == "mingw32")
+ {
+ if (!cast_false<bool> (rs["bin.rc.config.loaded"]))
+ load_module (rs, rs, "bin.rc.config", loc);
+ }
+
+ return true;
+ }
+
+ bool
+ core_init (scope& rs,
+ scope&,
+ const location& loc,
+ unique_ptr<module_base>&,
+ bool first,
+ bool,
+ const variable_map& hints)
+ {
+ tracer trace ("cc::core_init");
+ l5 ([&]{trace << "for " << rs;});
+
+ assert (first);
+
+ const string& tsys (cast<string> (rs["cc.target.system"]));
+
+ // Load cc.core.config.
+ //
+ if (!cast_false<bool> (rs["cc.core.config.loaded"]))
+ load_module (rs, rs, "cc.core.config", loc, false, hints);
+
+ // Load the bin module.
+ //
+ if (!cast_false<bool> (rs["bin.loaded"]))
+ load_module (rs, rs, "bin", loc);
+
+ // Load the bin.ar module.
+ //
+ if (!cast_false<bool> (rs["bin.ar.loaded"]))
+ load_module (rs, rs, "bin.ar", loc);
+
+ // For this target we link things directly with link.exe so load the
+ // bin.ld module.
+ //
+ if (tsys == "win32-msvc")
+ {
+ if (!cast_false<bool> (rs["bin.ld.loaded"]))
+ load_module (rs, rs, "bin.ld", loc);
+ }
+
+ // If our target is MinGW, then we will need the resource compiler
+ // (windres) in order to embed manifests into executables.
+ //
+ if (tsys == "mingw32")
+ {
+ if (!cast_false<bool> (rs["bin.rc.loaded"]))
+ load_module (rs, rs, "bin.rc", loc);
+ }
+
+ return true;
+ }
+
+ // The cc module is an "alias" for c and cxx. Its intended use is to make
+ // sure that the C/C++ configuration is captured in an amalgamation rather
+ // than subprojects.
+ //
+ static inline bool
+ init_alias (tracer& trace,
+ scope& rs,
+ scope& bs,
+ const char* m,
+ const char* c,
+ const char* c_loaded,
+ const char* cxx,
+ const char* cxx_loaded,
+ const location& loc,
+ const variable_map& hints)
+ {
+ l5 ([&]{trace << "for " << bs;});
+
+ // We only support root loading (which means there can only be one).
+ //
+ if (&rs != &bs)
+ fail (loc) << m << " module must be loaded in project root";
+
+ // We want to order the loading to match what user specified on the
+ // command line (config.c or config.cxx). This way the first loaded
+ // module (with user-specified config.*) will hint the compiler to the
+ // second.
+ //
+ bool lc (!cast_false<bool> (rs[c_loaded]));
+ bool lp (!cast_false<bool> (rs[cxx_loaded]));
+
+ // If none of them are already loaded, load c first only if config.c
+ // is specified.
+ //
+ if (lc && lp && rs["config.c"])
+ {
+ load_module (rs, rs, c, loc, false, hints);
+ load_module (rs, rs, cxx, loc, false, hints);
+ }
+ else
+ {
+ if (lp) load_module (rs, rs, cxx, loc, false, hints);
+ if (lc) load_module (rs, rs, c, loc, false, 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 ("cc::config_init");
+ return init_alias (trace, rs, bs,
+ "cc.config",
+ "c.config", "c.config.loaded",
+ "cxx.config", "cxx.config.loaded",
+ loc, hints);
+ }
+
+ bool
+ init (scope& rs,
+ scope& bs,
+ const location& loc,
+ unique_ptr<module_base>&,
+ bool,
+ bool,
+ const variable_map& hints)
+ {
+ tracer trace ("cc::init");
+ return init_alias (trace, rs, bs,
+ "cc",
+ "c", "c.loaded",
+ "cxx", "cxx.loaded",
+ loc, hints);
+ }
+
+ static const module_functions mod_functions[] =
+ {
+ // NOTE: don't forget to also update the documentation in init.hxx if
+ // changing anything here.
+
+ {"cc.core.vars", nullptr, core_vars_init},
+ {"cc.core.guess", nullptr, core_guess_init},
+ {"cc.core.config", nullptr, core_config_init},
+ {"cc.core", nullptr, core_init},
+ {"cc.config", nullptr, config_init},
+ {"cc", nullptr, init},
+ {nullptr, nullptr, nullptr}
+ };
+
+ const module_functions*
+ build2_cc_load ()
+ {
+ return mod_functions;
+ }
+ }
+}