aboutsummaryrefslogtreecommitdiff
path: root/libbuild2/module.cxx
blob: 50530f263cbdfa2d1b71d2b568a27f21ca0a9fde (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
139
140
141
142
143
144
145
146
147
// file      : libbuild2/module.cxx -*- C++ -*-
// copyright : Copyright (c) 2014-2019 Code Synthesis Ltd
// license   : MIT; see accompanying LICENSE file

#include <libbuild2/module.hxx>

#include <libbuild2/scope.hxx>
#include <libbuild2/variable.hxx>
#include <libbuild2/diagnostics.hxx>

using namespace std;

namespace build2
{
  available_module_map builtin_modules;

  void
  boot_module (scope& rs, const string& name, const location& loc)
  {
    // First see if this modules has already been loaded for this project.
    //
    loaded_module_map& lm (rs.root_extra->modules);
    auto i (lm.find (name));

    if (i != lm.end ())
    {
      module_state& s (i->second);

      // The only valid situation here is if the module has already been
      // bootstrapped.
      //
      assert (s.boot);
      return;
    }

    // Otherwise search for this module.
    //
    auto j (builtin_modules.find (name));

    if (j == builtin_modules.end ())
      fail (loc) << "unknown module " << name;

    const module_functions& mf (j->second);

    if (mf.boot == nullptr)
      fail (loc) << "module " << name << " shouldn't be loaded in bootstrap";

    i = lm.emplace (name,
                    module_state {true, false, mf.init, nullptr, loc}).first;
    i->second.first = mf.boot (rs, loc, i->second.module);

    rs.assign (var_pool.rw (rs).insert (name + ".booted")) = true;
  }

  bool
  load_module (scope& rs,
               scope& bs,
               const string& name,
               const location& loc,
               bool opt,
               const variable_map& hints)
  {
    // First see if this modules has already been loaded for this project.
    //
    loaded_module_map& lm (rs.root_extra->modules);
    auto i (lm.find (name));
    bool f (i == lm.end ());

    if (f)
    {
      // Otherwise search for this module.
      //
      auto j (builtin_modules.find (name));

      if (j == builtin_modules.end ())
      {
        if (!opt)
          fail (loc) << "unknown module " << name;
      }
      else
      {
        const module_functions& mf (j->second);

        if (mf.boot != nullptr)
          fail (loc) << "module " << name << " should be loaded in bootstrap";

        i = lm.emplace (
          name,
          module_state {false, false, mf.init, nullptr, loc}).first;
      }
    }
    else
    {
      module_state& s (i->second);

      if (s.boot)
      {
        s.boot = false;
        f = true; // This is a first call to init.
      }
    }

    // Note: pattern-typed in context.cxx:reset() as project-visibility
    // variables of type bool.
    //
    auto& vp (var_pool.rw (rs));
    value& lv (bs.assign (vp.insert (name + ".loaded")));
    value& cv (bs.assign (vp.insert (name + ".configured")));

    bool l; // Loaded.
    bool c; // Configured.

    // Suppress duplicate init() calls for the same module in the same scope.
    //
    if (!lv.null)
    {
      assert (!cv.null);

      l = cast<bool> (lv);
      c = cast<bool> (cv);

      if (!opt)
      {
        if (!l)
          fail (loc) << "unknown module " << name;

        // We don't have original diagnostics. We could call init() again so
        // that it can issue it. But that means optional modules must be
        // prepared to be called again if configuring failed. Let's keep it
        // simple for now.
        //
        if (!c)
          fail (loc) << "module " << name << " failed to configure";
      }
    }
    else
    {
      l = i != lm.end ();
      c = l && i->second.init (rs, bs, loc, i->second.module, f, opt, hints);

      lv = l;
      cv = c;
    }

    return l && c;
  }
}