/* Copyright 2020-2022 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 "bindjam.h" #include "compile.h" #include "cwd.h" #include "filesys.h" #include "frames.h" #include "mod_args.h" #include "mod_jam_modules.h" #include "modules.h" #include "object.h" #include "output.h" #include "pathsys.h" #include "rules.h" #include "value.h" #include "variable.h" #include #include #include #include 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) { // Do nothing, but keep the rule, for backwards compatability. // But do record the path passed in as a fallback to the loading. b2::jam::variable(".boost-build-dir") = b2::list_ref(lol_get(frame->args, 0)); return L0; } extern char const * saved_argv0; void bootstrap_dirscan( void * dirs, OBJECT * path, int found, timestamp const * const) { if (file_is_file(path) == 1) return; _pathname p(object_str(path)); std::string basename = p.base() + p.suffix(); if (basename == "." || basename == "..") return; static_cast *>(dirs)->push_back(object_str(path)); } bool b2::startup::bootstrap(FRAME * frame) { // We use the executable path as a root for searches. 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); } /** * Search for a boost-build.jam file to load in various locations. This is * solely for backwards compatiblity. The boost-build.jam file found is * loaded, but the `boost-build` invocation in it is ignored. */ 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. // I.e. in current directory and ancestor directories. 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. // ~b2(.exe)/.b2/kernel/boost-build.jam 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. // /usr/share/b2/src/kernel/boost-build.jam if (b2_file_path.empty()) { const std::string path { b2::paths::normalize( b2_exe_path + "/../../share/b2/src/kernel/" + boost_build_jam) }; if (b2::filesys::is_file(path)) b2_file_path = path; } // Check the BOOST_BUILD_PATH (and BOOST_ROOT) paths. // $(BOOST_BUILD_PATH)/boost-build.jam // $(BOOST_ROOT)/boost-build.jam if (b2_file_path.empty()) { b2::list_ref 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.push_back("BOOST_ROOT"); for (auto search_path : BOOST_BUILD_PATH) { std::string path = b2::value_ref { search_path }; path = b2::paths::normalize(path + "/" + boost_build_jam); if (b2::filesys::is_file(path)) { b2_file_path = path; break; } } } // Show where we found it, if asked. if (!b2_file_path.empty() && !b2::args::get_arg("debug-configuration").empty()) { out_printf("notice: found boost-build.jam at %s\n", b2_file_path.c_str()); } // Load the boost-build file if we find it for backwards compatability. We // ignore the `boost-build ..` invocation it does. Preferring to find our // own bootstrap engine file. if (!b2_file_path.empty()) { b2::jam::variable dot_boost_build_file { ".boost-build-file" }; dot_boost_build_file = b2_file_path; b2::value_ref b2_file_path_sym { b2_file_path }; parse_file(b2_file_path_sym, frame); } /** * Search for a build-system.jam file to load in various locations. The * `build-system.jam` is the starting point of loading the build system. */ const std::string buildsystem_jam { "build-system.jam" }; std::string buildsystem_file; std::string buildsystem_files_searched; // Check various locations relative to executable. if (buildsystem_file.empty()) { const char * dirs[] = { // Check relative to the executable for portable // install location. ".b2/", // Check relative to the exec for system install location. "../share/b2/", // Check relative to the exec for legacy install location. "../share/b2/src/", // Check development location relative to executable in src/engine. "../", // Check development location relative to executable at root. "src/", // Check for special Boost location. "tools/build/src/" }; for (auto dir : dirs) { const std::string path { b2::paths::normalize( b2_exe_path + "/../" + dir + buildsystem_jam) }; if (b2::filesys::is_file(path)) { buildsystem_file = path; break; } buildsystem_files_searched += " " + path + "\n"; } } // Check the development tree for the file to support not-installed // b2 executable locations. I.e. when building b2 with b2 for engine // development. if (buildsystem_file.empty()) { std::string work_dir(b2::paths::normalize(b2_exe_path + "/..") + "/"); while (buildsystem_file.empty() && !work_dir.empty()) { buildsystem_files_searched += " " + work_dir + "src/" + buildsystem_jam + "\n"; if (b2::filesys::is_file(work_dir + "src/" + buildsystem_jam)) buildsystem_file = work_dir + "src/" + buildsystem_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(); } } } // Last resort, search in the directory referenced by the boost-build rule. if (buildsystem_file.empty()) { b2::list_cref dot_boost_build_dir = b2::jam::variable(".boost-build-dir"); if (!dot_boost_build_dir.empty()) { std::string dir = b2::value_ref(*dot_boost_build_dir.begin()); if (!b2_file_path.empty() && !b2::paths::is_rooted(dir)) dir = b2_file_path + "/../" + dir; const std::string path = b2::paths::normalize(dir + "/" + buildsystem_jam); buildsystem_files_searched += " " + path + "\n"; if (b2::filesys::is_file(path)) buildsystem_file = path; } } // Failed to find the build files to load. if (buildsystem_file.empty()) { err_printf( "Unable to load B2\n" "-----------------\n" "No '%s' was found by searching for:\n" "%s\n" "Please consult the documentation at " "'https://www.bfgroup.xyz/b2/'.\n\n", buildsystem_file.c_str(), buildsystem_files_searched.c_str()); return false; } // Show where we found the bootstrap, if asked. if (!buildsystem_file.empty() && !b2::args::get_arg("debug-configuration").empty()) { out_printf("notice: loading B2 from %s\n", buildsystem_file.c_str()); } // // Load the build system bootstrap file we found. // parse_file(b2::value_ref { buildsystem_file }, frame); // Add any subdirs to the build-system.jam to search for the jam files. std::string buildsystem_dir = b2::paths::normalize(buildsystem_file + "/.."); std::vector buildsystem_subdirs; file_dirscan( value_ref(buildsystem_dir), &bootstrap_dirscan, &buildsystem_subdirs); b2::jam::variable boost_build_path_v("BOOST_BUILD_PATH"); for (auto subdir : buildsystem_subdirs) boost_build_path_v += subdir; boost_build_path_v += buildsystem_dir; b2::jam::variable(".ENVIRON", "BOOST_BUILD_PATH") = boost_build_path_v; jam::jam_context context(frame); // The modules module is required globally for everyone for the `ìmport` // rule. list_ref modules_imports("modules"); jam::modules::import( list_cref(*modules_imports), list_cref(), list_cref(), context.ref()); // Process option plugins first to allow them to prevent loading the rest of // the build system. list_ref option_imports("option"); jam::modules::import( list_cref(*option_imports), list_cref(), list_cref(), context.ref()); list_ref dont_build(jam::run_rule(context.frame, "option.process")); // Should we skip building, i.e. loading the build system, according to the // options processed? if (!dont_build.empty()) return true; // Check `--build-system` option. list_ref build_system_imports; for (auto arg : *jam::variable("ARGV")) { std::string opt = arg->str(); if (opt.substr(0, 15) == "--build-system=") build_system_imports.push_back(opt.substr(15)); } // Import the build-system module to start off the regular build process. if (build_system_imports.empty()) build_system_imports.push_back("build-system"); jam::modules::import(list_cref(*build_system_imports), list_cref(), list_cref(), context.ref()); return true; }