// file : build2/cli/init.cxx -*- C++ -*- // license : MIT; see accompanying LICENSE file #include #include #include #include #include #include #include #include #include #include #include namespace build2 { namespace cli { // Remaining issues/semantics change: // // @@ Unconfigured caching. // // @@ Default-found cli used to result in config.cli=cli and now it's just // omitted (and default-not-found -- in config.cli.configured=false). // // - Writing any default will take precedence over config.import.cli. // In fact, this duality is a bigger problem: if we have a config // that uses config.cli there is no way to reconfigure it to use // config.import.cli. // // - We could have saved it commented. // // - We could do this at the module level only since we also have // config.cli.options? // // - Note that in the CLI compiler itself we now rely on default cli // being NULL/undefined. So if faving, should probably be commented // out. BUT: it will still be defined, so will need to be defined // NULL. Note also that long term the CLI compiler will not use the // module relying on an ad hoc recipe instead. // // ! Maybe reserving NULL (instead of making it the same as NULL) for // this "configured to default" state and saving commented is not a // bad idea. Feels right to have some marker in config.build that // things are in effect. And I believe if config.import.cli is // specified, it will just be dropped. bool guess_init (scope& rs, scope& bs, const location& loc, bool, bool optional, module_init_extra& extra) { tracer trace ("cli::guess_init"); l5 ([&]{trace << "for " << rs;}); // We only support root loading (which means there can only be one). // if (rs != bs) fail (loc) << "cli.guess module must be loaded in project root"; // Adjust module config.build save priority (code generator). // config::save_module (rs, "cli", 150); // Enter metadata variables. // auto& vp (rs.var_pool ()); auto& v_ver (vp.insert ("cli.version")); auto& v_sum (vp.insert ("cli.checksum")); // Import the CLI compiler target. // // Note that the special config.cli=false value (recognized by the // import machinery) is treated as an explicit request to leave the // module unconfigured. // bool new_cfg (false); pair ir ( import_direct ( new_cfg, rs, name ("cli", dir_path (), "exe", "cli"), // cli%exe{cli} true /* phase2 */, optional, true /* metadata */, loc, "module load")); const exe* tgt (ir.first); // Extract metadata. // auto* ver (tgt != nullptr ? &cast (tgt->vars[v_ver]) : nullptr); auto* sum (tgt != nullptr ? &cast (tgt->vars[v_sum]) : nullptr); // Print the report. // // 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 << "cli " << project (rs) << '@' << rs << '\n'; if (tgt != nullptr) dr << " cli " << ir << '\n' << " version " << *ver << '\n' << " checksum " << *sum; else dr << " cli " << "not found, leaving unconfigured"; } if (tgt == nullptr) return false; // The cli variable (untyped) is an imported compiler target name. // rs.assign ("cli") = tgt->as_name (); rs.assign (v_sum) = *sum; rs.assign (v_ver) = *ver; { standard_version v (*ver); rs.assign ("cli.version.number") = v.version; rs.assign ("cli.version.major") = v.major (); rs.assign ("cli.version.minor") = v.minor (); rs.assign ("cli.version.patch") = v.patch (); } // Cache some values in the module for easier access in the rule. // extra.set_module (new module (data {*tgt, *sum})); return true; } bool config_init (scope& rs, scope& bs, const location& loc, bool, bool optional, module_init_extra& extra) { tracer trace ("cli::config_init"); l5 ([&]{trace << "for " << rs;}); // We only support root loading (which means there can only be one). // if (rs != bs) fail (loc) << "cli.config module must be loaded in project root"; // Load cli.guess and share its module instance as ours. // if (const shared_ptr* r = load_module ( rs, rs, "cli.guess", loc, optional, extra.hints)) { extra.module = *r; } else { // This can happen if someone already optionally loaded cli.guess // and it has failed to configure. // if (!optional) fail (loc) << "cli could not be configured" << info << "re-run with -V for more information"; return false; } // Configuration. // using config::append_config; // config.cli.options // // Note that we merge it into the corresponding cli.* variable. // append_config (rs, rs, "cli.options", nullptr); return true; } bool init (scope& rs, scope& bs, const location& loc, bool, bool optional, module_init_extra& extra) { tracer trace ("cli::init"); l5 ([&]{trace << "for " << rs;}); // We only support root loading (which means there can only be one). // if (rs != bs) fail (loc) << "cli module must be loaded in project root"; // Make sure the cxx module has been loaded since we need its targets // types (?xx{}). Note that we don't try to load it ourselves because of // the non-trivial variable merging semantics. So it is better to let // the user load cxx explicitly. @@ Not sure the reason still holds // though it might still make sense to expect the user to load cxx. // if (!cast_false (rs["cxx.loaded"])) fail (loc) << "cxx module must be loaded before cli"; // Load cli.config and get its module instance. // if (const shared_ptr* r = load_module ( rs, rs, "cli.config", loc, optional, extra.hints)) { extra.module = *r; } else { // This can happen if someone already optionally loaded cli.config // and it has failed to configure. // if (!optional) fail (loc) << "cli could not be configured" << info << "re-run with -V for more information"; return false; } auto& m (extra.module_as ()); // Register target types. // rs.insert_target_type (); rs.insert_target_type (); // Register our rules. // { auto reg = [&rs, &m] (meta_operation_id mid, operation_id oid) { rs.insert_rule (mid, oid, "cli.compile", m); rs.insert_rule (mid, oid, "cli.compile", m); rs.insert_rule (mid, oid, "cli.compile", m); rs.insert_rule (mid, oid, "cli.compile", m); }; reg (perform_id, update_id); reg (perform_id, clean_id); // Other rules (e.g., cc::compile) may need to have the group members // resolved/linked up. Looks like a general pattern: groups should // resolve on *(update). // // @@ meta-op wildcard? // reg (configure_id, update_id); reg (dist_id, update_id); } return true; } static const module_functions mod_functions[] = { // NOTE: don't forget to also update the documentation in init.hxx if // changing anything here. {"cli.guess", nullptr, guess_init}, {"cli.config", nullptr, config_init}, {"cli", nullptr, init}, {nullptr, nullptr, nullptr} }; const module_functions* build2_cli_load () { return mod_functions; } } }