From 8671b4dc516994b7f070a341c004aab28e525431 Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Thu, 5 Nov 2020 15:52:13 +0200 Subject: Initial Emscripten support - Target: wasm32-emscripten (wasm32-unknown-emscripten). - Compiler id: clang-emscripten (type clang, variant emscripten, class gcc). - Ability to build executables (.js plus .wasm) and static libraries (.a). Set executable bit on the .js file (so it can be executed with a suitable binfmt interpreter). - Default config.bin.lib for wasm32-emscripten is static instead of both. - Full C++ exception support is enable unless disabled explicitly by the user with -s DISABLE_EXCEPTION_CATCHING=1|2. - The bin module registers the wasm{} target type for wasm32-emscripten. --- libbuild2/bin/init.cxx | 291 ++++++++++++++++++++++++++----------------------- 1 file changed, 153 insertions(+), 138 deletions(-) (limited to 'libbuild2/bin/init.cxx') diff --git a/libbuild2/bin/init.cxx b/libbuild2/bin/init.cxx index 2f1e6db..83c4b38 100644 --- a/libbuild2/bin/init.cxx +++ b/libbuild2/bin/init.cxx @@ -145,7 +145,7 @@ namespace build2 config_init (scope& rs, scope& bs, const location& loc, - bool first, + bool, bool, module_init_extra& extra) { @@ -177,6 +177,126 @@ namespace build2 // config::save_module (rs, "bin", 350); + bool new_cfg (false); // Any new configuration values? + + // config.bin.target + // + const target_triplet* tgt; + { + const variable& var (ctx.var_pool["config.bin.target"]); + + // We first see if the value was specified via the configuration + // mechanism. + // + lookup l (lookup_config (new_cfg, rs, var)); + + // Then see if there is a config hint (e.g., from the cc module). + // + bool hint (false); + if (!l) + { + // Note: new_cfg is false for a hinted value. + // + if (auto hl = extra.hints[var]) + { + l = hl; + hint = true; + } + } + + if (!l) + fail (loc) << "unable to determine binutils target" << + info << "consider specifying it with " << var << + info << "or first load a module that can provide it as a hint, " + << "such as c or cxx"; + + // Split/canonicalize the target. + // + string s (cast (l)); + + // Did the user ask us to use config.sub? If this is a hinted value, + // then we assume it has already been passed through config.sub. + // + if (!hint && config_sub) + { + s = run (3, + *config_sub, + s.c_str (), + [] (string& l, bool) {return move (l);}); + l5 ([&]{trace << "config.sub target: '" << s << "'";}); + } + + try + { + target_triplet t (s); + + l5 ([&]{trace << "canonical target: '" << t.string () << "'; " + << "class: " << t.class_;}); + + assert (!hint || s == t.representation ()); + + // Also enter as bin.target.{cpu,vendor,system,version,class} + // for convenience of access. + // + rs.assign ("bin.target.cpu") = t.cpu; + rs.assign ("bin.target.vendor") = t.vendor; + rs.assign ("bin.target.system") = t.system; + rs.assign ("bin.target.version") = t.version; + rs.assign ("bin.target.class") = t.class_; + + tgt = &rs.assign ("bin.target", move (t)); + } + catch (const invalid_argument& e) + { + // This is where we suggest that the user specifies --config-sub + // to help us out. + // + fail << "unable to parse binutils target '" << s << "': " << e << + info << "consider using the --config-sub option"; + } + } + + // config.bin.pattern + // + const string* pat (nullptr); + { + const variable& var (ctx.var_pool["config.bin.pattern"]); + + // We first see if the value was specified via the configuration + // mechanism. + // + lookup l (lookup_config (new_cfg, rs, var)); + + // Then see if there is a config hint (e.g., from the C++ module). + // + if (!l) + { + // Note: new_cfg is false for a hinted value. + // + if (auto hl = extra.hints[var]) + l = hl; + } + + // For ease of use enter it as bin.pattern (since it can come from + // different places). + // + if (l) + { + const string& s (cast (l)); + + if (s.empty () || + (!path::traits_type::is_separator (s.back ()) && + s.find ('*') == string::npos)) + { + fail << "missing '*' or trailing '" + << char (path::traits_type::directory_separator) + << "' in binutils pattern '" << s << "'"; + } + + pat = &rs.assign ("bin.pattern", s); + } + } + // The idea here is as follows: if we already have one of // the bin.* variables set, then we assume this is static // project configuration and don't bother setting the @@ -184,15 +304,20 @@ namespace build2 // //@@ Need to validate the values. Would be more efficient // to do it once on assignment than every time on query. - // Custom var type? // // config.bin.lib // + // By default it's both unless the target doesn't support one of the + // variants. + // { value& v (rs.assign ("bin.lib")); if (!v) - v = *lookup_config (rs, "config.bin.lib", "both"); + v = *lookup_config (rs, + "config.bin.lib", + tgt->system == "emscripten" ? "static" : + "both"); } // config.bin.exe.lib @@ -272,140 +397,19 @@ namespace build2 set ("bin.exe.suffix", "config.bin.exe.suffix", s); } - if (first) + // If this is a configuration with new values, then print the report + // at verbosity level 2 and up (-v). + // + if (verb >= (new_cfg ? 2 : 3)) { - bool new_cfg (false); // Any new configuration values? + diag_record dr (text); - // config.bin.target - // - { - const variable& var (ctx.var_pool["config.bin.target"]); - - // We first see if the value was specified via the configuration - // mechanism. - // - lookup l (lookup_config (new_cfg, rs, var)); - - // Then see if there is a config hint (e.g., from the cc module). - // - bool hint (false); - if (!l) - { - // Note: new_cfg is false for a hinted value. - // - if (auto hl = extra.hints[var]) - { - l = hl; - hint = true; - } - } - - if (!l) - fail (loc) << "unable to determine binutils target" << - info << "consider specifying it with " << var << - info << "or first load a module that can provide it as a hint, " - << "such as c or cxx"; - - // Split/canonicalize the target. - // - string s (cast (l)); - - // Did the user ask us to use config.sub? If this is a hinted value, - // then we assume it has already been passed through config.sub. - // - if (!hint && config_sub) - { - s = run (3, - *config_sub, - s.c_str (), - [] (string& l, bool) {return move (l);}); - l5 ([&]{trace << "config.sub target: '" << s << "'";}); - } - - try - { - target_triplet t (s); - - l5 ([&]{trace << "canonical target: '" << t.string () << "'; " - << "class: " << t.class_;}); - - assert (!hint || s == t.representation ()); - - // Also enter as bin.target.{cpu,vendor,system,version,class} - // for convenience of access. - // - rs.assign ("bin.target.cpu") = t.cpu; - rs.assign ("bin.target.vendor") = t.vendor; - rs.assign ("bin.target.system") = t.system; - rs.assign ("bin.target.version") = t.version; - rs.assign ("bin.target.class") = t.class_; - - rs.assign ("bin.target") = move (t); - } - catch (const invalid_argument& e) - { - // This is where we suggest that the user specifies --config-sub - // to help us out. - // - fail << "unable to parse binutils target '" << s << "': " << e << - info << "consider using the --config-sub option"; - } - } - - // config.bin.pattern - // - { - const variable& var (ctx.var_pool["config.bin.pattern"]); - - // We first see if the value was specified via the configuration - // mechanism. - // - lookup l (lookup_config (new_cfg, rs, var)); - - // Then see if there is a config hint (e.g., from the C++ module). - // - if (!l) - { - // Note: new_cfg is false for a hinted value. - // - if (auto hl = extra.hints[var]) - l = hl; - } - - // For ease of use enter it as bin.pattern (since it can come from - // different places). - // - if (l) - { - const string& s (cast (l)); - - if (s.empty () || - (!path::traits_type::is_separator (s.back ()) && - s.find ('*') == string::npos)) - { - fail << "missing '*' or trailing '" - << char (path::traits_type::directory_separator) - << "' in binutils pattern '" << s << "'"; - } - - rs.assign ("bin.pattern") = s; - } - } - - // If this is a configuration with new values, then print the report - // at verbosity level 2 and up (-v). - // - if (verb >= (new_cfg ? 2 : 3)) - { - diag_record dr (text); + dr << "bin " << project (rs) << '@' << rs << '\n' + << " target " << *tgt; - dr << "bin " << project (rs) << '@' << rs << '\n' - << " target " << cast (rs["bin.target"]); - - if (auto l = rs["bin.pattern"]) - dr << '\n' - << " pattern " << cast (l); - } + if (pat != nullptr) + dr << '\n' + << " pattern " << *pat; } return true; @@ -428,7 +432,7 @@ namespace build2 // Cache some config values we will be needing below. // - const string& tclass (cast (rs["bin.target.class"])); + const target_triplet& tgt (cast (rs["bin.target"])); // Register target types and configure their default "installability". // @@ -497,12 +501,12 @@ namespace build2 // bin/, not lib/. // if (install_loaded) - install_path (bs, - dir_path (tclass == "windows" ? "bin" : "lib")); + install_path ( + bs, dir_path (tgt.class_ == "windows" ? "bin" : "lib")); // Create additional target types for certain targets. // - if (tclass == "windows") + if (tgt.class_ == "windows") { // Import library. // @@ -515,6 +519,17 @@ namespace build2 install_mode (bs, "644"); } } + + if (tgt.cpu == "wasm32" || tgt.cpu == "wasm64") + { + const target_type& wasm (bs.derive_target_type ("wasm").first); + + if (install_loaded) + { + install_path (bs, wasm, dir_path ("bin")); // Goes to install.bin + install_mode (bs, wasm, "644"); // But not executable. + } + } } // Register rules. -- cgit v1.1