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

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

#include <libbuild2/install/utility.hxx>

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

      // $install.resolve(<dir>[, <rel_base>])
      //
      // @@ TODO: add overload to call resolve_file().
      //
      // Resolve potentially relative install.* value to an absolute and
      // normalized directory based on (other) install.* values visible from
      // the calling scope.
      //
      // If rel_base is specified and is not empty, then make the resulting
      // directory relative to it. If rel_base itself is relative, first
      // resolve it to an absolute and normalized directory based on install.*
      // values. Note that this argument is mandatory if this function is
      // called during relocatable installation (install.relocatable is true).
      // While you can pass empty directory to suppress this functionality,
      // make sure this does not render the result non-relocatable.
      //
      // As an example, consider an executable that supports loading plugins
      // and requires the plugin installation directory to be embedded into
      // the executable during the build. The common way to support
      // relocatable installations for such cases is to embed a path relative
      // to the executable and complete it at runtime. If you would like to
      // always use the relative path, regardless of whether the installation
      // is relocatable of not, then you can simply always pass rel_base, for
      // example:
      //
      // plugin_dir = $install.resolve($install.lib, $install.bin)
      //
      // Alternatively, if you would like to continue using absolute paths for
      // non-relocatable installations, then you can use something like this:
      //
      // plugin_dir = $install.resolve($install.lib, ($install.relocatable ? $install.bin : [dir_path] ))
      //
      // Finally, if you are unable to support relocatable installations, the
      // correct way to handle this is NOT to always pass an empty path for
      // rel_base but rather assert in root.build that your project does not
      // support relocatable installations, for example:
      //
      // assert (!$install.relocatable) 'relocatable installation not supported'
      //
      // Note that this function is not pure.
      //
      f.insert (".resolve", false) += [] (const scope* s,
                                          dir_path dir,
                                          optional<dir_path> rel_base)
      {
        if (s == nullptr)
          fail << "install.resolve() called out of scope" << endf;

        if (!rel_base)
        {
          const scope& rs (*s->root_scope ());

          if (cast_false<bool> (rs["install.relocatable"]))
          {
            fail << "relocatable installation requires relative base "
                 << "directory" <<
              info << "pass empty relative base directory if this call does "
                 << "not affect installation relocatability" <<
              info << "or add `assert (!$install.relocatable) 'relocatable "
                 << "installation not supported'` before the call";
          }
        }

        return resolve_dir (*s,
                            move (dir),
                            rel_base ? move (*rel_base) : dir_path ());
      };

      // @@ TODO: add $install.chroot().

      // $install.filter(<path>[, <type>])
      //
      // Apply filters from config.install.filter and return true if the
      // specified filesystem entry should be installed/uninstalled. Note that
      // the entry is specified as an absolute and normalized installation
      // path (so not $path($>) but $install.resolve($>)).
      //
      // The type argument can be one of `regular`, `directory`, or `symlink`.
      // If unspecified, either `directory` or `regular` is assumed, based on
      // whether path is syntactially a directory (ends with a directory
      // separator).
      //
      // Note that this function is not pure.
      //
      f.insert (".filter", false) += [] (const scope* s,
                                         path p,
                                         optional<names> ot)
      {
        if (s == nullptr)
          fail << "install.filter() called out of scope" << endf;

        entry_type t;
        if (ot)
        {
          string v (convert<string> (move (*ot)));

          if      (v == "regular")   t = entry_type::regular;
          else if (v == "directory") t = entry_type::directory;
          else if (v == "symlink")   t = entry_type::symlink;
          else throw invalid_argument ("unknown type '" + v + '\'');
        }
        else
          t = p.to_directory () ? entry_type::directory : entry_type::regular;

        // Split into directory and leaf.
        //
        dir_path d;
        if (t == entry_type::directory)
        {
          d = path_cast<dir_path> (move (p));
          p = path (); // No leaf.
        }
        else
        {
          d = p.directory ();
          p.make_leaf ();
        }

        return filter_entry (*s->root_scope (), d, p, t);
      };
    }
  }
}