From a4796d5b851dac4a546f89c282f65e320076eb10 Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Sat, 27 Aug 2016 09:53:30 +0200 Subject: Clean up library export, make c and cxx modules project root only So now c and cxx modules can only be loaded in project root scope (normally root.build). Also, the c.std and cxx.std must now be set *before* loading the module to take effect. This means we won't be able to handle old buildfiles anymore but old versions of build2 should be able to handle new *.std placement. --- build/root.build | 3 +- build2/buildfile | 1 + build2/c/init.cxx | 274 ++++++++++++++++++++------------------ build2/cc/common | 43 +++--- build2/cc/compile.cxx | 24 ++-- build2/cc/gcc.cxx | 128 ++++++++++++++++++ build2/cc/init.cxx | 233 ++++++++++++++++---------------- build2/cc/link | 34 ++--- build2/cc/link.cxx | 230 +++++++++++++------------------- build2/cc/module | 29 ++-- build2/cc/module.cxx | 331 ++++++++++++++++++++++++---------------------- build2/cc/msvc.cxx | 9 +- build2/cc/pkgconfig.cxx | 27 ++-- build2/cxx/init.cxx | 272 +++++++++++++++++++------------------ build2/pkgconfig/init.cxx | 4 +- 15 files changed, 882 insertions(+), 760 deletions(-) create mode 100644 build2/cc/gcc.cxx diff --git a/build/root.build b/build/root.build index 9ed207f..0851534 100644 --- a/build/root.build +++ b/build/root.build @@ -2,6 +2,8 @@ # copyright : Copyright (c) 2014-2016 Code Synthesis Ltd # license : MIT; see accompanying LICENSE file +cxx.std = 14 + using cxx hxx{*}: extension = @@ -9,7 +11,6 @@ ixx{*}: extension = ixx txx{*}: extension = txx cxx{*}: extension = cxx -cxx.std = 14 cxx.poptions =+ -I$out_root -I$src_root # All exe{} in tests/ are, well, tests. Don't install them. diff --git a/build2/buildfile b/build2/buildfile index ad5a023..b24fc15 100644 --- a/build2/buildfile +++ b/build2/buildfile @@ -42,6 +42,7 @@ exe{b}: \ c/{hxx }{ target } \ cc/{hxx }{ common } \ cc/{hxx cxx}{ compile } \ + cc/{ cxx}{ gcc } \ cc/{hxx cxx}{ guess } \ cc/{hxx cxx}{ init } \ cc/{hxx cxx}{ install } \ diff --git a/build2/c/init.cxx b/build2/c/init.cxx index c846ae1..4032e07 100644 --- a/build2/c/init.cxx +++ b/build2/c/init.cxx @@ -8,6 +8,7 @@ #include #include +#include #include #include @@ -19,24 +20,29 @@ namespace build2 { namespace c { - using cc::config_module; + using cc::compiler_info; - class module: public cc::module + class config_module: public cc::config_module { public: explicit - module (data&& d): common (move (d)), cc::module (move (d)) {} + config_module (config_data&& d) + : config_data (move (d)), cc::config_module (move (d)) {} - bool - translate_std (string&, scope&, const value&) const override; + string + translate_std (const compiler_info&, + scope&, + const string&) const override; }; - bool module:: - translate_std (string& s, scope& r, const value& val) const + using cc::module; + + string config_module:: + translate_std (const compiler_info& ci, scope& rs, const string& v) const { - const string& v (cast (val)); + string r; - if (cid == "msvc") + if (ci.id.type == "msvc") { // Standard-wise, with VC you get what you get. The question is // whether we should verify that the requested standard is provided by @@ -56,128 +62,129 @@ namespace build2 // if (v != "90") { - uint64_t cver (cast (r[x_version_major])); + uint64_t cver (ci.version.major); if ((v == "99" && cver < 16) || // Since VS2010/10.0. (v == "11" && cver < 17)) // Since VS2012/11.0. { - fail << "C" << v << " is not supported by " - << cast (r[x_signature]) << - info << "required by " << project (r) << '@' << r.out_path (); + fail << "C" << v << " is not supported by " << ci.signature << + info << "required by " << project (rs) << '@' << rs.out_path (); } } - - return false; } else { // 90 and 89 are the same standard. Translate 99 to 9x and 11 to 1x // for compatibility with older versions of the compilers. // - s = "-std="; + r = "-std="; if (v == "90") - s += "c90"; + r += "c90"; else if (v == "99") - s += "c9x"; + r += "c9x"; else if (v == "11") - s += "c1x"; + r += "c1x"; else - s += v; // In case the user specifies something like 'gnu11'. - - return true; + r += v; // In case the user specifies something like 'gnu11'. } + + return r; } bool - config_init (scope& r, - scope& b, + config_init (scope& rs, + scope& bs, const location& loc, - unique_ptr& m, - bool first, + unique_ptr& mod, + bool, bool, const variable_map& hints) { tracer trace ("c::config_init"); - l5 ([&]{trace << "for " << b.out_path ();}); + l5 ([&]{trace << "for " << bs.out_path ();}); - if (first) - { - // Load cc.core.vars so that we can cache all the cc.* variables. - // - if (!cast_false (b["cc.core.vars.loaded"])) - load_module ("cc.core.vars", r, b, loc); + // 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"; - // Enter all the variables and initialize the module data. - // - auto& v (var_pool); - - cc::config_data d { - cc::lang::c, - - "c", - "c", - "gcc", - - // Note: some overridable, some not. - // - v.insert ("config.c", true), - v.insert ("config.c.poptions", true), - v.insert ("config.c.coptions", true), - v.insert ("config.c.loptions", true), - v.insert ("config.c.libs", true), - - v.insert ("c.path"), - v.insert ("c.poptions"), - v.insert ("c.coptions"), - v.insert ("c.loptions"), - v.insert ("c.libs"), - - v["cc.poptions"], - v["cc.coptions"], - v["cc.loptions"], - v["cc.libs"], - - v.insert ("c.export.poptions"), - v.insert ("c.export.coptions"), - v.insert ("c.export.loptions"), - v.insert ("c.export.libs"), - - v["cc.export.poptions"], - v["cc.export.coptions"], - v["cc.export.loptions"], - v["cc.export.libs"], - - v["cc.type"], - - v.insert ("c.std", true), - - v.insert ("c.id"), - v.insert ("c.id.type"), - v.insert ("c.id.variant"), - - v.insert ("c.version"), - v.insert ("c.version.major"), - v.insert ("c.version.minor"), - v.insert ("c.version.patch"), - v.insert ("c.version.build"), - - v.insert ("c.signature"), - v.insert ("c.checksum"), - - v.insert ("c.target"), - v.insert ("c.target.cpu"), - v.insert ("c.target.vendor"), - v.insert ("c.target.system"), - v.insert ("c.target.version"), - v.insert ("c.target.class") - }; - - assert (m == nullptr); - m.reset (new config_module (move (d))); - } + // Load cc.core.vars so that we can cache all the cc.* variables. + // + if (!cast_false (rs["cc.core.vars.loaded"])) + load_module ("cc.core.vars", rs, rs, loc); - static_cast (*m).init (r, b, loc, first, hints); + // Enter all the variables and initialize the module data. + // + auto& v (var_pool); + + cc::config_data d { + cc::lang::c, + + "c", + "c", + "gcc", + + // Note: some overridable, some not. + // + v.insert ("config.c", true), + v.insert ("config.c.poptions", true), + v.insert ("config.c.coptions", true), + v.insert ("config.c.loptions", true), + v.insert ("config.c.libs", true), + + v.insert ("c.path"), + v.insert ("c.sys_lib_dirs"), + + v.insert ("c.poptions"), + v.insert ("c.coptions"), + v.insert ("c.loptions"), + v.insert ("c.libs"), + + v["cc.poptions"], + v["cc.coptions"], + v["cc.loptions"], + v["cc.libs"], + + v.insert ("c.export.poptions"), + v.insert ("c.export.coptions"), + v.insert ("c.export.loptions"), + v.insert ("c.export.libs"), + + v["cc.export.poptions"], + v["cc.export.coptions"], + v["cc.export.loptions"], + v["cc.export.libs"], + + v["cc.type"], + + v.insert ("c.std", true), + + v.insert ("c.id"), + v.insert ("c.id.type"), + v.insert ("c.id.variant"), + + v.insert ("c.version"), + v.insert ("c.version.major"), + v.insert ("c.version.minor"), + v.insert ("c.version.patch"), + v.insert ("c.version.build"), + + v.insert ("c.signature"), + v.insert ("c.checksum"), + + v.insert ("c.target"), + v.insert ("c.target.cpu"), + v.insert ("c.target.vendor"), + v.insert ("c.target.system"), + v.insert ("c.target.version"), + v.insert ("c.target.class") + }; + + assert (mod == nullptr); + config_module* m; + mod.reset (m = new config_module (move (d))); + m->init (rs, loc, hints); return true; } @@ -195,51 +202,56 @@ namespace build2 }; bool - init (scope& r, - scope& b, + init (scope& rs, + scope& bs, const location& loc, - unique_ptr& m, - bool first, + unique_ptr& mod, + bool, bool, const variable_map& hints) { tracer trace ("c::init"); - l5 ([&]{trace << "for " << b.out_path ();}); + l5 ([&]{trace << "for " << bs.out_path ();}); + + // 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 (b["c.config.loaded"])) - load_module ("c.config", r, b, loc, false, hints); + if (!cast_false (rs["c.config.loaded"])) + load_module ("c.config", rs, rs, loc, false, hints); - if (first) - { - config_module& cm (*r.modules.lookup ("c.config")); + config_module& cm (*rs.modules.lookup ("c.config")); - cc::data d { - cm, + cc::data d { + cm, - "c.compile", - "c.link", - "c.install", - "c.uninstall", + "c.compile", + "c.link", + "c.install", + "c.uninstall", - cast (r[cm.x_id]), - cast (r[cm.x_target]), - cast (r[cm.x_target_system]), - cast (r[cm.x_target_class]), + cast (rs[cm.x_id]), + cast (rs[cm.x_target]), + cast (rs[cm.x_target_system]), + cast (rs[cm.x_target_class]), - cast_null (r["pkgconfig.path"]), + cm.tstd, - c::static_type, - hdr, - inc - }; + cast_null (rs["pkgconfig.path"]), + cast (rs[cm.x_sys_lib_dirs]), - assert (m == nullptr); - m.reset (new module (move (d))); - } + c::static_type, + hdr, + inc + }; - static_cast (*m).init (r, b, loc, first, hints); + assert (mod == nullptr); + module* m; + mod.reset (m = new module (move (d))); + m->init (rs, loc, hints); return true; } } diff --git a/build2/cc/common b/build2/cc/common index c25683c..41bcef5 100644 --- a/build2/cc/common +++ b/build2/cc/common @@ -36,7 +36,9 @@ namespace build2 const variable& config_x_loptions; const variable& config_x_libs; - const variable& x_path; + const variable& x_path; // Compiler process path. + const variable& x_sys_lib_dirs; // System library search directories. + const variable& x_poptions; const variable& x_coptions; const variable& x_loptions; @@ -89,14 +91,17 @@ namespace build2 const char* x_install; const char* x_uninstall; - // Cached values for some commonly-used variables. + // Cached values for some commonly-used variables/values. // const string& cid; // x.id const string& ctg; // x.target const string& tsys; // x.target.system const string& tclass; // x.target.class + const string& tstd; // Translated x_std value (can be empty). + const process_path* pkgconfig; // pkgconfig.path (can be NULL). + const dir_paths& sys_lib_dirs; // x.sys_lib_dirs const target_type& x_src; // Source target type (c{}, cxx{}). @@ -131,9 +136,11 @@ namespace build2 const char* uninstall, const string& id, const string& tg, - const string& sys, - const string& class_, + const string& ts, + const string& tc, + const string& std, const process_path* pkgc, + const dir_paths& sld, const target_type& src, const target_type* const* hdr, const target_type* const* inc) @@ -142,8 +149,8 @@ namespace build2 x_link (link), x_install (install), x_uninstall (uninstall), - cid (id), ctg (tg), tsys (sys), tclass (class_), - pkgconfig (pkgc), + cid (id), ctg (tg), tsys (ts), tclass (tc), + tstd (std), pkgconfig (pkgc), sys_lib_dirs (sld), x_src (src), x_hdr (hdr), x_inc (inc) {} }; @@ -152,31 +159,21 @@ namespace build2 public: common (data&& d): data (move (d)) {} - // Language standard (x.std) mapping. T is either target or scope. + // Language standard (x.std) mapping. // - template void - append_std (cstrings& args, scope& root, T& t, string& storage) const + append_std (cstrings& args) const { - if (auto l = t[x_std]) - if (translate_std (storage, root, *l)) - args.push_back (storage.c_str ()); + if (!tstd.empty ()) + args.push_back (tstd.c_str ()); } - template void - hash_std (sha256& csum, scope& root, T& t) const + hash_std (sha256& cs) const { - string s; - if (auto l = t[x_std]) - if (translate_std (s, root, *l)) - csum.append (s); + if (!tstd.empty ()) + cs.append (tstd); } - - // Return true if there is an option (stored in the first argument). - // - virtual bool - translate_std (string&, scope&, const value&) const = 0; }; } } diff --git a/build2/cc/compile.cxx b/build2/cc/compile.cxx index fdd4231..20cd392 100644 --- a/build2/cc/compile.cxx +++ b/build2/cc/compile.cxx @@ -82,7 +82,8 @@ namespace build2 append_options (args, l, var); }; - link_.process_libraries (l, l.is_a (), true, nullptr, opt); + link_.process_libraries ( + sys_lib_dirs, l, l.is_a (), true, nullptr, opt); } void compile:: @@ -102,7 +103,8 @@ namespace build2 hash_options (cs, l, var); }; - link_.process_libraries (l, l.is_a (), true, nullptr, opt); + link_.process_libraries ( + sys_lib_dirs, l, l.is_a (), true, nullptr, opt); } recipe compile:: @@ -172,7 +174,7 @@ namespace build2 // When cleaning, ignore prerequisites that are not in the same or a // subdirectory of our project root. // - optional lib_paths; // Extract lazily. + optional usr_lib_dirs; // Extract lazily. lorder lo; if (a.operation () == update_id) @@ -197,7 +199,7 @@ namespace build2 // if (p.proj () == nullptr || link_.search_library ( - lib_paths, p.prerequisite, lo) == nullptr) + sys_lib_dirs, usr_lib_dirs, p.prerequisite, lo) == nullptr) { match_only (a, p.search ()); } @@ -280,7 +282,7 @@ namespace build2 hash_options (cs, t, x_poptions); hash_options (cs, t, c_coptions); hash_options (cs, t, x_coptions); - hash_std (cs, rs, t); + hash_std (cs); if (ct == otype::s) { @@ -461,7 +463,8 @@ namespace build2 append_prefixes (m, l, var); }; - link_.process_libraries (l, l.is_a (), true, nullptr, opt); + link_.process_libraries ( + sys_lib_dirs, l, l.is_a (), true, nullptr, opt); } auto compile:: @@ -689,9 +692,8 @@ namespace build2 // const process_path* xc (nullptr); cstrings args; - string std; // Storage. - auto init_args = [&t, lo, &src, &rs, &xc, &args, &std, this] () + auto init_args = [&t, lo, &src, &rs, &xc, &args, this] () { xc = &cast (rs[x_path]); args.push_back (xc->recall_string ()); @@ -719,7 +721,7 @@ namespace build2 append_options (args, t, c_coptions); append_options (args, t, x_coptions); - append_std (args, rs, t, std); + append_std (args); if (t.is_a ()) { @@ -1388,9 +1390,9 @@ namespace build2 append_options (args, t, c_coptions); append_options (args, t, x_coptions); - string std, out, out1; // Storage. + string out, out1; // Storage. - append_std (args, rs, t, std); + append_std (args); if (cid == "msvc") { diff --git a/build2/cc/gcc.cxx b/build2/cc/gcc.cxx new file mode 100644 index 0000000..a6c7448 --- /dev/null +++ b/build2/cc/gcc.cxx @@ -0,0 +1,128 @@ +// file : build2/cc/gcc.cxx -*- C++ -*- +// copyright : Copyright (c) 2014-2016 Code Synthesis Ltd +// license : MIT; see accompanying LICENSE file + +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include + +using namespace std; +using namespace butl; + +namespace build2 +{ + namespace cc + { + using namespace bin; + + // Extract system library search paths from GCC (gcc/g++) or compatible + // (Clang, Intel) using the -print-search-dirs option. + // + dir_paths config_module:: + gcc_library_search_paths (process_path& xc, scope& rs) const + { + dir_paths r; + + cstrings args; + string std; // Storage. + + args.push_back (xc.recall_string ()); + append_options (args, rs, c_coptions); + append_options (args, rs, x_coptions); + if (!tstd.empty ()) args.push_back (tstd.c_str ()); + append_options (args, rs, c_loptions); + append_options (args, rs, x_loptions); + args.push_back ("-print-search-dirs"); + args.push_back (nullptr); + + if (verb >= 3) + print_process (args); + + string l; + try + { + process pr (xc, args.data (), 0, -1); // Open pipe to stdout. + + try + { + ifdstream is (pr.in_ofd, fdstream_mode::skip, ifdstream::badbit); + + string s; + while (getline (is, s)) + { + if (s.compare (0, 12, "libraries: =") == 0) + { + l.assign (s, 12, string::npos); + break; + } + } + + is.close (); // Don't block. + + if (!pr.wait ()) + throw failed (); + } + catch (const ifdstream::failure&) + { + pr.wait (); + fail << "error reading " << x_lang << " compiler -print-search-dirs " + << "output"; + } + } + catch (const process_error& e) + { + error << "unable to execute " << args[0] << ": " << e.what (); + + if (e.child ()) + exit (1); + + throw failed (); + } + + if (l.empty ()) + fail << "unable to extract " << x_lang << " compiler system library " + << "search paths"; + + // Now the fun part: figuring out which delimiter is used. Normally it + // is ':' but on Windows it is ';' (or can be; who knows for sure). Also + // note that these paths are absolute (or should be). So here is what we + // are going to do: first look for ';'. If found, then that's the + // delimiter. If not found, then there are two cases: it is either a + // single Windows path or the delimiter is ':'. To distinguish these two + // cases we check if the path starts with a Windows drive. + // + char d (';'); + string::size_type e (l.find (d)); + + if (e == string::npos && + (l.size () < 2 || l[0] == '/' || l[1] != ':')) + { + d = ':'; + e = l.find (d); + } + + // Now chop it up. We already have the position of the first delimiter + // (if any). + // + for (string::size_type b (0);; e = l.find (d, (b = e + 1))) + { + r.emplace_back (l, b, (e != string::npos ? e - b : e)); + r.back ().normalize (); + + if (e == string::npos) + break; + } + + return r; + } + } +} diff --git a/build2/cc/init.cxx b/build2/cc/init.cxx index 066367d..42412a7 100644 --- a/build2/cc/init.cxx +++ b/build2/cc/init.cxx @@ -74,8 +74,8 @@ namespace build2 } bool - core_config_init (scope& r, - scope& b, + core_config_init (scope& rs, + scope&, const location& loc, unique_ptr&, bool first, @@ -83,96 +83,93 @@ namespace build2 const variable_map& hints) { tracer trace ("cc::core_config_init"); - l5 ([&]{trace << "for " << b.out_path ();}); + l5 ([&]{trace << "for " << rs.out_path ();}); + + assert (first); // Load cc.core.vars. // - if (first) - { - if (!cast_false (b["cc.core.vars.loaded"])) - load_module ("cc.core.vars", r, b, loc); - } + if (!cast_false (rs["cc.core.vars.loaded"])) + load_module ("cc.core.vars", rs, rs, loc); // Configure. // - if (first) + + // Adjust module priority (compiler). + // + config::save_module (rs, "cc", 250); + + // config.cc.id + // { - // Adjust module priority (compiler). + // This value must be hinted. // - config::save_module (r, "cc", 250); + rs.assign ("cc.id") = cast (hints["config.cc.id"]); + } - // config.cc.id + // config.cc.target + // + { + // This value must be hinted and already canonicalized. // + const string& s (cast (hints["config.cc.target"])); + + try { - // This value must be hinted. + //@@ We do it in the hinting module and here. Any way not to + // duplicate the effort? Maybe move the splitting here and + // simply duplicate the values there? // - r.assign ("cc.id") = cast (hints["config.cc.id"]); - } + triplet t (s); - // config.cc.target - // - { - // This value must be hinted and already canonicalized. + // Enter as cc.target.{cpu,vendor,system,version,class}. // - const string& s (cast (hints["config.cc.target"])); - - try - { - //@@ We do it in the hinting module and here. Any way not to - // duplicate the effort? Maybe move the splitting here and - // simply duplicate the values there? - // - triplet t (s); - - // Enter as cc.target.{cpu,vendor,system,version,class}. - // - r.assign ("cc.target") = s; - r.assign ("cc.target.cpu") = move (t.cpu); - r.assign ("cc.target.vendor") = move (t.vendor); - r.assign ("cc.target.system") = move (t.system); - r.assign ("cc.target.version") = move (t.version); - r.assign ("cc.target.class") = move (t.class_); - } - catch (const invalid_argument& e) - { - assert (false); // Should have been caught by the hinting module. - } + rs.assign ("cc.target") = s; + rs.assign ("cc.target.cpu") = move (t.cpu); + rs.assign ("cc.target.vendor") = move (t.vendor); + rs.assign ("cc.target.system") = move (t.system); + rs.assign ("cc.target.version") = move (t.version); + rs.assign ("cc.target.class") = move (t.class_); } - - // config.cc.pattern - // + catch (const invalid_argument& e) { - // This value could be hinted. - // - if (auto l = hints["config.cc.pattern"]) - r.assign ("cc.pattern") = cast (l); + assert (false); // Should have been caught by the hinting module. } + } - // 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.pattern + // + { + // This value could be hinted. + // + if (auto l = hints["config.cc.pattern"]) + rs.assign ("cc.pattern") = cast (l); } + // 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. // // - b.assign ("cc.poptions") += cast_null ( - config::optional (r, "config.cc.poptions")); + rs.assign ("cc.poptions") += cast_null ( + config::optional (rs, "config.cc.poptions")); - b.assign ("cc.coptions") += cast_null ( - config::optional (r, "config.cc.coptions")); + rs.assign ("cc.coptions") += cast_null ( + config::optional (rs, "config.cc.coptions")); - b.assign ("cc.loptions") += cast_null ( - config::optional (r, "config.cc.loptions")); + rs.assign ("cc.loptions") += cast_null ( + config::optional (rs, "config.cc.loptions")); - b.assign ("cc.libs") += cast_null ( - config::optional (r, "config.cc.libs")); + rs.assign ("cc.libs") += cast_null ( + config::optional (rs, "config.cc.libs")); // Load the bin.config module. // - if (!cast_false (b["bin.config.loaded"])) + if (!cast_false (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. @@ -180,13 +177,13 @@ namespace build2 variable_map h; if (first) { - h.assign ("config.bin.target") = cast (r["cc.target"]); + h.assign ("config.bin.target") = cast (rs["cc.target"]); if (auto l = hints["config.bin.pattern"]) - h.assign ("config.bin.pattern") = cast (l); + h.assign ("config.bin.pattern") = cast (l); } - load_module ("bin.config", r, b, loc, false, h); + load_module ("bin.config", rs, rs, loc, false, h); } // Verify bin's target matches ours (we do it even if we loaded it @@ -195,8 +192,8 @@ namespace build2 // if (first) { - const string& ct (cast (r["cc.target"])); - const string& bt (cast (r["bin.target"])); + const string& ct (cast (rs["cc.target"])); + const string& bt (cast (rs["bin.target"])); if (bt != ct) fail (loc) << "cc and bin module target mismatch" << @@ -204,31 +201,31 @@ namespace build2 info << "bin.target is " << bt; } - const string& cid (cast (r["cc.id"])); - const string& tsys (cast (r["cc.target.system"])); + const string& cid (cast (rs["cc.id"])); + const string& tsys (cast (rs["cc.target.system"])); // Load bin.*.config for bin.* modules we may need (see core_init() // below). // - if (auto l = r["config.bin.lib"]) + if (auto l = rs["config.bin.lib"]) { if (cast (l) != "shared") { - if (!cast_false (b["bin.ar.config.loaded"])) - load_module ("bin.ar.config", r, b, loc); + if (!cast_false (rs["bin.ar.config.loaded"])) + load_module ("bin.ar.config", rs, rs, loc); } } if (cid == "msvc") { - if (!cast_false (b["bin.ld.config.loaded"])) - load_module ("bin.ld.config", r, b, loc); + if (!cast_false (rs["bin.ld.config.loaded"])) + load_module ("bin.ld.config", rs, rs, loc); } if (tsys == "mingw32") { - if (!cast_false (b["bin.rc.config.loaded"])) - load_module ("bin.rc.config", r, b, loc); + if (!cast_false (rs["bin.rc.config.loaded"])) + load_module ("bin.rc.config", rs, rs, loc); } // Load (optionally) the pkgconfig.config module. @@ -238,53 +235,55 @@ namespace build2 // doesn't set pkgconfig.target. Perhaps only set if it was used // to derive the program name? // - if (first && !cast_false (b["pkgconfig.config.loaded"])) + if (!cast_false (rs["pkgconfig.config.loaded"])) { // Prepare configuration hints. // variable_map h; - h.assign ("config.pkgconfig.target") = cast (r["cc.target"]); + h.assign ("config.pkgconfig.target") = cast (rs["cc.target"]); - load_module ("pkgconfig.config", r, r, loc, true, h); + load_module ("pkgconfig.config", rs, rs, loc, true, h); } return true; } bool - core_init (scope& r, - scope& b, + core_init (scope& rs, + scope&, const location& loc, unique_ptr&, - bool, + bool first, bool, const variable_map& hints) { tracer trace ("cc::core_init"); - l5 ([&]{trace << "for " << b.out_path ();}); + l5 ([&]{trace << "for " << rs.out_path ();}); + + assert (first); // Load cc.core.config. // - if (!cast_false (b["cc.core.config.loaded"])) - load_module ("cc.core.config", r, b, loc, false, hints); + if (!cast_false (rs["cc.core.config.loaded"])) + load_module ("cc.core.config", rs, rs, loc, false, hints); // Load the bin module. // - if (!cast_false (b["bin.loaded"])) - load_module ("bin", r, b, loc); + if (!cast_false (rs["bin.loaded"])) + load_module ("bin", rs, rs, loc); - const string& cid (cast (r["cc.id"])); - const string& tsys (cast (r["cc.target.system"])); + const string& cid (cast (rs["cc.id"])); + const string& tsys (cast (rs["cc.target.system"])); // Load the bin.ar module unless we were asked to only build shared // libraries. // - if (auto l = r["config.bin.lib"]) + if (auto l = rs["config.bin.lib"]) { if (cast (l) != "shared") { - if (!cast_false (b["bin.ar.loaded"])) - load_module ("bin.ar", r, b, loc); + if (!cast_false (rs["bin.ar.loaded"])) + load_module ("bin.ar", rs, rs, loc); } } @@ -293,8 +292,8 @@ namespace build2 // if (cid == "msvc") { - if (!cast_false (b["bin.ld.loaded"])) - load_module ("bin.ld", r, b, loc); + if (!cast_false (rs["bin.ld.loaded"])) + load_module ("bin.ld", rs, rs, loc); } // If our target is MinGW, then we will need the resource compiler @@ -302,8 +301,8 @@ namespace build2 // if (tsys == "mingw32") { - if (!cast_false (b["bin.rc.loaded"])) - load_module ("bin.rc", r, b, loc); + if (!cast_false (rs["bin.rc.loaded"])) + load_module ("bin.rc", rs, rs, loc); } return true; @@ -315,45 +314,51 @@ namespace build2 // static inline bool init_alias (tracer& trace, + const char* m, const char* c, const char* c_loaded, const char* cxx, const char* cxx_loaded, - scope& r, - scope& b, + scope& rs, + scope& bs, const location& loc, const variable_map& hints) { - l5 ([&]{trace << "for " << b.out_path ();}); + l5 ([&]{trace << "for " << bs.out_path ();}); + + // 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 (b[c_loaded])); - bool lp (!cast_false (b[cxx_loaded])); + bool lc (!cast_false (rs[c_loaded])); + bool lp (!cast_false (rs[cxx_loaded])); // If none of them are already loaded, load c first only if config.c // is specified. // - if (lc && lp && r["config.c"]) + if (lc && lp && rs["config.c"]) { - load_module (c, r, b, loc, false, hints); - load_module (cxx, r, b, loc, false, hints); + load_module (c, rs, rs, loc, false, hints); + load_module (cxx, rs, rs, loc, false, hints); } else { - if (lp) load_module (cxx, r, b, loc, false, hints); - if (lc) load_module (c, r, b, loc, false, hints); + if (lp) load_module (cxx, rs, rs, loc, false, hints); + if (lc) load_module (c, rs, rs, loc, false, hints); } return true; } bool - config_init (scope& r, - scope& b, + config_init (scope& rs, + scope& bs, const location& loc, unique_ptr&, bool, @@ -361,17 +366,15 @@ namespace build2 const variable_map& hints) { tracer trace ("cc::config_init"); - return init_alias (trace, + return init_alias (trace, "cc.config", "c.config", "c.config.loaded", "cxx.config", "cxx.config.loaded", - r, b, - loc, - hints); + rs, bs, loc, hints); } bool - init (scope& r, - scope& b, + init (scope& rs, + scope& bs, const location& loc, unique_ptr&, bool, @@ -379,12 +382,10 @@ namespace build2 const variable_map& hints) { tracer trace ("cc::init"); - return init_alias (trace, + return init_alias (trace, "cc", "c", "c.loaded", "cxx", "cxx.loaded", - r, b, - loc, - hints); + rs, bs, loc, hints); } } } diff --git a/build2/cc/link b/build2/cc/link index 474c299..09678c2 100644 --- a/build2/cc/link +++ b/build2/cc/link @@ -40,7 +40,8 @@ namespace build2 friend class compile; void - process_libraries (file&, + process_libraries (const dir_paths&, + file&, bool, bool, const function&, @@ -55,21 +56,14 @@ namespace build2 hash_libraries (sha256&, file&, bool) const; file& - resolve_library (name, scope&, lorder, optional&) const; - - // Extract system library search paths from GCC or compatible (Clang, - // Intel) using the -print-search-dirs option. - // - void - gcc_library_search_paths (scope&, dir_paths&) const; - - // Extract system library search paths from VC (msvc.cxx). - // - void - msvc_library_search_paths (scope&, dir_paths&) const; + resolve_library (name, + scope&, + lorder, + const dir_paths&, + optional&) const; dir_paths - extract_library_paths (scope&) const; + extract_library_dirs (scope&) const; bool pkgconfig_extract (scope&, @@ -77,7 +71,7 @@ namespace build2 const string*, const string&, const dir_path&, - optional&, + const dir_paths&, lorder) const; // Alternative search logic for VC (msvc.cxx). @@ -93,20 +87,20 @@ namespace build2 const prerequisite_key&) const; target* - search_library (optional& spc, + search_library (const dir_paths& sysd, + optional& usrd, prerequisite& p, lorder lo) const { if (p.target == nullptr) // First check the cache. - p.target = search_library (spc, p.key (), lo); + p.target = search_library (sysd, usrd, p.key (), lo); return p.target; } - // Note that pk's scope should not be NULL (even if dir is absolute). - // target* - search_library (optional&, + search_library (const dir_paths&, + optional&, const prerequisite_key&, lorder) const; diff --git a/build2/cc/link.cxx b/build2/cc/link.cxx index a4fa1dd..f26bf57 100644 --- a/build2/cc/link.cxx +++ b/build2/cc/link.cxx @@ -39,107 +39,8 @@ namespace build2 { } - // Extract system library search paths from GCC or compatible (Clang, - // Intel) using the -print-search-dirs option. - // - void link:: - gcc_library_search_paths (scope& bs, dir_paths& r) const - { - scope& rs (*bs.root_scope ()); - - cstrings args; - string std; // Storage. - - args.push_back (cast (rs[config_x]).string ().c_str ()); - append_options (args, bs, c_coptions); - append_options (args, bs, x_coptions); - append_std (args, rs, bs, std); - append_options (args, bs, c_loptions); - append_options (args, bs, x_loptions); - args.push_back ("-print-search-dirs"); - args.push_back (nullptr); - - if (verb >= 3) - print_process (args); - - string l; - try - { - process pr (args.data (), 0, -1); // Open pipe to stdout. - - try - { - ifdstream is (pr.in_ofd, fdstream_mode::skip, ifdstream::badbit); - - string s; - while (getline (is, s)) - { - if (s.compare (0, 12, "libraries: =") == 0) - { - l.assign (s, 12, string::npos); - break; - } - } - - is.close (); // Don't block. - - if (!pr.wait ()) - throw failed (); - } - catch (const ifdstream::failure&) - { - pr.wait (); - fail << "error reading " << x_lang << " compiler -print-search-dirs " - << "output"; - } - } - catch (const process_error& e) - { - error << "unable to execute " << args[0] << ": " << e.what (); - - if (e.child ()) - exit (1); - - throw failed (); - } - - if (l.empty ()) - fail << "unable to extract " << x_lang << " compiler system library " - << "search paths"; - - // Now the fun part: figuring out which delimiter is used. Normally it - // is ':' but on Windows it is ';' (or can be; who knows for sure). Also - // note that these paths are absolute (or should be). So here is what we - // are going to do: first look for ';'. If found, then that's the - // delimiter. If not found, then there are two cases: it is either a - // single Windows path or the delimiter is ':'. To distinguish these two - // cases we check if the path starts with a Windows drive. - // - char d (';'); - string::size_type e (l.find (d)); - - if (e == string::npos && - (l.size () < 2 || l[0] == '/' || l[1] != ':')) - { - d = ':'; - e = l.find (d); - } - - // Now chop it up. We already have the position of the first delimiter - // (if any). - // - for (string::size_type b (0);; e = l.find (d, (b = e + 1))) - { - r.emplace_back (l, b, (e != string::npos ? e - b : e)); - r.back ().normalize (); - - if (e == string::npos) - break; - } - } - dir_paths link:: - extract_library_paths (scope& bs) const + extract_library_dirs (scope& bs) const { dir_paths r; @@ -193,16 +94,14 @@ namespace build2 if (auto l = bs[c_loptions]) extract (*l); if (auto l = bs[x_loptions]) extract (*l); - if (cid == "msvc") - msvc_library_search_paths (bs, r); - else - gcc_library_search_paths (bs, r); - return r; } + // Note that pk's scope should not be NULL (even if dir is absolute). + // target* link:: - search_library (optional& spc, + search_library (const dir_paths& sysd, + optional& usrd, const prerequisite_key& p, lorder lo) const { @@ -300,15 +199,17 @@ namespace build2 // Now search. // - if (!spc) - spc = extract_library_paths (*p.scope); - liba* a (nullptr); libs* s (nullptr); path f; // Reuse the buffer. - const dir_path* pd; - for (const dir_path& d: *spc) + const dir_path* pd (nullptr); + + auto search =[&a, &s, + &an, &ae, + &sn, &se, + &name, ext, + &p, &f, &trace, this] (const dir_path& d) -> bool { timestamp mt; @@ -427,14 +328,38 @@ namespace build2 a = msvc_search_static (ld, d, p); } - if (a != nullptr || s != nullptr) + return a != nullptr || s != nullptr; + }; + + // First try user directories (i.e., -L). + // + if (!usrd) + usrd = extract_library_dirs (*p.scope); + + for (const dir_path& d: *usrd) + { + if (search (d)) { pd = &d; break; } } - if (a == nullptr && s == nullptr) + // Next try system directories (i.e., those extracted from the compiler). + // + if (pd == nullptr) + { + for (const dir_path& d: sysd) + { + if (search (d)) + { + pd = &d; + break; + } + } + } + + if (pd == nullptr) return nullptr; // Add the "using static/shared library" macro (used, for example, to @@ -508,14 +433,14 @@ namespace build2 // will copy those macros (or custom ones) from *.export.poptions. // if (pkgconfig == nullptr || - !pkgconfig_extract (*p.scope, *a, p.proj, name, *pd, spc, lo)) + !pkgconfig_extract (*p.scope, *a, p.proj, name, *pd, sysd, lo)) add_macro (*a, "STATIC"); } if (s != nullptr && mark_cc (*s)) { if (pkgconfig == nullptr || - !pkgconfig_extract (*p.scope, *s, p.proj, name, *pd, spc, lo)) + !pkgconfig_extract (*p.scope, *s, p.proj, name, *pd, sysd, lo)) add_macro (*s, "SHARED"); } @@ -643,7 +568,7 @@ namespace build2 scope& bs (t.base_scope ()); lorder lo (link_order (bs, lt)); - optional lib_paths; // Extract lazily. + optional usr_lib_dirs; // Extract lazily. for (prerequisite_member p: group_prerequisite_members (a, t)) { @@ -654,7 +579,8 @@ namespace build2 // Handle imported libraries. // if (p.proj () != nullptr) - pt = search_library (lib_paths, p.prerequisite, lo); + pt = search_library ( + sys_lib_dirs, usr_lib_dirs, p.prerequisite, lo); if (pt == nullptr) { @@ -826,7 +752,7 @@ namespace build2 // inject_fsdir (a, t); - optional lib_paths; // Extract lazily. + optional usr_lib_dirs; // Extract lazily. // Process prerequisites: do rule chaining for C and X source files as // well as search and match. @@ -847,7 +773,8 @@ namespace build2 // Handle imported libraries. // if (p.proj () != nullptr) - pt = search_library (lib_paths, p.prerequisite, lo); + pt = search_library ( + sys_lib_dirs, usr_lib_dirs, p.prerequisite, lo); // The rest is the same basic logic as in search_and_match(). // @@ -1075,6 +1002,7 @@ namespace build2 // void link:: process_libraries ( + const dir_paths& def_sysd, file& l, bool la, bool iface_only, @@ -1135,7 +1063,11 @@ namespace build2 if (proc_lib) proc_lib (f->path ()); - process_libraries (*f, a, iface_only, proc_lib, proc_opt); + // Note that def_sysd (which is wrong) will never be used since + // this library was matched by a rule (see below). + // + process_libraries ( + def_sysd, *f, a, iface_only, proc_lib, proc_opt); } } } @@ -1143,14 +1075,18 @@ namespace build2 // Process libraries (recursively) from *.export.libs (of type names) // handling import, etc. // - scope* bs (nullptr); // Resolve lazily. - optional lo; // Calculate lazily. - optional spc; // Extract lazily. - - auto proc_int = - [&l, la, iface_only, &proc_lib, &proc_opt, &bs, &lo, &spc, this] ( - const lookup& lu) + scope* bs (nullptr); // Resolve lazily. + optional lo; // Calculate lazily. + const dir_paths* sysd (nullptr); // Resolve lazily. + optional usrd; // Extract lazily. + + auto proc_int = [&l, la, t, iface_only, + &proc_lib, &proc_opt, + &sysd, &usrd, &def_sysd, + &bs, &lo, this] (const lookup& lu) { + assert (t != nullptr); + const names* ns (cast_null (lu)); if (ns == nullptr || ns->empty ()) return; @@ -1175,7 +1111,23 @@ namespace build2 if (!lo) lo = link_order (*bs, la ? otype::a : otype::s); - file& t (resolve_library (n, *bs, *lo, spc)); + if (sysd == nullptr) + { + if (*t == "cc") + sysd = &def_sysd; // Imported library, use importer's sysd. + else + { + // Use the search dirs corresponding to this library type. + // + scope& rs (*bs->root_scope ()); + sysd = &cast ( + rs.vars[*t == x + ? x_sys_lib_dirs + : var_pool[*t + ".sys_lib_dirs"]]); + } + } + + file& t (resolve_library (n, *bs, *lo, *sysd, usrd)); if (proc_lib) { @@ -1195,7 +1147,7 @@ namespace build2 // Process it recursively. // process_libraries ( - t, t.is_a (), iface_only, proc_lib, proc_opt); + *sysd, t, t.is_a (), iface_only, proc_lib, proc_opt); } } }; @@ -1299,7 +1251,7 @@ namespace build2 append_options (args, l, var); }; - process_libraries (l, la, false, lib, opt); + process_libraries (sys_lib_dirs, l, la, false, lib, opt); } void link:: @@ -1322,7 +1274,7 @@ namespace build2 hash_options (cs, l, var); }; - process_libraries (l, la, false, lib, opt); + process_libraries (sys_lib_dirs, l, la, false, lib, opt); } // The name can be an absolute target name (e.g., /tmp/libfoo/lib{foo}) or @@ -1338,7 +1290,8 @@ namespace build2 resolve_library (name n, scope& s, lorder lo, - optional& spc) const + const dir_paths& sysd, + optional& usrd) const { if (n.type != "lib" && n.type != "liba" && n.type != "libs") fail << "target name " << n << " is not a library"; @@ -1368,7 +1321,7 @@ namespace build2 dir_path out; prerequisite_key pk {n.proj, {tt, &n.dir, &out, &n.value, ext}, &s}; - xt = search_library (spc, pk, lo); + xt = search_library (sysd, usrd, pk, lo); if (xt == nullptr) { @@ -1605,7 +1558,6 @@ namespace build2 // Storage. // - string std; string soname1, soname2; strings sargs; @@ -1632,7 +1584,7 @@ namespace build2 { append_options (args, t, c_coptions); append_options (args, t, x_coptions); - append_std (args, rs, t, std); + append_std (args); } append_options (args, t, c_loptions); @@ -1809,7 +1761,7 @@ namespace build2 // Ok, so we are updating. Finish building the command line. // - string out, out1, out2; // Storage. + string out, out1, out2, out3; // Storage. // Translate paths to relative (to working directory) ones. This results // in easier to read diagnostics. @@ -1901,10 +1853,10 @@ namespace build2 // if (!manifest.empty ()) { - std = "/MANIFESTINPUT:"; // Repurpose storage for std. - std += relative (manifest).string (); + out3 = "/MANIFESTINPUT:"; + out3 += relative (manifest).string (); args.push_back ("/MANIFEST:EMBED"); - args.push_back (std.c_str ()); + args.push_back (out3.c_str ()); } if (lt == otype::s) diff --git a/build2/cc/module b/build2/cc/module index bed7673..09f5e5a 100644 --- a/build2/cc/module +++ b/build2/cc/module @@ -21,6 +21,8 @@ namespace build2 { namespace cc { + struct compiler_info; + class config_module: public module_base, public virtual config_data { public: @@ -28,11 +30,22 @@ namespace build2 config_module (config_data&& d) : config_data (move (d)) {} void - init (scope&, - scope&, - const location&, - bool first, - const variable_map&); + init (scope&, const location&, const variable_map&); + + // Translate the x.std value to the standard-selecting option if there + // is any. + // + virtual string + translate_std (const compiler_info&, scope&, const string&) const = 0; + + string tstd; + + private: + dir_paths + gcc_library_search_paths (process_path&, scope&) const; // gcc.cxx + + dir_paths + msvc_library_search_paths (process_path&, scope&) const; // msvc.cxx }; class module: public module_base, protected virtual common, @@ -47,11 +60,7 @@ namespace build2 install (move (d), *this) {} void - init (scope&, - scope&, - const location&, - bool first, - const variable_map&); + init (scope&, const location&, const variable_map&); }; } } diff --git a/build2/cc/module.cxx b/build2/cc/module.cxx index 76b2b56..7ed8a9d 100644 --- a/build2/cc/module.cxx +++ b/build2/cc/module.cxx @@ -27,161 +27,183 @@ namespace build2 namespace cc { void config_module:: - init (scope& r, - scope& b, - const location& loc, - bool first, - const variable_map&) + init (scope& rs, const location& loc, const variable_map&) { tracer trace (x, "config_init"); - bool cc_loaded (cast_false (b["cc.core.config.loaded"])); + bool cc_loaded (cast_false (rs["cc.core.config.loaded"])); // Configure. // compiler_info ci; // For program patterns. - if (first) + // Adjust module priority (compiler). Also order cc module before us + // (we don't want to use priorities for that in case someone manages + // to slot in-between). + // + if (!cc_loaded) + config::save_module (rs, "cc", 250); + + config::save_module (rs, x, 250); + + const variable& config_c_coptions (var_pool["config.cc.coptions"]); + + // config.x + // + + // Normally we will have a persistent configuration and computing the + // default value every time will be a waste. So try without a default + // first. + // + auto p (config::omitted (rs, config_x)); + + if (p.first == nullptr) { - // Adjust module priority (compiler). Also order cc module before us - // (we don't want to use priorities for that in case someone manages - // to slot in-between). + // If someone already loaded cc.core.config then use its toolchain + // id and (optional) pattern to guess an appropriate default (e.g., + // for {gcc, *-4.9} we will get g++-4.9). // - if (!cc_loaded) - config::save_module (r, "cc", 250); + path d (cc_loaded + ? guess_default (x_lang, + cast (rs["cc.id"]), + cast_null (rs["cc.pattern"])) + : path (x_default)); + + // If this value was hinted, save it as commented out so that if the + // user changes the source of the pattern, this one will get updated + // as well. + // + auto p1 (config::required (rs, + config_x, + d, + false, + cc_loaded ? config::save_commented : 0)); + p.first = &p1.first.get (); + p.second = p1.second; + } - config::save_module (r, x, 250); + // Figure out which compiler we are dealing with, its target, etc. + // + const path& xc (cast (*p.first)); + ci = guess (x_lang, + xc, + cast_null (rs[config_c_coptions]), + cast_null (rs[config_x_coptions])); - const variable& config_c_coptions (var_pool["config.cc.coptions"]); + // Translate x_std value (if any) to the compiler option (if any). + // + if (auto l = rs[x_std]) + tstd = translate_std (ci, rs, cast (*l)); - // config.x - // + // Extract system library search paths from the compiler. + // + dir_paths lib_dirs (ci.id.type == "msvc" + ? msvc_library_search_paths (ci.path, rs) + : gcc_library_search_paths (ci.path, rs)); - // Normally we will have a persistent configuration and computing the - // default value every time will be a waste. So try without a default - // first. - // - auto p (config::omitted (r, config_x)); + // If this is a new value (e.g., we are configuring), then print the + // report at verbosity level 2 and up (-v). + // + if (verb >= (p.second ? 2 : 3)) + { + diag_record dr (text); - if (p.first == nullptr) { - // If someone already loaded cc.core.config then use its toolchain - // id and (optional) pattern to guess an appropriate default (e.g., - // for {gcc, *-4.9} we will get g++-4.9). - // - path d (cc_loaded - ? guess_default (x_lang, - cast (r["cc.id"]), - cast_null (r["cc.pattern"])) - : path (x_default)); - - // If this value was hinted, save it as commented out so that if the - // user changes the source of the pattern, this one will get updated - // as well. - // - auto p1 (config::required (r, - config_x, - d, - false, - cc_loaded ? config::save_commented : 0)); - p.first = &p1.first.get (); - p.second = p1.second; + dr << x << ' ' << project (rs) << '@' << rs.out_path () << '\n' + << " " << left << setw (11) << x << ci.path << '\n' + << " id " << ci.id << '\n' + << " version " << ci.version.string << '\n' + << " major " << ci.version.major << '\n' + << " minor " << ci.version.minor << '\n' + << " patch " << ci.version.patch << '\n'; } - // Figure out which compiler we are dealing with, its target, etc. - // - const path& xc (cast (*p.first)); - ci = guess (x_lang, - xc, - cast_null (r[config_c_coptions]), - cast_null (r[config_x_coptions])); - - // If this is a new value (e.g., we are configuring), then print the - // report at verbosity level 2 and up (-v). - // - if (verb >= (p.second ? 2 : 3)) + if (!ci.version.build.empty ()) { - diag_record dr (text); - - { - dr << x << ' ' << project (r) << '@' << r.out_path () << '\n' - << " " << left << setw (11) << x << ci.path << '\n' - << " id " << ci.id << '\n' - << " version " << ci.version.string << '\n' - << " major " << ci.version.major << '\n' - << " minor " << ci.version.minor << '\n' - << " patch " << ci.version.patch << '\n'; - } - - if (!ci.version.build.empty ()) - dr << " build " << ci.version.build << '\n'; - - { - dr << " signature " << ci.signature << '\n' - << " target " << ci.target << '\n'; - } - - if (!ci.cc_pattern.empty ()) // bin_pattern printed by bin - dr << " pattern " << ci.cc_pattern << '\n'; - - { - dr << " checksum " << ci.checksum; - } + dr << " build " << ci.version.build << '\n'; } - r.assign (x_path) = move (ci.path); - r.assign (x_id) = ci.id.string (); - r.assign (x_id_type) = move (ci.id.type); - r.assign (x_id_variant) = move (ci.id.variant); - - r.assign (x_version) = move (ci.version.string); - r.assign (x_version_major) = ci.version.major; - r.assign (x_version_minor) = ci.version.minor; - r.assign (x_version_patch) = ci.version.patch; - r.assign (x_version_build) = move (ci.version.build); + { + dr << " signature " << ci.signature << '\n' + << " target " << ci.target << '\n'; + } - r.assign (x_signature) = move (ci.signature); - r.assign (x_checksum) = move (ci.checksum); + if (!tstd.empty ()) + { + dr << " std " << tstd << '\n'; + } - // Split/canonicalize the target. First see if the user asked us to - // use config.sub. - // - if (ops.config_sub_specified ()) + if (!ci.cc_pattern.empty ()) // bin_pattern printed by bin { - ci.target = run (ops.config_sub (), - ci.target.c_str (), - [] (string& l) {return move (l);}); - l5 ([&]{trace << "config.sub target: '" << ci.target << "'";}); + dr << " pattern " << ci.cc_pattern << '\n'; } - try + if (verb >= 3 && !lib_dirs.empty ()) { - string canon; - triplet t (ci.target, canon); - - l5 ([&]{trace << "canonical target: '" << canon << "'; " - << "class: " << t.class_;}); - - // Enter as x.target.{cpu,vendor,system,version,class}. - // - r.assign (x_target) = move (canon); - r.assign (x_target_cpu) = move (t.cpu); - r.assign (x_target_vendor) = move (t.vendor); - r.assign (x_target_system) = move (t.system); - r.assign (x_target_version) = move (t.version); - r.assign (x_target_class) = move (t.class_); + dr << " lib dirs\n"; + for (const dir_path& d: lib_dirs) + dr << " " << d << '\n'; } - catch (const invalid_argument& e) + { - // This is where we suggest that the user specifies --config-sub to - // help us out. - // - fail << "unable to parse " << x_lang << "compiler target '" - << ci.target << "': " << e.what () << - info << "consider using the --config-sub option"; + dr << " checksum " << ci.checksum; } } + rs.assign (x_path) = move (ci.path); + rs.assign (x_sys_lib_dirs) = move (lib_dirs); + + rs.assign (x_id) = ci.id.string (); + rs.assign (x_id_type) = move (ci.id.type); + rs.assign (x_id_variant) = move (ci.id.variant); + + rs.assign (x_version) = move (ci.version.string); + rs.assign (x_version_major) = ci.version.major; + rs.assign (x_version_minor) = ci.version.minor; + rs.assign (x_version_patch) = ci.version.patch; + rs.assign (x_version_build) = move (ci.version.build); + + rs.assign (x_signature) = move (ci.signature); + rs.assign (x_checksum) = move (ci.checksum); + + // Split/canonicalize the target. First see if the user asked us to + // use config.sub. + // + if (ops.config_sub_specified ()) + { + ci.target = run (ops.config_sub (), + ci.target.c_str (), + [] (string& l) {return move (l);}); + l5 ([&]{trace << "config.sub target: '" << ci.target << "'";}); + } + + try + { + string canon; + triplet t (ci.target, canon); + + l5 ([&]{trace << "canonical target: '" << canon << "'; " + << "class: " << t.class_;}); + + // Enter as x.target.{cpu,vendor,system,version,class}. + // + rs.assign (x_target) = move (canon); + rs.assign (x_target_cpu) = move (t.cpu); + rs.assign (x_target_vendor) = move (t.vendor); + rs.assign (x_target_system) = move (t.system); + rs.assign (x_target_version) = move (t.version); + rs.assign (x_target_class) = move (t.class_); + } + catch (const invalid_argument& e) + { + // This is where we suggest that the user specifies --config-sub to + // help us out. + // + fail << "unable to parse " << x_lang << "compiler target '" + << ci.target << "': " << e.what () << + info << "consider using the --config-sub option"; + } + // config.x.{p,c,l}options // config.x.libs // @@ -198,52 +220,49 @@ namespace build2 // using x // x.coptions += # Note: '+='. // - b.assign (x_poptions) += cast_null ( - config::optional (r, config_x_poptions)); + rs.assign (x_poptions) += cast_null ( + config::optional (rs, config_x_poptions)); - b.assign (x_coptions) += cast_null ( - config::optional (r, config_x_coptions)); + rs.assign (x_coptions) += cast_null ( + config::optional (rs, config_x_coptions)); - b.assign (x_loptions) += cast_null ( - config::optional (r, config_x_loptions)); + rs.assign (x_loptions) += cast_null ( + config::optional (rs, config_x_loptions)); - b.assign (x_libs) += cast_null ( - config::optional (r, config_x_libs)); + rs.assign (x_libs) += cast_null ( + config::optional (rs, config_x_libs)); // Load cc.core.config. // if (!cc_loaded) { - // Prepare configuration hints. They are only used on the first load - // of cc.core.config so we only populate them on our first load. + // Prepare configuration hints. // variable_map h; - if (first) - { - h.assign ("config.cc.id") = cast (r[x_id]); - h.assign ("config.cc.target") = cast (r[x_target]); - if (!ci.cc_pattern.empty ()) - h.assign ("config.cc.pattern") = move (ci.cc_pattern); + h.assign ("config.cc.id") = cast (rs[x_id]); + h.assign ("config.cc.target") = cast (rs[x_target]); - if (!ci.bin_pattern.empty ()) - h.assign ("config.bin.pattern") = move (ci.bin_pattern); - } + if (!ci.cc_pattern.empty ()) + h.assign ("config.cc.pattern") = move (ci.cc_pattern); + + if (!ci.bin_pattern.empty ()) + h.assign ("config.bin.pattern") = move (ci.bin_pattern); - load_module ("cc.core.config", r, b, loc, false, h); + load_module ("cc.core.config", rs, rs, loc, false, h); } - else if (first) + else { // If cc.core.config is already loaded, verify its configuration // matched ours since it could have been loaded by another c-family // module. // - auto check = [&r, &loc, this](const char* cvar, + auto check = [&rs, &loc, this](const char* cvar, const variable& xvar, const char* w) { - const string& cv (cast (r[cvar])); - const string& xv (cast (r[xvar])); + const string& cv (cast (rs[cvar])); + const string& xv (cast (rs[xvar])); if (cv != xv) fail (loc) << "cc and " << x << " module " << w << " mismatch" << @@ -261,26 +280,22 @@ namespace build2 } void module:: - init (scope& r, - scope& b, - const location& loc, - bool, - const variable_map&) + init (scope& rs, const location& loc, const variable_map&) { tracer trace (x, "init"); // Load cc.core. Besides other things, this will load bin (core) plus // extra bin.* modules we may need. // - if (!cast_false (b["cc.core.loaded"])) - load_module ("cc.core", r, b, loc); + if (!cast_false (rs["cc.core.loaded"])) + load_module ("cc.core", rs, rs, loc); // Register target types and configure their "installability". // { using namespace install; - auto& t (b.target_types); + auto& t (rs.target_types); t.insert (x_src); @@ -289,7 +304,7 @@ namespace build2 for (const target_type* const* ht (x_hdr); *ht != nullptr; ++ht) { t.insert (**ht); - install_path (**ht, b, dir_path ("include")); + install_path (**ht, rs, dir_path ("include")); } } @@ -298,7 +313,7 @@ namespace build2 { using namespace bin; - auto& r (b.rules); + auto& r (rs.rules); // We register for configure so that we detect unresolved imports // during configuration rather that later, e.g., during update. @@ -323,7 +338,7 @@ namespace build2 // Only register static object/library rules if the bin.ar module is // loaded (by us or by the user). // - if (cast_false (b["bin.ar.loaded"])) + if (cast_false (rs["bin.ar.loaded"])) { r.insert (perform_update_id, x_compile, cr); r.insert (perform_clean_id, x_compile, cr); diff --git a/build2/cc/msvc.cxx b/build2/cc/msvc.cxx index 216bbf2..e4323d4 100644 --- a/build2/cc/msvc.cxx +++ b/build2/cc/msvc.cxx @@ -14,7 +14,9 @@ #include #include + #include +#include using namespace std; using namespace butl; @@ -85,8 +87,8 @@ namespace build2 // Extract system library search paths from MSVC. // - void link:: - msvc_library_search_paths (scope&, dir_paths&) const + dir_paths config_module:: + msvc_library_search_paths (process_path&, scope&) const { // The linker doesn't seem to have any built-in paths and all of them // come from the LIB environment variable. @@ -96,8 +98,9 @@ namespace build2 // // Should we actually bother? LIB is normally used for system // libraries and its highly unlikely we will see an explicit import - // for a library from one of those directories. + // for a library from one of those directories. Let's wait and see. // + return dir_paths (); } // Inspect the file and determine if it is static or import library. diff --git a/build2/cc/pkgconfig.cxx b/build2/cc/pkgconfig.cxx index 2122ea5..eb7f0a6 100644 --- a/build2/cc/pkgconfig.cxx +++ b/build2/cc/pkgconfig.cxx @@ -41,12 +41,11 @@ namespace build2 const string* proj, const string& stem, const dir_path& libd, - optional& sys_sp, + const dir_paths& sysd, lorder lo) const { tracer trace (x, "link::pkgconfig_extract"); - assert (sys_sp); assert (pkgconfig != nullptr); // Check if we have the pkgconfig/ subdirectory in this library's @@ -291,15 +290,15 @@ namespace build2 // The reason we do it is the link order. For general libraries it // shouldn't matter if we imported them via an export stub, direct // import installed, or via a .pc file (which we could have generated - // from the export stub). The exception is "system libraries" (which + // from the export stub). The exception is "runtime libraries" (which // are really the extension of libc) such as -lm, -ldl, -lpthread, // etc. Those we will detect and leave as -l*. // - // If we managed to resolve all the -l's (sans system), then we can + // If we managed to resolve all the -l's (sans runtime), then we can // omit -L's for nice and tidy command line. // bool all (true); - optional sp; // Populate lazily. + optional usrd; // Populate lazily. for (name& n: libs) { @@ -333,15 +332,13 @@ namespace build2 continue; } - // Prepare the search paths. + // Prepare user search paths by entering the -L paths from the .pc + // file. // - if (have_L && !sp) + if (have_L && !usrd) { - sp = dir_paths (); + usrd = dir_paths (); - // First enter the -L paths from the .pc file so that they take - // precedence. - // for (auto i (lops.begin ()); i != lops.end (); ++i) { const string& o (*i); @@ -361,13 +358,9 @@ namespace build2 fail << "relative -L directory in '" << lstr << "'" << info << "while parsing pkg-config --libs output of " << f; - sp->push_back (move (d)); + usrd->push_back (move (d)); } } - - // Then append system paths. - // - sp->insert (sp->end (), sys_sp->begin (), sys_sp->end ()); } // @@ OUT: for now we assume out is undetermined, just like in @@ -381,7 +374,7 @@ namespace build2 nullptr, {&lib::static_type, &out, &out, &name, ext}, &s}; if (lib* lt = static_cast ( - search_library (have_L ? sp : sys_sp, pk, lo))) + search_library (sysd, usrd, pk, lo))) { file& f (static_cast (link_member (*lt, lo))); l = f.path ().string (); diff --git a/build2/cxx/init.cxx b/build2/cxx/init.cxx index e504284..b5de7d9 100644 --- a/build2/cxx/init.cxx +++ b/build2/cxx/init.cxx @@ -8,6 +8,7 @@ #include #include +#include #include #include @@ -19,24 +20,29 @@ namespace build2 { namespace cxx { - using cc::config_module; + using cc::compiler_info; - class module: public cc::module + class config_module: public cc::config_module { public: explicit - module (data&& d): common (move (d)), cc::module (move (d)) {} + config_module (config_data&& d) + : config_data (move (d)), cc::config_module (move (d)) {} - bool - translate_std (string&, scope&, const value&) const override; + string + translate_std (const compiler_info&, + scope&, + const string&) const override; }; - bool module:: - translate_std (string& s, scope& r, const value& val) const + using cc::module; + + string config_module:: + translate_std (const compiler_info& ci, scope& rs, const string& v) const { - const string& v (cast (val)); + string r; - if (cid == "msvc") + if (ci.id.type == "msvc") { // C++ standard-wise, with VC you get what you get. The question is // whether we should verify that the requested standard is provided by @@ -49,7 +55,7 @@ namespace build2 // if (v != "98" && v != "03") { - uint64_t cver (cast (r[x_version_major])); + uint64_t cver (ci.version.major); // @@ Is mapping for 14 and 17 correct? Maybe Update 2 for 14? // @@ -57,127 +63,128 @@ namespace build2 (v == "14" && cver < 19) || // C++14 since VS2015/14.0. (v == "17" && cver < 20)) // C++17 since VS20??/15.0. { - fail << "C++" << v << " is not supported by " - << cast (r[x_signature]) << - info << "required by " << project (r) << '@' << r.out_path (); + fail << "C++" << v << " is not supported by " << ci.signature << + info << "required by " << project (rs) << '@' << rs.out_path (); } } - - return false; } else { // Translate 11 to 0x, 14 to 1y, and 17 to 1z for compatibility with // older versions of the compilers. // - s = "-std="; + r = "-std="; if (v == "98") - s += "c++98"; + r += "c++98"; else if (v == "03") - s += "c++03"; + r += "c++03"; else if (v == "11") - s += "c++0x"; + r += "c++0x"; else if (v == "14") - s += "c++1y"; + r += "c++1y"; else if (v == "17") - s += "c++1z"; + r += "c++1z"; else - s += v; // In case the user specifies something like 'gnu++17'. - - return true; + r += v; // In case the user specifies something like 'gnu++17'. } + + return r; } bool - config_init (scope& r, - scope& b, + config_init (scope& rs, + scope& bs, const location& loc, - unique_ptr& m, - bool first, + unique_ptr& mod, + bool, bool, const variable_map& hints) { tracer trace ("cxx::config_init"); - l5 ([&]{trace << "for " << b.out_path ();}); + l5 ([&]{trace << "for " << bs.out_path ();}); - if (first) - { - // Load cc.core.vars so that we can cache all the cc.* variables. - // - if (!cast_false (b["cc.core.vars.loaded"])) - load_module ("cc.core.vars", r, b, loc); + // We only support root loading (which means there can only be one). + // + if (&rs != &bs) + fail (loc) << "cxx.config module must be loaded in project root"; - // Enter all the variables and initialize the module data. - // - auto& v (var_pool); + // Load cc.core.vars so that we can cache all the cc.* variables. + // + if (!cast_false (rs["cc.core.vars.loaded"])) + load_module ("cc.core.vars", rs, rs, loc); - cc::config_data d { - cc::lang::cxx, + // Enter all the variables and initialize the module data. + // + auto& v (var_pool); - "cxx", - "c++", - "g++", + cc::config_data d { + cc::lang::cxx, - // Note: some overridable, some not. - // - v.insert ("config.cxx", true), - v.insert ("config.cxx.poptions", true), - v.insert ("config.cxx.coptions", true), - v.insert ("config.cxx.loptions", true), - v.insert ("config.cxx.libs", true), - - v.insert ("cxx.path"), - v.insert ("cxx.poptions"), - v.insert ("cxx.coptions"), - v.insert ("cxx.loptions"), - v.insert ("cxx.libs"), - - v["cc.poptions"], - v["cc.coptions"], - v["cc.loptions"], - v["cc.libs"], - - v.insert ("cxx.export.poptions"), - v.insert ("cxx.export.coptions"), - v.insert ("cxx.export.loptions"), - v.insert ("cxx.export.libs"), - - v["cc.export.poptions"], - v["cc.export.coptions"], - v["cc.export.loptions"], - v["cc.export.libs"], - - v["cc.type"], - - v.insert ("cxx.std", true), - - v.insert ("cxx.id"), - v.insert ("cxx.id.type"), - v.insert ("cxx.id.variant"), - - v.insert ("cxx.version"), - v.insert ("cxx.version.major"), - v.insert ("cxx.version.minor"), - v.insert ("cxx.version.patch"), - v.insert ("cxx.version.build"), - - v.insert ("cxx.signature"), - v.insert ("cxx.checksum"), - - v.insert ("cxx.target"), - v.insert ("cxx.target.cpu"), - v.insert ("cxx.target.vendor"), - v.insert ("cxx.target.system"), - v.insert ("cxx.target.version"), - v.insert ("cxx.target.class") - }; - - assert (m == nullptr); - m.reset (new config_module (move (d))); - } + "cxx", + "c++", + "g++", - static_cast (*m).init (r, b, loc, first, hints); + // Note: some overridable, some not. + // + v.insert ("config.cxx", true), + v.insert ("config.cxx.poptions", true), + v.insert ("config.cxx.coptions", true), + v.insert ("config.cxx.loptions", true), + v.insert ("config.cxx.libs", true), + + v.insert ("cxx.path"), + v.insert ("cxx.sys_lib_dirs"), + + v.insert ("cxx.poptions"), + v.insert ("cxx.coptions"), + v.insert ("cxx.loptions"), + v.insert ("cxx.libs"), + + v["cc.poptions"], + v["cc.coptions"], + v["cc.loptions"], + v["cc.libs"], + + v.insert ("cxx.export.poptions"), + v.insert ("cxx.export.coptions"), + v.insert ("cxx.export.loptions"), + v.insert ("cxx.export.libs"), + + v["cc.export.poptions"], + v["cc.export.coptions"], + v["cc.export.loptions"], + v["cc.export.libs"], + + v["cc.type"], + + v.insert ("cxx.std", true), + + v.insert ("cxx.id"), + v.insert ("cxx.id.type"), + v.insert ("cxx.id.variant"), + + v.insert ("cxx.version"), + v.insert ("cxx.version.major"), + v.insert ("cxx.version.minor"), + v.insert ("cxx.version.patch"), + v.insert ("cxx.version.build"), + + v.insert ("cxx.signature"), + v.insert ("cxx.checksum"), + + v.insert ("cxx.target"), + v.insert ("cxx.target.cpu"), + v.insert ("cxx.target.vendor"), + v.insert ("cxx.target.system"), + v.insert ("cxx.target.version"), + v.insert ("cxx.target.class") + }; + + assert (mod == nullptr); + config_module* m; + mod.reset (m = new config_module (move (d))); + m->init (rs, loc, hints); return true; } @@ -202,51 +209,56 @@ namespace build2 }; bool - init (scope& r, - scope& b, + init (scope& rs, + scope& bs, const location& loc, - unique_ptr& m, - bool first, + unique_ptr& mod, + bool, bool, const variable_map& hints) { tracer trace ("cxx::init"); - l5 ([&]{trace << "for " << b.out_path ();}); + l5 ([&]{trace << "for " << bs.out_path ();}); + + // We only support root loading (which means there can only be one). + // + if (&rs != &bs) + fail (loc) << "cxx module must be loaded in project root"; // Load cxx.config. // - if (!cast_false (b["cxx.config.loaded"])) - load_module ("cxx.config", r, b, loc, false, hints); + if (!cast_false (rs["cxx.config.loaded"])) + load_module ("cxx.config", rs, rs, loc, false, hints); - if (first) - { - config_module& cm (*r.modules.lookup ("cxx.config")); + config_module& cm (*rs.modules.lookup ("cxx.config")); - cc::data d { - cm, + cc::data d { + cm, - "cxx.compile", - "cxx.link", - "cxx.install", - "cxx.uninstall", + "cxx.compile", + "cxx.link", + "cxx.install", + "cxx.uninstall", - cast (r[cm.x_id]), - cast (r[cm.x_target]), - cast (r[cm.x_target_system]), - cast (r[cm.x_target_class]), + cast (rs[cm.x_id]), + cast (rs[cm.x_target]), + cast (rs[cm.x_target_system]), + cast (rs[cm.x_target_class]), - cast_null (r["pkgconfig.path"]), + cm.tstd, - cxx::static_type, - hdr, - inc - }; + cast_null (rs["pkgconfig.path"]), + cast (rs[cm.x_sys_lib_dirs]), - assert (m == nullptr); - m.reset (new module (move (d))); - } + cxx::static_type, + hdr, + inc + }; - static_cast (*m).init (r, b, loc, first, hints); + assert (mod == nullptr); + module* m; + mod.reset (m = new module (move (d))); + m->init (rs, loc, hints); return true; } } diff --git a/build2/pkgconfig/init.cxx b/build2/pkgconfig/init.cxx index b7100e3..f82e710 100644 --- a/build2/pkgconfig/init.cxx +++ b/build2/pkgconfig/init.cxx @@ -30,8 +30,10 @@ namespace build2 tracer trace ("pkgconfig::config_init"); l5 ([&]{trace << "for " << bs.out_path ();}); + // We only support root loading (which means there can only be one). + // if (&rs != &bs) - fail (l) << "pkgconfig.config loaded for non-root scope"; + fail (l) << "pkgconfig.config module must be loaded in project root"; // Enter variables. // -- cgit v1.1