aboutsummaryrefslogtreecommitdiff
path: root/libbuild2/config/functions.cxx
blob: 3e1b8a3383f419c1566e9342b96ccb2a3654526d (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
// file      : libbuild2/config/functions.cxx -*- C++ -*-
// license   : MIT; see accompanying LICENSE file

#include <sstream>

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

#include <libbuild2/config/module.hxx>
#include <libbuild2/config/operation.hxx>

using namespace std;

namespace build2
{
  namespace config
  {
    void
    functions (function_map& m)
    {
      function_family f (m, "config");

      // $config.origin()
      //
      // Return the origin of the specified configuration variable value.
      // Possible result values and their semantics are as follows:
      //
      // undefined
      //    The variable is undefined.
      //
      // default
      //    The variable has the default value from the config directive (or
      //    as specified by a module).
      //
      // buildfile
      //    The variable has the value from a buildfile, normally config.build
      //    but could also be from file(s) specified with config.config.load.
      //
      // override
      //    The variable has the command line override value. Note that if
      //    the override happens to be append/prepend, then the value could
      //    incorporate the original value.
      //
      // Note that the variable must be specified as a name and not as an
      // expansion (i.e., without $).
      //
      // Note that this function is not pure.
      //
      f.insert (".origin", false) += [] (const scope* s, names name)
      {
        if (s == nullptr)
          fail << "config.origin() called out of scope" << endf;

        // Only look in the root scope since that's the only config.*
        // variables we generally consider.
        //
        s = s->root_scope ();

        if (s == nullptr)
          fail << "config.origin() called out of project" << endf;

        string n (convert<string> (move (name)));

        // Make sure this is a config.* variable. This could matter since we
        // reply on the semantics of value::extra. We could also detect
        // special variables like config.booted, some config.config.*, etc.,
        // (see config_save() for details) but that seems harmless.
        //
        if (n.compare (0, 7, "config.") != 0)
          fail << "non-config.* variable passed to config.origin()" << endf;

        const variable* var (s->ctx.var_pool.find (n));

        if (var == nullptr)
          return "undefined";

        pair<lookup, size_t> org (s->lookup_original (*var));
        pair<lookup, size_t> ovr (var->overrides == nullptr
                                  ? org
                                  : s->lookup_override (*var, org));

        if (!ovr.first.defined ())
          return "undefined";

        if (org.first != ovr.first)
          return "override";

        return org.first->extra ? "default" : "buildfile";
      };

      // $config.save()
      //
      // Return the configuration file contents as a string, similar to the
      // config.config.save variable functionality.
      //
      // Note that this function can only be used during configure unless the
      // config module creation was requested for other meta-operations with
      // config.config.module=true in bootstrap.build.
      //
      // Note that this function is not pure.
      //
      f.insert (".save", false) += [] (const scope* s)
      {
        if (s == nullptr)
          fail << "config.save() called out of scope" << endf;

        s = s->root_scope ();

        if (s == nullptr)
          fail << "config.save() called out of project" << endf;

        // See save_config() for details.
        //
        assert (s->ctx.phase == run_phase::load);
        module* mod (s->rw ().find_module<module> (module::name));

        if (mod == nullptr)
          fail << "config.save() called without config module";

        ostringstream os;

        // Empty project set should is ok as long as inherit is false.
        //
        project_set ps;
        save_config (*s,
                     os, path_name ("config.save()"),
                     false /* inherit */,
                     *mod,
                     ps);

        return os.str ();
      };
    }
  }
}