aboutsummaryrefslogtreecommitdiff
path: root/bpkg/package-configuration.cxx
blob: fb742e8d779860fe7e985e17d46b5aa0068ab637 (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
// file      : bpkg/package-configuration.cxx -*- C++ -*-
// license   : MIT; see accompanying LICENSE file

#include <bpkg/package-configuration.hxx>

#include <bpkg/package-skeleton.hxx>

namespace bpkg
{
  using build2::config::variable_origin;

  bool
  up_negotiate_configuration (
    package_configurations& cfgs,
    package_skeleton& dept,
    pair<size_t, size_t> pos,
    const small_vector<reference_wrapper<package_skeleton>, 1>& depcs)
  {
    dependent_config_variable_values old_cfgs;

    // Step 1: save a snapshot of the old configuration while unsetting values
    // that have this dependent as the originator and reloading the defaults.
    //
    // While at it, also collect the configurations to pass to dependent's
    // evaluate_*() calls.
    //
    package_skeleton::dependency_configurations depc_cfgs;
    depc_cfgs.reserve (depcs.size ());

    for (package_skeleton& depc: depcs)
    {
      package_configuration& cfg (cfgs[depc.key]);

      for (config_variable_value& v: cfg)
      {
        if (v.origin == variable_origin::buildfile)
        {
          if (*v.dependent == dept.key)
          {
            old_cfgs.push_back (
              dependent_config_variable_value {
                v.name, move (v.value), move (*v.dependent)});

            v.origin = variable_origin::undefined;
            v.dependent = nullopt;
          }
          else
            old_cfgs.push_back (
              dependent_config_variable_value {v.name, v.value, *v.dependent});
        }
      }

      depc.reload_defaults (cfg);

      depc_cfgs.push_back (cfg);
    }

    // Step 2: execute the prefer/accept or requires clauses.
    //
    pos.first--; pos.second--; // Convert to 0-base.

    const dependency_alternative& da (
      dept.available.get ().dependencies[pos.first][pos.second]);

    if (!(da.require
          ? dept.evaluate_require (depc_cfgs, *da.require, pos.first)
          : dept.evaluate_prefer_accept (depc_cfgs,
                                         *da.prefer, *da.accept, pos.first)))
    {
      fail << "unable to negotiate acceptable configuration"; // @@ TODO
    }

    // Check if anything changed by comparing to entries in old_cfgs.
    //
    {
      optional<size_t> n (0); // Number of unchanged.

      for (package_skeleton& depc: depcs)
      {
        package_configuration& cfg (cfgs[depc.key]);

        for (config_variable_value& v: cfg)
        {
          if (v.origin == variable_origin::buildfile)
          {
            auto i (find_if (old_cfgs.begin (), old_cfgs.end (),
                             [&v] (const dependent_config_variable_value& o)
                             {
                               return v.name == o.name;
                             }));

            if (i == old_cfgs.end () ||
                i->value != v.value  ||
                i->dependent != *v.dependent)
            {
              n = nullopt;
              break;
            }

            ++*n;
          }
        }

        if (!n)
          break;
      }

      // If we haven't seen any changed and we've seen the same number, then
      // nothing has changed.
      //
      if (n && *n == old_cfgs.size ())
        return false;
    }


    // @@ TODO: look for cycles in change history.
    // @@ TODO: save in change history.
    //
    /*
    dependent_config_variable_values new_cfgs; // @@ TODO.

    old_cfgs.sort ();
    new_cfgs.sort ();
    */

    return true;
  }
}