aboutsummaryrefslogtreecommitdiff
path: root/bbot/build-config.cxx
blob: 226ead383b85b3a9f9665ec450c1d088d433837b (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
// file      : bbot/build-config.cxx -*- C++ -*-
// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd
// license   : MIT; see accompanying LICENSE file

#include <bbot/build-config>

#include <string>
#include <cstddef>   // size_t
#include <utility>   // move()
#include <stdexcept> // invalid_argument

#include <butl/path>
#include <butl/fdstream>
#include <butl/tab-parser>

using namespace std;
using namespace butl;

namespace bbot
{
  LIBBBOT_EXPORT build_configs
  parse_buildtab (istream& is, const string& name)
  {
    build_configs r;
    tab_parser parser (is, name);

    tab_fields tl;
    while (!(tl = parser.next ()).empty ())
    {
      size_t n (tl.size ()); // Fields count.
      size_t i (0);          // The field currently being processed.

      // Throw tab_parsing for the field currently being processed. If i == n
      // then we refer to the end-of-line column (presumably reporting a missed
      // field).
      //
      auto bad_line = [&name, &tl, &i, n] (const string& d, size_t offset = 0)
      {
        // Offset beyond the end-of-line is meaningless.
        //
        assert (i < n || (i == n && offset == 0));

        throw tab_parsing (name,
                           tl.line,
                           i == n
                           ? tl.end_column
                           : tl[i].column + offset,
                           d);
      };

      build_config config;
      config.machine_pattern = move (tl[i++].value);

      // Configuration name field is a required one.
      //
      if (i == n)
        bad_line ("no configuration name found");

      config.name = move (tl[i].value);

      // Make sure the name is unique.
      //
      for (const auto& c: r)
        if (c.name == config.name)
          bad_line ("duplicate configuration name");

      // If there is no target nor configuration variables then save the
      // configuration and proceed with the next line.
      //
      if (++i == n)
      {
        r.emplace_back (move (config));
        continue;
      }

      // If the third field doesn't contain '=' character, then we will treat
      // it as a target.
      //
      if (tl[i].value.find ('=') == string::npos)
      {
        try
        {
          config.target = target_triplet (tl[i].value);
        }
        catch (const invalid_argument& e)
        {
          bad_line (e.what ());
        }

        ++i;
      }

      try
      {
        for (; i < n; ++i)
          config.vars.emplace_back (variable (move (tl[i].value)));
      }
      catch (const invalid_variable& e)
      {
        bad_line (e.what (), e.pos); // Note that tl[i].value is moved from,
                                     // but happily we don't use it
      }

      // Save the configuration.
      //
      r.emplace_back (move (config));
    }

    return r;
  }

  build_configs
  parse_buildtab (const path& p)
  {
    ifdstream ifs (p);
    build_configs r (parse_buildtab (ifs, p.string ()));

    ifs.close (); // Throws on failure.
    return r;
  }
}