aboutsummaryrefslogtreecommitdiff
path: root/build/operation
blob: 743458abfd8273876077b1b2daa1e47c0c5ffddf (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
// file      : build/operation -*- C++ -*-
// copyright : Copyright (c) 2014-2015 Code Synthesis Tools CC
// license   : MIT; see accompanying LICENSE file

#ifndef BUILD_OPERATION
#define BUILD_OPERATION

#include <string>
#include <cstdint>
#include <iosfwd>

#include <build/utility> // string_table

namespace build
{
  // While we are using uint8_t for the meta/operation ids, we assume
  // that each is limited to 4 bits (max 128 entries) so that we can
  // store the combined action id in uint8_t as well. This makes our
  // life easier when it comes to defining switch labels for action
  // ids (no need to mess with endian-ness).
  //
  // Note that 0 is not a valid meta/operation/action id.
  //
  using meta_operation_id = std::uint8_t;
  using operation_id = std::uint8_t;
  using action_id = std::uint8_t;

  struct action
  {
    action (meta_operation_id m, operation_id o): id ((m << 4) | o) {}

    meta_operation_id
    meta_operation () const {return id >> 4;}

    operation_id
    operation () const {return id & 0xF;}

    // Implicit conversion operator to action_id for the switch()
    // statement, etc.
    //
    operator action_id () const {return id;}

    action_id id;
  };

  std::ostream&
  operator<< (std::ostream&, action);

  // Id constants for build-in operations.
  //
  const meta_operation_id perform_id   = 1;
  const meta_operation_id configure_id = 2;
  const meta_operation_id disfigure_id = 3;

  const operation_id update_id = 1;
  const operation_id clean_id  = 2;

  const action_id perform_update_id = (perform_id << 4) | update_id;
  const action_id perform_clean_id  = (perform_id << 4) | clean_id;

  // Recipe execution mode.
  //
  // When a target is a prerequisite of another target, its recipe can be
  // executed before the dependent's recipe (the normal case) or after.
  // We will call these "front" and "back" execution modes, respectively
  // (think "the prerequisite is 'front-running' the dependent").
  //
  // There could also be several dependent targets and the prerequisite's
  // recipe can be execute as part of the first dependent (the normal
  // case) or last (or for all/some of them; see the recipe execution
  // protocol in <target>). We will call these "first" and "last"
  // execution modes, respectively.
  //
  // Now you may be having a hard time imagining where a mode other than
  // the normal one (first/front) could be useful. An the answer is,
  // compensating or inverse operations such as clean, uninstall, etc.
  // If we use the last/back mode for, say, clean, then we will remove
  // targets in the order inverse to the way they were updated. While
  // this sounds like an elegant idea, are there any practical benefits
  // of doing it this way. As it turns out there is (at least) one: when
  // we are removing a directory (see fsdir{}), we want to do it after
  // all the targets that depend on it (such as files, sub-directories)
  // were removed. If we do it before, then the directory won't be empty
  // yet.
  //
  // It appears that this execution mode is dictated by the essence of
  // the operation. Constructive operations (those that "do") seem to
  // naturally use the first/front mode. That is, we need to "do" the
  // prerequisite first before we can "do" the dependent. While the
  // destructive ones (those that "undo") seem to need last/back. That
  // is, we need to "undo" all the dependents before we can "undo" the
  // prerequisite (say, we need to remove all the files before we can
  // remove their directory).
  //
  // If you noticed the parallel with the way C++ construction and
  // destruction works for base/derived object then you earned a gold
  // star!
  //
  // Note that the front/back mode is realized in the dependen's recipe
  // (which is another indication that it is a property of the operation).
  //
  enum class execution_mode {first, last};

  // Meta/operation tables.
  //
  struct meta_operation_info
  {
    const std::string name;

    const std::string&
    key () const {return name;} // string_table interface.
  };

  struct operation_info
  {
    const std::string name;
    const execution_mode mode;

    const std::string&
    key () const {return name;} // string_table interface.
  };

  using meta_operation_table = string_table<meta_operation_id,
                                            meta_operation_info>;
  using operation_table = string_table<operation_id, operation_info>;

  extern meta_operation_table meta_operations;
  extern operation_table operations;
}

#endif // BUILD_OPERATION