aboutsummaryrefslogtreecommitdiff
path: root/build2/config/init.cxx
blob: a5cdf5ac474a677c6157fe634ab77be3de72ff0f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
// file      : build2/config/init.cxx -*- C++ -*-
// copyright : Copyright (c) 2014-2016 Code Synthesis Ltd
// license   : MIT; see accompanying LICENSE file

#include <build2/config/init>

#include <build2/file>
#include <build2/rule>
#include <build2/scope>
#include <build2/context>
#include <build2/filesystem> // file_exists()
#include <build2/diagnostics>

#include <build2/config/module>
#include <build2/config/utility>
#include <build2/config/operation>

using namespace std;
using namespace butl;

namespace build2
{
  namespace config
  {
    const string module::name ("config");
    const uint64_t module::version (1);

    void
    boot (scope& rs, const location& loc, unique_ptr<module_base>&)
    {
      tracer trace ("config::boot");

      const dir_path& out_root (rs.out_path ());
      l5 ([&]{trace << "for " << out_root;});

      // Register meta-operations.
      //
      rs.meta_operations.insert (configure_id, configure);
      rs.meta_operations.insert (disfigure_id, disfigure);

      // Load config.build if one exists.
      //
      // Note that we have to do this during bootstrap since the order in
      // which the modules will be initialized is unspecified. So it is
      // possible that some module which needs the configuration will get
      // called first.
      //
      const variable& c_v (var_pool.insert<uint64_t> ("config.version"));

      // 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<uint64_t> (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
    init (scope& rs,
          scope&,
          const location& l,
          unique_ptr<module_base>& mod,
          bool first,
          bool,
          const variable_map& config_hints)
    {
      tracer trace ("config::init");

      if (!first)
      {
        warn (l) << "multiple config module initializations";
        return true;
      }

      l5 ([&]{trace << "for " << rs.out_path ();});

      assert (config_hints.empty ()); // We don't known any hints.

      // Only create the module if we are configuring.
      //
      if (current_mif->id == configure_id)
        mod.reset (new module);

      // Adjust priority for the import pseudo-module so that config.import.*
      // values come first in config.build.
      //
      config::save_module (rs, "import", INT32_MIN);

      // Register alias and fallback rule for the configure meta-operation.
      //
      {
        // We need this rule for out-of-any-project dependencies (e.g.,
        // libraries imported from /usr/lib).
        //
        global_scope->rules.insert<file> (
          configure_id, 0, "config.file", file_rule::instance);

        auto& r (rs.rules);

        r.insert<target> (configure_id, 0, "config", fallback_rule::instance);
        r.insert<file> (configure_id, 0, "config.file", fallback_rule::instance);
        r.insert<alias> (configure_id, 0, "config.alias", alias_rule::instance);
      }

      return true;
    }
  }
}