summaryrefslogtreecommitdiffstats
path: root/src/boost/tools/build/src/engine/startup.cpp
blob: 6e9414ba20092fb1adf87abb7cb5f480d4112d76 (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
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
/*
Copyright 2020 René Ferdinand Rivera Morell
Distributed under the Boost Software License, Version 1.0.
(See accompanying file LICENSE.txt or https://www.bfgroup.xyz/b2/LICENSE.txt)
*/

#include "startup.h"
#include "rules.h"
#include "frames.h"
#include "object.h"
#include "pathsys.h"
#include "cwd.h"
#include "filesys.h"
#include "output.h"
#include "variable.h"

#include <cstdlib>
#include <string>
#include <algorithm>

namespace
{
    void bind_builtin(
        char const *name_, LIST *(*f)(FRAME *, int flags),
        int flags, char const **args)
    {
        FUNCTION *func;
        OBJECT *name = object_new(name_);
        func = function_builtin(f, flags, args);
        new_rule_body(root_module(), name, func, 1);
        function_free(func);
        object_free(name);
    }
} // namespace

void b2::startup::load_builtins()
{
    {
        char const *args[] = {"dir", "?", 0};
        bind_builtin("boost-build", builtin_boost_build, 0, args);
    }
}

LIST *b2::startup::builtin_boost_build(FRAME *frame, int flags)
{
    b2::jam::list dir_arg{lol_get(frame->args, 0)};
    std::string dir;
    if (!dir_arg.empty()) dir = b2::jam::object(*dir_arg.begin());

    b2::jam::variable dot_bootstrap_file{".bootstrap-file"};
    if (dot_bootstrap_file)
    {
        err_printf(
            "Error: Illegal attempt to re-bootstrap the build system by invoking\n"
            "\n"
            "   'boost-build '%s' ;\n"
            "\n"
            "Please consult the documentation at "
            "'https://www.bfgroup.xyz/b2/'.\n\n",
           dir.c_str());
        return L0;
    }

    // # Add the given directory to the path so we can find the build system. If
    // # dir is empty, has no effect.
    b2::jam::variable dot_boost_build_file{".boost-build-file"};
    b2::jam::list dot_boost_build_file_val{static_cast<b2::jam::list>(dot_boost_build_file)};
    std::string boost_build_jam = b2::jam::object{*dot_boost_build_file_val.begin()};
    std::string boost_build_dir;
    if (b2::paths::is_rooted(dir))
        boost_build_dir = dir;
    else
        boost_build_dir = b2::paths::normalize(
            std::string{boost_build_jam}+"/../"+dir);
    b2::jam::list search_path{b2::jam::object{boost_build_dir}};
    b2::jam::variable BOOST_BUILD_PATH{"BOOST_BUILD_PATH"};
    search_path.append(BOOST_BUILD_PATH);

    // We set the global, and env, BOOST_BUILD_PATH so that the loading of the
    // build system finds the initial set of modules needed for starting it up.
    BOOST_BUILD_PATH = search_path;

    // The code that loads the rest of B2, in particular the site-config.jam
    // and user-config.jam configuration files uses os.environ, so we need to
    // update the value there.
    b2::jam::variable dot_ENVIRON__BOOST_BUILD_PATH{".ENVIRON", "BOOST_BUILD_PATH"};
    dot_ENVIRON__BOOST_BUILD_PATH = search_path;

    // # Try to find the build system bootstrap file 'bootstrap.jam'.
    std::string bootstrap_file;
    for (auto path: search_path)
    {
        std::string file = b2::jam::object{path};
        file = b2::paths::normalize(file+"/bootstrap.jam");
        if (b2::filesys::is_file(file))
        {
            bootstrap_file = file;
            break;
        }
    }

    // # There is no bootstrap.jam we can find, exit with an error.
    if (bootstrap_file.empty())
    {
        err_printf(
            "Unable to load B2: could not find build system.\n"
            "-----------------------------------------------\n"
            "%s attempted to load the build system by invoking\n"
            "\n"
            "   'boost-build %s ;'\n"
            "\n"
            "but we were unable to find 'bootstrap.jam' in the specified directory "
            "or in BOOST_BUILD_PATH:\n",
            boost_build_jam.c_str(), dir.c_str());
        for (auto path: search_path)
        {
            std::string file = b2::jam::object{path};
            err_printf("    %s\n", file.c_str());
        }
        err_puts(
            "Please consult the documentation at "
            "'https://www.bfgroup.xyz/b2/'.\n\n");
        return L0;
    }

    // Set the bootstrap=file var as it's used by the build system to refer to
    // the rest of the build system files.
    dot_bootstrap_file = b2::jam::list{b2::jam::object{bootstrap_file}};

    // Show where we found it, if asked.
    b2::jam::variable dot_OPTION__debug_configuration{".OPTION", "debug-configration"};
    if (dot_OPTION__debug_configuration)
    {
        out_printf("notice: loading B2 from %s\n", bootstrap_file.c_str());
    }

    // # Load the build system, now that we know where to start from.
    parse_file(b2::jam::object{bootstrap_file}, frame);

    return L0;
}

extern char const *saved_argv0;

bool b2::startup::bootstrap(FRAME *frame)
{
    b2::jam::list ARGV = b2::jam::variable{"ARGV"};
    b2::jam::object opt_debug_configuration{"--debug-configuration"};
    b2::jam::variable dot_OPTION__debug_configuration{".OPTION", "debug-configration"};
    for (auto arg: ARGV)
    {
        if (opt_debug_configuration == arg)
        {
            dot_OPTION__debug_configuration = b2::jam::list{b2::jam::object{"true"}};
            break;
        }
    }

    char *b2_exe_path_pchar = executable_path(saved_argv0);
    const std::string b2_exe_path{b2_exe_path_pchar};
    if (b2_exe_path_pchar)
    {
        std::free(b2_exe_path_pchar);
    }
    const std::string boost_build_jam{"boost-build.jam"};
    std::string b2_file_path;

    // Attempt to find the `boost-build.jam` boot file in work directory tree.
    if (b2_file_path.empty())
    {
        std::string work_dir{b2::paths::normalize(b2::cwd_str()) + "/"};
        while (b2_file_path.empty() && !work_dir.empty())
        {
            if (b2::filesys::is_file(work_dir + boost_build_jam))
                b2_file_path = work_dir + boost_build_jam;
            else if (work_dir.length() == 1 && work_dir[0] == '/')
                work_dir.clear();
            else
            {
                auto parent_pos = work_dir.rfind('/', work_dir.length() - 2);
                if (parent_pos != std::string::npos)
                    work_dir.erase(parent_pos + 1);
                else
                    work_dir.clear();
            }
        }
    }

    // Check relative to the executable for portable install location.
    if (b2_file_path.empty())
    {
        const std::string path{
            b2::paths::normalize(
                b2_exe_path + "/../.b2/kernel/" + boost_build_jam)};
        if (b2::filesys::is_file(path))
            b2_file_path = path;
    }

    // Check relative to the executable for portable install location.
    if (b2_file_path.empty())
    {
        const std::string path{
            b2::paths::normalize(
                b2_exe_path + "/../../share/boost-build/src/kernel/" + boost_build_jam)};
        if (b2::filesys::is_file(path))
            b2_file_path = path;
    }

    // Check the BOOST_BUILD_PATH (and BOOST_ROOT) paths.
    if (b2_file_path.empty())
    {
        b2::jam::list BOOST_BUILD_PATH = b2::jam::variable{"BOOST_BUILD_PATH"};
        // For back-compat with Boost we also search in the BOOST_ROOT location.
        BOOST_BUILD_PATH.append(b2::jam::list(b2::jam::variable{"BOOST_ROOT"}));
        for (auto search_path: BOOST_BUILD_PATH)
        {
            std::string path = b2::jam::object{search_path};
            path = b2::paths::normalize(path+"/"+boost_build_jam);
            if (b2::filesys::is_file(path))
            {
                b2_file_path = path;
                break;
            }
        }
    }

    // Indicate a load failure when we can't find the build file.
    if (b2_file_path.empty())
    {
        const char * not_found_error =
            "Unable to load B2: could not find 'boost-build.jam'\n"
            "---------------------------------------------------\n"
            "Attempted search from '%s' up to the root "
            "at '%s'\n"
            "Please consult the documentation at "
            "'https://www.bfgroup.xyz/b2/'.\n\n";
        err_printf(not_found_error, b2::cwd_str().c_str(), b2_exe_path.c_str());
        return false;
    }

    // Show where we found it, if asked.
    if (dot_OPTION__debug_configuration)
    {
        out_printf("notice: found boost-build.jam at %s\n", b2_file_path.c_str());
    }

    // Load the build system bootstrap file we found. But check we did that.
    b2::jam::variable dot_boost_build_file{".boost-build-file"};
    dot_boost_build_file = b2_file_path;
    b2::jam::object b2_file_path_sym{b2_file_path};
    parse_file(b2_file_path_sym, frame);
    b2::jam::list dot_dot_bootstrap_file_val = b2::jam::variable{".bootstrap-file"};
    if (dot_dot_bootstrap_file_val.empty())
    {
        err_printf(
            "Unable to load B2\n"
            "-----------------\n"
            "'%s' was found by searching from %s up to the root.\n"
            "\n"
            "However, it failed to call the 'boost-build' rule to indicate "
            "the location of the build system.\n"
            "\n"
            "Please consult the documentation at "
            "'https://www.bfgroup.xyz/b2/'.\n\n",
            b2_file_path.c_str(), b2::cwd_str().c_str());
        return false;
    }

    return true;
}