From f89d2c16c1dad9b8d2f3b0e402a47e30521f5a69 Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Tue, 30 Aug 2016 09:51:03 +0200 Subject: Add support for config.build file versioning --- build2/b.cxx | 16 ++++++---------- build2/config/init.cxx | 43 +++++++++++++++++++++++++++++++++++++++---- build2/config/module | 2 ++ build2/config/operation.cxx | 10 +++++----- build2/context | 25 ++++++++++++++++++++++++- build2/context.cxx | 5 +++++ build2/dist/operation.cxx | 14 +++++--------- build2/file | 8 ++++++++ build2/file.cxx | 31 ++++++++++++++++++++++--------- 9 files changed, 116 insertions(+), 38 deletions(-) diff --git a/build2/b.cxx b/build2/b.cxx index bb4f0fe..6d6723a 100644 --- a/build2/b.cxx +++ b/build2/b.cxx @@ -297,6 +297,7 @@ main (int argc, char* argv[]) { vector_view opspecs; const string& mname (lifted == nullptr ? mit->name : lifted->name); + current_mname = &mname; if (lifted == nullptr) { @@ -345,6 +346,7 @@ main (int argc, char* argv[]) // A lifted meta-operation will always have default operation. // const string& oname (lifted == nullptr ? os.name : string ()); + current_oname = &oname; if (lifted != nullptr) lifted = nullptr; // Clear for the next iteration. @@ -719,7 +721,7 @@ main (int argc, char* argv[]) if (mif->meta_operation_pre != nullptr) mif->meta_operation_pre (); - current_mif = mif; + set_current_mif (*mif); dirty = true; } // @@ -973,9 +975,7 @@ main (int argc, char* argv[]) if (mif->operation_pre != nullptr) mif->operation_pre (pre_oid); // Cannot be translated. - current_inner_oif = pre_oif; - current_outer_oif = oif; - current_mode = pre_oif->mode; + set_current_oif (*pre_oif, oif); dependency_count = 0; action a (mid, pre_oid, oid); @@ -990,9 +990,7 @@ main (int argc, char* argv[]) << ", id " << static_cast (pre_oid);}); } - current_inner_oif = oif; - current_outer_oif = nullptr; - current_mode = oif->mode; + set_current_oif (*oif); dependency_count = 0; action a (mid, oid, 0); @@ -1008,9 +1006,7 @@ main (int argc, char* argv[]) if (mif->operation_pre != nullptr) mif->operation_pre (post_oid); // Cannot be translated. - current_inner_oif = post_oif; - current_outer_oif = oif; - current_mode = post_oif->mode; + set_current_oif (*post_oif, oif); dependency_count = 0; action a (mid, post_oid, oid); diff --git a/build2/config/init.cxx b/build2/config/init.cxx index 6c713df..368b9d0 100644 --- a/build2/config/init.cxx +++ b/build2/config/init.cxx @@ -23,9 +23,10 @@ namespace build2 namespace config { const string module::name ("config"); + const uint64_t module::version; void - boot (scope& rs, const location&, unique_ptr&) + boot (scope& rs, const location& loc, unique_ptr&) { tracer trace ("config::boot"); @@ -44,10 +45,44 @@ namespace build2 // possible that some module which needs the configuration will get // called first. // - path f (out_root / config_file); + const variable& c_v (var_pool.insert ("config.version")); - if (file_exists (f)) - source (f, rs, rs); + // Don't load it if we are disfiguring. This is a bit tricky since the + // build2 core may not yet know it is disfiguring. But we know. + // + if (*current_mname != disfigure.name && + (!current_mname->empty () || *current_oname != disfigure.name)) + { + path f (out_root / config_file); + + if (file_exists (f)) + { + // 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 (f, c_v.name.c_str ())); + uint64_t v (p.second ? cast (p.first) : 0); + + if (v != module::version) + fail (loc) << "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 (f, rs, rs); + } + } } bool diff --git a/build2/config/module b/build2/config/module index e6fb197..8f9a9b9 100644 --- a/build2/config/module +++ b/build2/config/module @@ -78,7 +78,9 @@ namespace build2 struct module: module_base { config::saved_modules saved_modules; + static const string name; // init.cxx + static constexpr const uint64_t version = 1; }; } } diff --git a/build2/config/operation.cxx b/build2/config/operation.cxx index 90c2bbb..e6ab14a 100644 --- a/build2/config/operation.cxx +++ b/build2/config/operation.cxx @@ -80,6 +80,8 @@ namespace build2 "free to edit." << endl << "#" << endl; + ofs << "config.version = " << module::version << endl; + if (auto l = root.vars["amalgamation"]) { const dir_path& d (cast (l)); @@ -371,13 +373,11 @@ namespace build2 id < rs->operations.size (); ++id) { - const operation_info* oi (rs->operations[id]); - if (oi == nullptr) + const operation_info* oif (rs->operations[id]); + if (oif == nullptr) continue; - current_inner_oif = oi; - current_outer_oif = nullptr; - current_mode = oi->mode; + set_current_oif (*oif); dependency_count = 0; match (action (configure_id, id), t); diff --git a/build2/context b/build2/context index e7b8c7d..257e96b 100644 --- a/build2/context +++ b/build2/context @@ -26,12 +26,35 @@ namespace build2 // Current action (meta/operation). // + // The names unlike info are available during boot but may not yet be + // lifted. The name is always for an outer operation (or meta operation + // that hasn't been recognized as such yet). + // + extern const string* current_mname; + extern const string* current_oname; + extern const meta_operation_info* current_mif; extern const operation_info* current_inner_oif; extern const operation_info* current_outer_oif; - extern execution_mode current_mode; + inline void + set_current_mif (const meta_operation_info& mif) + { + current_mif = &mif; + current_mname = &mif.name; + } + + inline void + set_current_oif (const operation_info& inner_oif, + const operation_info* outer_oif = nullptr) + { + current_inner_oif = &inner_oif; + current_outer_oif = outer_oif; + current_oname = &(outer_oif == nullptr ? inner_oif : *outer_oif).name; + current_mode = inner_oif.mode; + } + // Total number of dependency relationships in the current action. // Together with the target::dependents count it is incremented // during the rule search & match phase and is decremented during diff --git a/build2/context.cxx b/build2/context.cxx index ac972ac..0e05926 100644 --- a/build2/context.cxx +++ b/build2/context.cxx @@ -32,10 +32,15 @@ namespace build2 string_pool extension_pool; string_pool project_name_pool; + const string* current_mname; + const string* current_oname; + const meta_operation_info* current_mif; const operation_info* current_inner_oif; const operation_info* current_outer_oif; + execution_mode current_mode; + uint64_t dependency_count; variable_overrides diff --git a/build2/dist/operation.cxx b/build2/dist/operation.cxx index d03fe28..12bcc61 100644 --- a/build2/dist/operation.cxx +++ b/build2/dist/operation.cxx @@ -101,17 +101,15 @@ namespace build2 id < rs->operations.size (); ++id) { - const operation_info* oi (rs->operations[id]); - if (oi == nullptr) + const operation_info* oif (rs->operations[id]); + if (oif == nullptr) continue; // Note that we are not calling operation_pre/post() callbacks // here since the meta operation is dist and we know what we // are doing. // - current_inner_oif = oi; - current_outer_oif = nullptr; - current_mode = oi->mode; + set_current_oif (*oif); dependency_count = 0; action a (dist_id, id); @@ -237,14 +235,12 @@ namespace build2 if (perform.meta_operation_pre != nullptr) perform.meta_operation_pre (); - current_mif = &perform; + set_current_mif (perform); if (perform.operation_pre != nullptr) perform.operation_pre (update_id); - current_inner_oif = &update; - current_outer_oif = nullptr; - current_mode = update.mode; + set_current_oif (update); dependency_count = 0; action a (perform_id, update_id); diff --git a/build2/file b/build2/file index f5dac83..1502d73 100644 --- a/build2/file +++ b/build2/file @@ -129,6 +129,14 @@ namespace build2 void load_root_pre (scope& root); + // Extract the specified variable value from a buildfile. It is expected to + // be the first non-comment line and not to rely on any variable expansion + // other than those from the global scope or any variable overrides. Return + // an indication of whether the variable was found. + // + pair + extract_variable (const path&, const char* var); + // 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 diff --git a/build2/file.cxx b/build2/file.cxx index d267fec..46362e1 100644 --- a/build2/file.cxx +++ b/build2/file.cxx @@ -272,7 +272,7 @@ namespace build2 // be the first non-comment line and not to rely on any variable expansion // other than those from the global scope or any variable overrides. // - static value + pair extract_variable (const path& bf, const char* name) { try @@ -288,8 +288,7 @@ namespace build2 tt != token_type::prepend && tt != token_type::append)) { - error << "variable '" << name << "' expected as first line in " << bf; - throw failed (); // Suppress "used uninitialized" warning. + return make_pair (value (), false); } const variable& var (var_pool.find (move (t.value))); @@ -300,14 +299,18 @@ namespace build2 value* v (tmp.vars.find (var)); assert (v != nullptr); - return move (*v); // Steal the value, the scope is going away. + + // Steal the value, the scope is going away. + // + return make_pair (move (*v), true); } catch (const ifdstream::failure& e) { - fail << "unable to read buildfile " << bf << ": " << e.what (); + error << "unable to read buildfile " << bf << ": " << e.what (); + throw failed (); } - return value (); // Never reaches. + // Never reached. } // Extract the project name from bootstrap.build. @@ -336,7 +339,12 @@ namespace build2 src_root = &fallback_src_root; else { - src_root_v = extract_variable (f, "src_root"); + auto p (extract_variable (f, "src_root")); + + if (!p.second) + error << "variable 'src_root' expected as first line in " << f; + + src_root_v = move (p.first); src_root = &cast (src_root_v); l5 ([&]{trace << "extracted src_root " << *src_root << " for " << out_root;}); @@ -345,8 +353,13 @@ namespace build2 string name; { - value v (extract_variable (*src_root / bootstrap_file, "project")); - name = cast (move (v)); + path f (*src_root / bootstrap_file); + auto p (extract_variable (f, "project")); + + if (!p.second) + error << "variable 'project' expected as first line in " << f; + + name = cast (move (p.first)); } l5 ([&]{trace << "extracted project name '" << name << "' for " -- cgit v1.1