diff options
-rw-r--r-- | build2/b.cxx | 2 | ||||
-rw-r--r-- | libbuild2/config/init.cxx | 108 | ||||
-rw-r--r-- | libbuild2/config/module.cxx | 2 | ||||
-rw-r--r-- | libbuild2/config/operation.cxx | 264 | ||||
-rw-r--r-- | libbuild2/context.cxx | 2 | ||||
-rw-r--r-- | libbuild2/dist/operation.cxx | 6 | ||||
-rw-r--r-- | libbuild2/file.cxx | 95 | ||||
-rw-r--r-- | libbuild2/file.hxx | 26 | ||||
-rw-r--r-- | libbuild2/lexer.hxx | 2 | ||||
-rw-r--r-- | libbuild2/module.cxx | 2 | ||||
-rw-r--r-- | libbuild2/operation.cxx | 14 | ||||
-rw-r--r-- | libbuild2/operation.hxx | 7 | ||||
-rw-r--r-- | libbuild2/parser.cxx | 11 | ||||
-rw-r--r-- | libbuild2/parser.hxx | 3 | ||||
-rw-r--r-- | libbuild2/utility.cxx | 32 | ||||
-rw-r--r-- | libbuild2/utility.hxx | 13 | ||||
-rw-r--r-- | libbuild2/version/init.cxx | 3 |
17 files changed, 416 insertions, 176 deletions
diff --git a/build2/b.cxx b/build2/b.cxx index 4571567..1ebcc53 100644 --- a/build2/b.cxx +++ b/build2/b.cxx @@ -106,7 +106,7 @@ namespace build2 if (ops.structured_result ()) { - const target& t (at.as_target ()); + const target& t (at.as<target> ()); context& ctx (t.ctx); cout << at.state diff --git a/libbuild2/config/init.cxx b/libbuild2/config/init.cxx index 6998017..4e1890a 100644 --- a/libbuild2/config/init.cxx +++ b/libbuild2/config/init.cxx @@ -6,6 +6,7 @@ #include <libbuild2/file.hxx> #include <libbuild2/rule.hxx> +#include <libbuild2/lexer.hxx> #include <libbuild2/scope.hxx> #include <libbuild2/context.hxx> #include <libbuild2/filesystem.hxx> // exists() @@ -32,9 +33,18 @@ namespace build2 const string& mname (rs.ctx.current_mname); const string& oname (rs.ctx.current_oname); - // Only create the module if we are configuring or creating. This is a - // bit tricky since the build2 core may not yet know if this is the - // case. But we know. + // While config.import (see below) could theoretically be specified in a + // buildfile, config.export is expected to always be specified as a + // command line override. + // + // Note: must be entered during bootstrap since we need it in + // configure_execute(). + // + vp.insert<path> ("config.export", true /* ovr */); + + // Only create the module if we are configuring or creating or if it was + // forced with config.module (useful if we need to call $config.export() + // during other meta-operations). // if (( mname == "configure" || mname == "create") || (mname.empty () && (oname == "configure" || oname == "create"))) @@ -80,42 +90,80 @@ namespace build2 assert (config_hints.empty ()); // We don't known any hints. + // Note that the config.<name>* variables belong to the module <name>. + // So the only "special" variables we can allocate in config.* are + // config.config.*, names that have been "gifted" to us by other modules + // (like config.version) as well as names that we have reserved to not + // be valid module names (build, import, export). + // auto& vp (rs.ctx.var_pool.rw (rs)); - // Load config.build if one exists (we don't need to worry about - // disfigure since we will never be init'ed). + auto& c_v (vp.insert<uint64_t> ("config.version", false /*ovr*/)); + auto& c_i (vp.insert<paths> ("config.import", true /* ovr */)); + + // Load config.build if one exists followed by extra files specified in + // config.import (we don't need to worry about disfigure since we will + // never be init'ed). // - const variable& c_v (vp.insert<uint64_t> ("config.version", false)); + auto load_config = [&rs, &c_v] (const path& f, const location& l) + { + // Check the config version. We assume that old versions cannot + // understand new configs and new versions are incompatible with old + // configs. + // + // We extract the value manually instead of loading and then checking + // in order to be able to fixup/migrate the file which we may want to + // do in the future. + // + + // This is tricky for stdin since we cannot reopen it (or put more + // than one character back). So what we are going to do is continue + // reading after extracting the variable. One side effect of this is + // that we won't have the config.version variable entered in the scope + // but that is harmless (we could do it manually if necessary). + // + ifdstream ifs; + lexer lex (open_file_or_stdin (f, ifs), f); + + // Assume missing version is 0. + // + auto p (extract_variable (rs.ctx, lex, c_v)); + uint64_t v (p.second ? cast<uint64_t> (p.first) : 0); + + if (v != module::version) + fail (l) << "incompatible config file " << f << + info << "config file version " << v + << (p.second ? "" : " (missing)") << + info << "config module version " << module::version << + info << "consider reconfiguring " << project (rs) << '@' + << rs.out_path (); + + source (rs, rs, lex); + }; { path f (config_file (rs)); if (exists (f)) + load_config (f, l); + } + + if (lookup l = rs[c_i]) + { + // Only load files that were specified on our root scope as well as + // global overrides. This way we can use our override "positioning" + // machinery (i.e., where the override applies) to decide where the + // extra config is loaded. The resulting semantics feels quite natural + // and consistent with command line variable overrides: + // + // b config.import=.../config.build # outermost amalgamation + // b ./config.import=.../config.build # this project + // b !config.import=.../config.build # every project + // + if (l.belongs (rs) || l.belongs (rs.ctx.global_scope)) { - // Check the config version. We assume that old versions cannot - // understand new configs and new versions are incompatible with old - // configs. - // - // We extract the value manually instead of loading and then - // checking in order to be able to fixup/migrate the file which we - // may want to do in the future. - // - { - // Assume missing version is 0. - // - auto p (extract_variable (rs.ctx, f, c_v)); - uint64_t v (p.second ? cast<uint64_t> (p.first) : 0); - - if (v != module::version) - fail (l) << "incompatible config file " << f << - info << "config file version " << v - << (p.second ? "" : " (missing)") << - info << "config module version " << module::version << - info << "consider reconfiguring " << project (rs) << '@' - << out_root; - } - - source (rs, rs, f); + for (const path& f: cast<paths> (l)) + load_config (f, location (&f)); } } diff --git a/libbuild2/config/module.cxx b/libbuild2/config/module.cxx index 7e9b765..b43f1d9 100644 --- a/libbuild2/config/module.cxx +++ b/libbuild2/config/module.cxx @@ -30,7 +30,7 @@ namespace build2 i = sm.insert (string (n, 0, n.find ('.', 7))); } - // Don't insert duplicates. The config.import vars are particularly + // Don't insert duplicates. The config.import.* vars are particularly // susceptible to duplication. // saved_variables& sv (i->second); diff --git a/libbuild2/config/operation.cxx b/libbuild2/config/operation.cxx index 535018e..9050645 100644 --- a/libbuild2/config/operation.cxx +++ b/libbuild2/config/operation.cxx @@ -87,35 +87,45 @@ namespace build2 using project_set = set<const scope*>; // Use pointers to get comparison. + // If inherit is false, then don't rely on inheritance from outer scopes + // (used for config.export). + // static void - save_config (const scope& rs, const project_set& projects) + save_config (const scope& rs, + const path& f, + bool inherit, + const project_set& projects) { context& ctx (rs.ctx); - path f (config_file (rs)); + const module& mod (*rs.lookup_module<const module> (module::name)); - if (verb) - text << (verb >= 2 ? "cat >" : "save ") << f; + const string& df (f.string () != "-" ? f.string () : "<stdout>"); - const module& mod (*rs.lookup_module<const module> (module::name)); + if (verb) + text << (verb >= 2 ? "cat >" : "save ") << df; try { - ofdstream ofs (f); + ofdstream ofs; + ostream& os (open_file_or_stdout (f, ofs)); - ofs << "# Created automatically by the config module, but feel " << + os << "# Created automatically by the config module, but feel " << "free to edit." << endl - << "#" << endl; + << "#" << endl; - ofs << "config.version = " << module::version << endl; + os << "config.version = " << module::version << endl; - if (auto l = rs.vars[ctx.var_amalgamation]) + if (inherit) { - const dir_path& d (cast<dir_path> (l)); + if (auto l = rs.vars[ctx.var_amalgamation]) + { + const dir_path& d (cast<dir_path> (l)); - ofs << endl - << "# Base configuration inherited from " << d << endl - << "#" << endl; + os << endl + << "# Base configuration inherited from " << d << endl + << "#" << endl; + } } // Save config variables. @@ -146,6 +156,11 @@ namespace build2 if (!l.defined ()) continue; + // Handle inherited from outer scope values. + // + // Note that we keep this logic (with warnings and all) even if + // inherit is false to make things easier to reason about. + // if (!(l.belongs (rs) || l.belongs (ctx.global_scope))) { // This is presumably an inherited value. But it could also be @@ -205,42 +220,50 @@ namespace build2 } } - if (found) // Inherited. - continue; - - location loc (&f); - - // If this value is not defined in a project's root scope, then - // something is broken. - // - if (r == nullptr) - fail (loc) << "inherited variable " << var << " value " - << "is not from a root scope"; - - // If none of the outer project's configurations use this value, - // then we warn and save as our own. One special case where we - // don't want to warn the user is if the variable is overriden. - // - if (org.first == ovr.first) + if (found) { - diag_record dr; - dr << warn (loc) << "saving previously inherited variable " - << var; - - dr << info (loc) << "because project " << *r - << " no longer uses it in its configuration"; - - if (verb >= 2) + // Inherited. + // + if (inherit) + continue; + } + else + { + location loc (&f); + + // If this value is not defined in a project's root scope, + // then something is broken. + // + if (r == nullptr) + fail (loc) << "inherited variable " << var << " value " + << "is not from a root scope"; + + // If none of the outer project's configurations use this + // value, then we warn and save as our own. One special case + // where we don't want to warn the user is if the variable is + // overriden. + // + if (org.first == ovr.first) { - dr << info (loc) << "variable value: "; + diag_record dr; + dr << warn (loc) << "saving previously inherited variable " + << var; - if (*l) + dr << info (loc) << "because project " << *r + << " no longer uses it in its configuration"; + + if (verb >= 2) { - storage.clear (); - dr << "'" << reverse (*l, storage) << "'"; + dr << info (loc) << "variable value: "; + + if (*l) + { + storage.clear (); + dr << "'" << reverse (*l, storage) << "'"; + } + else + dr << "[null]"; } - else - dr << "[null]"; } } } @@ -264,7 +287,7 @@ namespace build2 // if (first) { - ofs << endl; + os << endl; first = false; } @@ -274,7 +297,7 @@ namespace build2 org.first == ovr.first && // Not overriden. (sv.flags & save_commented) == save_commented) { - ofs << '#' << n << " =" << endl; + os << '#' << n << " =" << endl; continue; } @@ -283,20 +306,20 @@ namespace build2 storage.clear (); names_view ns (reverse (v, storage)); - ofs << n; + os << n; if (ns.empty ()) - ofs << " ="; + os << " ="; else { - ofs << " = "; - to_stream (ofs, ns, true, '@'); // Quote. + os << " = "; + to_stream (os, ns, true, '@'); // Quote. } - ofs << endl; + os << endl; } else - ofs << n << " = [null]" << endl; + os << n << " = [null]" << endl; } } @@ -304,12 +327,15 @@ namespace build2 } catch (const io_error& e) { - fail << "unable to write " << f << ": " << e; + fail << "unable to write " << df << ": " << e; } } static void - configure_project (action a, const scope& rs, project_set& projects) + configure_project (action a, + const scope& rs, + const variable* c_e, // config.export + project_set& projects) { tracer trace ("configure_project"); @@ -332,8 +358,7 @@ namespace build2 mkdir (out_root / rs.root_extra->bootstrap_dir, 2); } - // We distinguish between a complete configure and operation- - // specific. + // We distinguish between a complete configure and operation-specific. // if (a.operation () == default_id) { @@ -341,15 +366,48 @@ namespace build2 // Save src-root.build unless out_root is the same as src. // - if (out_root != src_root) + if (c_e == nullptr && out_root != src_root) save_src_root (rs); - // Save config.build. + // Save config.build unless an alternative is specified with + // config.export. Similar to config.import we will only save to that + // file if it is specified on our root scope or as a global override + // (the latter is a bit iffy but let's allow it, for example, to dump + // everything to stdout). Note that to save a subproject's config we + // will have to use a scope-specific override (since the default will + // apply to the amalgamation): // - save_config (rs, projects); + // b configure: subproj/ subproj/config.export=.../config.build + // + // Could be confusing but then normally it will be the amalgamation + // whose configuration we want to export. + // + // Note also that if config.export is specified we do not rewrite + // config.build files (say, of subprojects) as well as src-root.build + // above. Failed that, if we are running in a disfigured project, we + // may end up leaving it in partially configured state. + // + if (c_e == nullptr) + save_config (rs, config_file (rs), true /* inherit */, projects); + else + { + lookup l (rs[*c_e]); + if (l && (l.belongs (rs) || l.belongs (ctx.global_scope))) + { + // While writing the complete configuration seems like a natural + // default, there might be a desire to take inheritance into + // account (if, say, we are exporting at multiple levels). One can + // of course just copy the relevant config.build files, but we may + // still want to support this mode somehow in the future (maybe + // using `+` as a modifier, say config.export=+.../config.build). + // + save_config (rs, cast<path> (l), false /* inherit */, projects); + } + } } else { + fail << "operation-specific configuration not yet supported"; } // Configure subprojects that have been loaded. @@ -368,7 +426,7 @@ namespace build2 if (nrs.out_path () != out_nroot) // This subproject not loaded. continue; - configure_project (a, nrs, projects); + configure_project (a, nrs, c_e, projects); } } } @@ -513,6 +571,15 @@ namespace build2 { bool fwd (forward (params)); + context& ctx (fwd ? ts[0].as<scope> ().ctx : ts[0].as<target> ().ctx); + + const variable* c_e (ctx.var_pool.find ("config.export")); + + if (c_e->overrides == nullptr) + c_e = nullptr; + else if (fwd) + fail << "config.export specified for forward configuration"; + project_set projects; for (const action_target& at: ts) @@ -521,53 +588,52 @@ namespace build2 { // Forward configuration. // - const scope& rs (*static_cast<const scope*> (at.target)); + const scope& rs (at.as<scope> ()); configure_forward (rs, projects); - continue; } + else + { + // Normal configuration. + // + // Match rules to configure every operation supported by each + // project. Note that we are not calling operation_pre/post() + // callbacks here since the meta operation is configure and we know + // what we are doing. + // + // Note that we cannot do this in parallel. We cannot parallelize + // the outer loop because we should match for a single action at a + // time. And we cannot swap the loops because the list of operations + // is target-specific. However, inside match(), things can proceed + // in parallel. + // + const target& t (at.as<target> ()); + const scope* rs (t.base_scope ().root_scope ()); - // Normal configuration. - // - // Match rules to configure every operation supported by each project. - // Note that we are not calling operation_pre/post() callbacks here - // since the meta operation is configure and we know what we are - // doing. - // - // Note that we cannot do this in parallel. We cannot parallelize the - // outer loop because we should match for a single action at a time. - // And we cannot swap the loops because the list of operations is - // target-specific. However, inside match(), things can proceed in - // parallel. - // - const target& t (at.as_target ()); - const scope* rs (t.base_scope ().root_scope ()); - - if (rs == nullptr) - fail << "out of project target " << t; - - context& ctx (t.ctx); + if (rs == nullptr) + fail << "out of project target " << t; - const operations& ops (rs->root_extra->operations); + const operations& ops (rs->root_extra->operations); - for (operation_id id (default_id + 1); // Skip default_id. - id < ops.size (); - ++id) - { - if (const operation_info* oif = ops[id]) + for (operation_id id (default_id + 1); // Skip default_id. + id < ops.size (); + ++id) { - // Skip aliases (e.g., update-for-install). - // - if (oif->id != id) - continue; + if (const operation_info* oif = ops[id]) + { + // Skip aliases (e.g., update-for-install). + // + if (oif->id != id) + continue; - ctx.current_operation (*oif); + ctx.current_operation (*oif); - phase_lock pl (ctx, run_phase::match); - match (action (configure_id, id), t); + phase_lock pl (ctx, run_phase::match); + match (action (configure_id, id), t); + } } - } - configure_project (a, *rs, projects); + configure_project (a, *rs, c_e, projects); + } } } @@ -802,7 +868,7 @@ namespace build2 // for (const action_target& at: ts) { - const scope& rs (*static_cast<const scope*> (at.target)); + const scope& rs (at.as<scope> ()); if (!(fwd ? disfigure_forward ( rs, projects) diff --git a/libbuild2/context.cxx b/libbuild2/context.cxx index 292feed..b4a3e93 100644 --- a/libbuild2/context.cxx +++ b/libbuild2/context.cxx @@ -473,7 +473,7 @@ namespace build2 // Enter builtin variables and patterns. // - // All config. variables are by default overridable. + // All config.* variables are by default overridable. // vp.insert_pattern ("config.**", nullopt, true, nullopt, true, false); diff --git a/libbuild2/dist/operation.cxx b/libbuild2/dist/operation.cxx index 391f7d8..ad829df 100644 --- a/libbuild2/dist/operation.cxx +++ b/libbuild2/dist/operation.cxx @@ -75,7 +75,7 @@ namespace build2 // For now we assume all the targets are from the same project. // - const target& t (ts[0].as_target ()); + const target& t (ts[0].as<target> ()); const scope* rs (t.base_scope ().root_scope ()); if (rs == nullptr) @@ -118,7 +118,7 @@ namespace build2 // for (const action_target& at: ts) { - const target& t (at.as_target ()); + const target& t (at.as<target> ()); if (rs != t.base_scope ().root_scope ()) fail << "target " << t << " is from a different project" << @@ -353,7 +353,7 @@ namespace build2 for (size_t i (0), n (files.size ()); i != n; ++i) { - const file& t (*files[i].as_target ().is_a<file> ()); + const file& t (*files[i].as<target> ().is_a<file> ()); // Figure out where this file is inside the target directory. // diff --git a/libbuild2/file.cxx b/libbuild2/file.cxx index 603943c..b55d576 100644 --- a/libbuild2/file.cxx +++ b/libbuild2/file.cxx @@ -4,8 +4,6 @@ #include <libbuild2/file.hxx> -#include <iostream> // cin - #include <libbuild2/scope.hxx> #include <libbuild2/target.hxx> #include <libbuild2/context.hxx> @@ -161,27 +159,39 @@ namespace build2 } static void - source (scope& root, scope& base, const path& bf, bool boot) + source (scope& root, scope& base, lexer& l, bool boot) { tracer trace ("source"); + const path& bf (l.name ()); + try { - bool sin (bf.string () == "-"); - - ifdstream ifs; - - if (!sin) - ifs.open (bf); - else - cin.exceptions (ifdstream::failbit | ifdstream::badbit); - - istream& is (sin ? cin : ifs); - l5 ([&]{trace << "sourcing " << bf;}); parser p (root.ctx, boot); - p.parse_buildfile (is, bf, root, base); + p.parse_buildfile (l, root, base); + } + catch (const io_error& e) + { + fail << "unable to read buildfile " << bf << ": " << e; + } + } + + static void + source (scope& root, scope& base, istream& is, const path& bf, bool boot) + { + lexer l (is, bf); + source (root, base, l, boot); + } + + static void + source (scope& root, scope& base, const path& bf, bool boot) + { + try + { + ifdstream ifs; + return source (root, base, open_file_or_stdin (bf, ifs), bf, boot); } catch (const io_error& e) { @@ -195,6 +205,18 @@ namespace build2 source (root, base, bf, false); } + void + source (scope& root, scope& base, istream& is, const path& bf) + { + source (root, base, is, bf, false); + } + + void + source (scope& root, scope& base, lexer& l) + { + source (root, base, l, false); + } + bool source_once (scope& root, scope& base, const path& bf, scope& once) { @@ -206,7 +228,7 @@ namespace build2 return false; } - source (root, base, bf); + source (root, base, bf, false); return true; } @@ -498,18 +520,17 @@ namespace build2 } pair<value, bool> - extract_variable (context& ctx, const path& bf, const variable& var) + extract_variable (context& ctx, lexer& l, const variable& var) { + const path& bf (l.name ()); + try { - ifdstream ifs (bf); - - lexer lex (ifs, bf); - token t (lex.next ()); + token t (l.next ()); token_type tt; if (t.type != token_type::word || t.value != var.name || - ((tt = lex.next ().type) != token_type::assign && + ((tt = l.next ().type) != token_type::assign && tt != token_type::prepend && tt != token_type::append)) { @@ -518,7 +539,7 @@ namespace build2 parser p (ctx); temp_scope tmp (ctx.global_scope.rw ()); - p.parse_variable (lex, tmp, var, tt); + p.parse_variable (l, tmp, var, tt); value* v (tmp.vars.find_to_modify (var).first); assert (v != nullptr); @@ -533,6 +554,32 @@ namespace build2 } } + pair<value, bool> + extract_variable (context& ctx, + istream& is, + const path& bf, + const variable& var) + { + lexer l (is, bf); + return extract_variable (ctx, l, var); + } + + pair<value, bool> + extract_variable (context& ctx, const path& bf, const variable& var) + { + try + { + ifdstream ifs (bf); + return extract_variable (ctx, ifs, bf, var); + } + catch (const io_error& e) + { + fail << "unable to read buildfile " << bf << ": " << e << endf; + } + } + + + // Extract the project name from bootstrap.build. // static project_name @@ -745,7 +792,7 @@ namespace build2 // root scope multiple time. // if (rs.buildfiles.insert (f).second) - source (rs, rs, f, true); + source (rs, rs, f, true /* boot */); else l5 ([&]{trace << "skipping already sourced " << f;}); diff --git a/libbuild2/file.hxx b/libbuild2/file.hxx index 3d3c38b..78bd049 100644 --- a/libbuild2/file.hxx +++ b/libbuild2/file.hxx @@ -18,6 +18,8 @@ namespace build2 { + class lexer; + using subprojects = std::map<project_name, dir_path>; LIBBUILD2_SYMEXPORT ostream& @@ -71,6 +73,18 @@ namespace build2 LIBBUILD2_SYMEXPORT void source (scope& root, scope& base, const path&); + // As above, but extract from a stream. The name argument is used for + // diagnostics. + // + LIBBUILD2_SYMEXPORT void + source (scope& root, scope& base, istream&, const path& name); + + // As above, but extract from a lexer (this could be useful for sourcing + // stdin that requires parse_variable()). + // + LIBBUILD2_SYMEXPORT void + source (scope& root, scope& base, lexer&); + // As above but first check if this buildfile has already been sourced for // the base scope. Return false if the file has already been sourced. // @@ -195,6 +209,18 @@ namespace build2 LIBBUILD2_SYMEXPORT pair<value, bool> extract_variable (context&, const path&, const variable&); + // As above, but extract from a stream. The name argument is used for + // diagnostics. + // + LIBBUILD2_SYMEXPORT pair<value, bool> + extract_variable (context&, istream&, const path& name, const variable&); + + // As above, but extract from a lexer (this could be useful for extracting + // from stdin). + // + LIBBUILD2_SYMEXPORT pair<value, bool> + extract_variable (context&, lexer&, const variable&); + // Import has two phases: the first is triggered by the import directive in // the buildfile. It will try to find and load the project. Failed that, it // will return the project-qualified name of the target which will be used diff --git a/libbuild2/lexer.hxx b/libbuild2/lexer.hxx index 90d546d..a2c7431 100644 --- a/libbuild2/lexer.hxx +++ b/libbuild2/lexer.hxx @@ -194,7 +194,7 @@ namespace build2 mode (lexer_mode::normal, '@', escapes); } - const path name_; + const path name_; // @@ TODO: why not shallow (like istream)? std::stack<state> state_; bool sep_; // True if we skipped spaces in peek(). diff --git a/libbuild2/module.cxx b/libbuild2/module.cxx index 50c6d53..a8304ff 100644 --- a/libbuild2/module.cxx +++ b/libbuild2/module.cxx @@ -304,7 +304,7 @@ namespace build2 } assert (tgs.size () == 1); - const target& l (tgs[0].as_target ()); + const target& l (tgs[0].as<target> ()); if (!l.is_a ("libs")) fail (loc) << "wrong export from build system module " << mod; diff --git a/libbuild2/operation.cxx b/libbuild2/operation.cxx index a9b6107..86cd571 100644 --- a/libbuild2/operation.cxx +++ b/libbuild2/operation.cxx @@ -136,7 +136,7 @@ namespace build2 if (ts.empty ()) return; - context& ctx (ts[0].as_target ().ctx); + context& ctx (ts[0].as<target> ().ctx); { phase_lock l (ctx, run_phase::match); @@ -176,7 +176,7 @@ namespace build2 for (; i != n; ++i) { - const target& t (ts[i].as_target ()); + const target& t (ts[i].as<target> ()); l5 ([&]{trace << diag_doing (a, t);}); target_state s (match_async (a, t, 0, task_count, false)); @@ -208,7 +208,7 @@ namespace build2 for (size_t j (0); j != n; ++j) { action_target& at (ts[j]); - const target& t (at.as_target ()); + const target& t (at.as<target> ()); target_state s (j < i ? match (a, t, false) @@ -264,7 +264,7 @@ namespace build2 if (ts.empty ()) return; - context& ctx (ts[0].as_target ().ctx); + context& ctx (ts[0].as<target> ().ctx); // Reverse the order of targets if the execution mode is 'last'. // @@ -338,7 +338,7 @@ namespace build2 for (const action_target& at: ts) { - const target& t (at.as_target ()); + const target& t (at.as<target> ()); l5 ([&]{trace << diag_doing (a, t);}); @@ -389,7 +389,7 @@ namespace build2 bool fail (false); for (action_target& at: ts) { - const target& t (at.as_target ()); + const target& t (at.as<target> ()); switch ((at.state = t.executed_state (a, false))) { @@ -521,7 +521,7 @@ namespace build2 if (i != 0) cout << endl; - const scope& rs (*static_cast<const scope*> (ts[i].target)); + const scope& rs (ts[i].as<scope> ()); context& ctx (rs.ctx); diff --git a/libbuild2/operation.hxx b/libbuild2/operation.hxx index 7b6310f..921b77a 100644 --- a/libbuild2/operation.hxx +++ b/libbuild2/operation.hxx @@ -29,16 +29,15 @@ namespace build2 // struct action_target { - using target_type = build2::target; - const void* target = nullptr; target_state state = target_state::unknown; action_target () = default; action_target (const void* t): target (t) {} - const target_type& - as_target () const {return *static_cast<const target_type*> (target);} + template <typename T> + const T& + as () const {return *static_cast<const T*> (target);} }; class action_targets: public vector<action_target> diff --git a/libbuild2/parser.cxx b/libbuild2/parser.cxx index ca4f8d5..7b76d18 100644 --- a/libbuild2/parser.cxx +++ b/libbuild2/parser.cxx @@ -213,9 +213,14 @@ namespace build2 void parser:: parse_buildfile (istream& is, const path& p, scope& root, scope& base) { - path_ = &p; + lexer l (is, p); + parse_buildfile (l, root, base); + } - lexer l (is, *path_); + void parser:: + parse_buildfile (lexer& l, scope& root, scope& base) + { + path_ = &l.name (); lexer_ = &l; root_ = &root; scope_ = &base; @@ -224,7 +229,7 @@ namespace build2 prerequisite_ = nullptr; default_target_ = nullptr; - enter_buildfile (p); // Needs scope_. + enter_buildfile (*path_); // Needs scope_. token t; type tt; diff --git a/libbuild2/parser.hxx b/libbuild2/parser.hxx index 8b6179c..04a8e5c 100644 --- a/libbuild2/parser.hxx +++ b/libbuild2/parser.hxx @@ -36,6 +36,9 @@ namespace build2 void parse_buildfile (istream&, const path& name, scope& root, scope& base); + void + parse_buildfile (lexer&, scope& root, scope& base); + buildspec parse_buildspec (istream&, const path& name); diff --git a/libbuild2/utility.cxx b/libbuild2/utility.cxx index 78d2df2..7f40688 100644 --- a/libbuild2/utility.cxx +++ b/libbuild2/utility.cxx @@ -8,7 +8,7 @@ #include <cerrno> // ENOENT #include <cstring> // strlen(), str[n]cmp() -#include <iostream> // cerr +#include <iostream> // cin cout cerr #include <libbuild2/target.hxx> #include <libbuild2/context.hxx> @@ -100,6 +100,36 @@ namespace build2 dir_path home; const dir_path* relative_base = &work; + istream& + open_file_or_stdin (const path& f, ifdstream& ifs) + { + if (f.string () != "-") + { + ifs.open (f); + return ifs; + } + else + { + cin.exceptions (ifdstream::failbit | ifdstream::badbit); + return cin; + } + } + + ostream& + open_file_or_stdout (const path& f, ofdstream& ofs) + { + if (f.string () != "-") + { + ofs.open (f); + return ofs; + } + else + { + cout.exceptions (ofdstream::failbit | ofdstream::badbit); + return cout; + } + } + path relative (const path_target& t) { diff --git a/libbuild2/utility.hxx b/libbuild2/utility.hxx index 43b556e..415dc8b 100644 --- a/libbuild2/utility.hxx +++ b/libbuild2/utility.hxx @@ -88,6 +88,19 @@ namespace build2 using butl::eof; + // Open a file or, if the file name is `-`, stdin/stdout. + // + // Note that ofdstream::close() should be called explicitly if not stdout + // (but harmless to call even if it is). Also note that our overload of + // operator<<(path) always translats `-` to `<stdin>` so care must be taken + // when issuing diagnostics. + // + istream& + open_file_or_stdin (const path&, ifdstream&); + + ostream& + open_file_or_stdout (const path&, ofdstream&); + // Diagnostics state (verbosity level, etc; see <libbuild2/diagnostics.hxx>). // // Note on naming of values (here and in the global state below) that come diff --git a/libbuild2/version/init.cxx b/libbuild2/version/init.cxx index 123dc65..c9d5021 100644 --- a/libbuild2/version/init.cxx +++ b/libbuild2/version/init.cxx @@ -221,6 +221,9 @@ namespace build2 // Set all the version.* variables. // + // Note also that we have "gifted" the config.version variable name to + // the config module. + // auto& vp (ctx.var_pool.rw (rs)); auto set = [&vp, &rs] (const char* var, auto val) |