aboutsummaryrefslogtreecommitdiff
path: root/libbuild2/build/script/script.cxx
blob: 3485f544a96abe846887d88c1fc5c76f3654a080 (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
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
// file      : libbuild2/build/script/script.cxx -*- C++ -*-
// license   : MIT; see accompanying LICENSE file

#include <libbuild2/build/script/script.hxx>

#include <libbutl/filesystem.mxx>

#include <libbuild2/target.hxx>

#include <libbuild2/build/script/parser.hxx>

using namespace std;

namespace build2
{
  namespace build
  {
    namespace script
    {
      // environment
      //
      static const optional<string> wd_name ("current directory");

      environment::
      environment (action a, const target_type& t, bool temp)
          : build2::script::environment (
              t.ctx,
              cast<target_triplet> (t.ctx.global_scope["build.host"]),
              dir_name_view (&work, &wd_name),
              temp_dir.path, false /* temp_dir_keep */,
              redirect (redirect_type::none),
              redirect (redirect_type::merge, 2),
              redirect (redirect_type::pass)),
            target (t),
            vars (context, false /* global */)
      {
        // Set special variables.
        //
        {
          // $>
          //
          names ns;
          for (const target_type* m (&t); m != nullptr; m = m->adhoc_member)
            m->as_name (ns);

          assign (var_pool.insert (">")) = move (ns);
        }

        {
          // $<
          //
          // Note that at this stage (after execute_prerequisites()) ad hoc
          // prerequisites are no longer in prerequisite_targets which means
          // they won't end up in $< either. While at first thought ad hoc
          // prerequisites in ad hoc recipes don't seem to make much sense,
          // they could be handy to exclude certain preresquisites from $<
          // while still treating them as such.
          //
          names ns;
          for (const target_type* pt: t.prerequisite_targets[a])
            if (pt != nullptr)
              pt->as_name (ns);

          assign (var_pool.insert ("<")) = move (ns);
        }

        // Set the $~ special variable.
        //
        if (temp)
        {
          create_temp_dir ();
          assign (var_pool.insert<dir_path> ("~")) = temp_dir.path;
        }
      }

      void environment::
      create_temp_dir ()
      {
        // Create the temporary directory for this run regardless of the
        // dry-run mode, since some commands still can be executed (see run()
        // for details). This is also the reason why we are not using the
        // build2 filesystem API that considers the dry-run mode.
        //
        // Note that the directory auto-removal is active.
        //
        dir_path& td (temp_dir.path);

        assert (td.empty ()); // Must be called once.

        try
        {
          td = dir_path::temp_path ("buildscript");
        }
        catch (const system_error& e)
        {
          fail << "unable to obtain temporary directory for buildscript "
               << "execution" << e;
        }

        mkdir_status r;

        try
        {
          r = try_mkdir (td);
        }
        catch (const system_error& e)
        {
          fail << "unable to create temporary directory '" << td << "': "
               << e << endf;
        }

        // Note that the temporary directory can potentially stay after some
        // abnormally terminated script run. Clean it up and reuse if that's
        // the case.
        //
        if (r == mkdir_status::already_exists)
        try
        {
          butl::rmdir_r (td, false /* dir */);
        }
        catch (const system_error& e)
        {
          fail << "unable to cleanup temporary directory '" << td << "': "
               << e;
        }

        if (verb >= 3)
          text << "mkdir " << td;
      }

      void environment::
      set_variable (string&& nm,
                    names&& val,
                    const string& attrs,
                    const location& ll)
      {
        // Check if we are trying to modify any of the special variables.
        //
        if (parser::special_variable (nm))
          fail (ll) << "attempt to set '" << nm << "' special variable";

        // Set the variable value and attributes.
        //
        const variable& var (var_pool.insert (move (nm)));

        value& lhs (assign (var));

        // If there are no attributes specified then the variable assignment
        // is straightforward. Otherwise we will use the build2 parser helper
        // function.
        //
        if (attrs.empty ())
          lhs.assign (move (val), &var);
        else
        {
          // If there is an error in the attributes string, our diagnostics
          // will look like this:
          //
          // <attributes>:1:1 error: unknown value attribute x
          //   buildfile:10:1 info: while parsing attributes '[x]'
          //
          // Note that the attributes parsing error is the only reason for a
          // failure.
          //
          auto df = make_diag_frame (
            [attrs, &ll](const diag_record& dr)
            {
              dr << info (ll) << "while parsing attributes '" << attrs << "'";
            });

          parser p (context);
          p.apply_value_attributes (&var,
                                    lhs,
                                    value (move (val)),
                                    attrs,
                                    token_type::assign,
                                    path_name ("<attributes>"));
        }
      }

      lookup environment::
      lookup (const variable& var) const
      {
        auto p (vars.lookup (var));
        if (p.first != nullptr)
          return lookup_type (*p.first, p.second, vars);

        return lookup_in_buildfile (var.name);
      }

      lookup environment::
      lookup (const string& name) const
      {
        // Every variable that is ever set in a script has been added during
        // variable line execution or introduced with the set builtin. Which
        // means that if one is not found in the environment pool then it can
        // only possibly be set in the buildfile.
        //
        const variable* pvar (var_pool.find (name));
        return pvar != nullptr ? lookup (*pvar) : lookup_in_buildfile (name);
      }

      lookup environment::
      lookup_in_buildfile (const string& n) const
      {
        // Switch to the corresponding buildfile variable. Note that we don't
        // want to insert a new variable into the pool (we might be running
        // in parallel). Plus, if there is no such variable, then we cannot
        // possibly find any value.
        //
        const variable* pvar (context.var_pool.find (n));

        if (pvar == nullptr)
          return lookup_type ();

        return target[*pvar];
      }

      value& environment::
      append (const variable& var)
      {
        auto l (lookup (var));

        if (l.defined () && l.belongs (*this)) // Existing var.
          return vars.modify (l);

        value& r (assign (var)); // NULL.

        if (l.defined ())
          r = *l; // Copy value (and type) from the outer scope.

        return r;
      }
    }
  }
}