44namespace fs = std::filesystem;
52 #ifndef BOB_REBUILD_CMD
53 #define BOB_REBUILD_CMD { "g++", "-o", "_PROGRAM_", "_SOURCE_" }
59 #define GO_REBUILD_YOURSELF(argc, argv) bob::go_rebuild_yourself(argc, argv, __FILE__, bob::RebuildConfig(BOB_REBUILD_CMD))
90 const std::string_view
PROGRAM =
"_PROGRAM_";
93 const std::string_view
SOURCE =
"_SOURCE_";
106 Cmd cmd(
string source =
"bob.cpp",
string program =
"bob")
const;
133 #define PANIC(msg) bob::_panic(__FILE__, __LINE__, msg)
134 #define WARNING(msg) bob::_warning(__FILE__, __LINE__, msg)
137 [[noreturn]]
void _panic(path file,
int line,
string msg);
138 void _warning(path file,
int line,
string msg);
192 void checklist(
const vector<string> &items,
const vector<bool> &statuses);
250 int await(
string * output =
nullptr);
254 bool poll(
string * output =
nullptr);
288 vector<string> parts;
440 struct CmdRunnerSlot {
443 CmdRunnerSlot() : index{-1} {}
449 size_t process_count;
451 vector<CmdRunnerSlot> slots;
455 bool populate_slots();
459 void set_exit_code(CmdRunnerSlot &slot);
503 typedef std::vector<fs::path>
Paths;
506 typedef std::function<void(
const vector<path>&,
const vector<path>&)>
RecipeFunc;
599 void cli_panic(
const string &msg);
600 string parse_args(
int argc,
string argv[]);
622 int run(
int argc,
string argv[]);
655 vector<string> raw_args;
656 void set_defaults(
int argc,
char* argv[]);
659 Cli(
int argc,
char* argv[]);
661 Cli(
string title,
int argc,
char* argv[]);
673 const std::string
RESET =
"\033[0m";
679 const std::string
BLACK =
"\033[30m";
680 const std::string
RED =
"\033[31m";
681 const std::string
GREEN =
"\033[32m";
683 const std::string
BLUE =
"\033[34m";
685 const std::string
CYAN =
"\033[36m";
686 const std::string
WHITE =
"\033[37m";
731 const std::string
BOLD =
"\033[1m";
732 const std::string
DIM =
"\033[2m";
734 const std::string
BLINK =
"\033[5m";
752#ifdef BOB_IMPLEMENTATION
757 typedef std::vector<fs::path>
Paths;
759 int run_yourself(fs::path bin,
int argc,
char* argv[]) {
760 fs::path bin_path = fs::relative(bin);
761 auto run_cmd = Cmd({
"./" + bin_path.string()});
762 for (
int i = 1; i < argc; ++i) run_cmd.push(argv[i]);
763 std::cout << std::endl;
764 return run_cmd.run();
769 assert(!output.empty());
770 assert(!input.empty());
772 if (!fs::exists(output))
return true;
774 auto output_mtime = fs::last_write_time(output);
775 auto input_mtime = fs::last_write_time(input);
777 return output_mtime < input_mtime;
783 for (
const string &part :
parts) {
791 void go_rebuild_yourself(
int argc,
char* argv[], path source_file_name, RebuildConfig config) {
792 assert(argc > 0 &&
"No program provided via argv[0]");
794 path root = fs::current_path();
796 path binary_path = fs::relative(argv[0], root);
797 path source_path = fs::relative(source_file_name, root);
798 path header_path = __FILE__;
800 if (source_path.has_parent_path()) {
801 WARNING(
"Source file is not next to executable. This may cause issues.");
804 if (source_path.empty() || header_path.empty() || binary_path.empty()) {
805 PANIC(
"Failed to determine paths for source, header, or binary.\n\n"
806 "The bob executable must be compiled and run from the same directory as the source file.");
809 bool rebuild_needed =
false;
811 auto rebuild_yourself = Recipe(
813 {source_path, header_path},
814 [binary_path, source_path, config](
Paths,
Paths) {
815 config.cmd(source_path.string(), binary_path.string()).check();
819 if (rebuild_yourself.needs_rebuild()) {
820 rebuild_yourself.build();
826 bool find_root(path * root,
string marker_file) {
827 path &root_path = *root;
828 root_path = fs::current_path();
829 while (root_path != root_path.root_path()) {
830 if (fs::exists(root_path / marker_file)) {
833 root_path = root_path.parent_path();
844 if (!fs::exists(dir)) {
845 std::cout <<
"Creating directory: " + dir.string() << std::endl;
846 if (!fs::create_directories(dir)) {
847 std::cerr <<
"Failed to create directory: " << dir << std::endl;
851 return fs::absolute(dir);
854 void _warning(path file,
int line,
string msg) {
855 std::cerr <<
term::YELLOW <<
"[WARNING] " << file.string() <<
":" << line <<
": " << msg <<
term::RESET << std::endl;
859 void _panic(path file,
int line,
string msg) {
860 std::cerr <<
term::RED <<
"[ERROR] " << file.string() <<
":" << line <<
": " << msg <<
term::RESET << std::endl;
864 string I(path p) {
return "-I" + p.string(); }
868 string path_env = getenv(
"PATH");
869 if (path_env.empty()) PANIC(
"PATH environment variable is not set.");
873 size_t end = path_env.find(
':');
875 while (end != string::npos) {
876 string dir = path_env.substr(start, end - start);
877 path bin = fs::path(dir) / bin_name;
878 if (fs::exists(bin)) {
882 end = path_env.find(
':', start);
888 void checklist(
const vector<string> &items,
const vector<bool> &statuses) {
889 if (items.size() != statuses.size()) {
890 PANIC(
"Checklist items and statuses must have the same length.");
893 size_t max_length = 0;
894 for (
const auto &item : items) {
895 max_length = std::max(max_length, item.length());
898 std::cout << std::endl;
899 for (
size_t i = 0; i < items.size(); ++i) {
903 std::cout <<
" [" << (statuses[i] ?
"✓" :
"✗") <<
"] " << items[i];
904 for (
size_t j = items[i].length(); j < max_length; ++j) {
909 std::cout << std::endl;
913 bool all_installed =
true;
914 vector<path> installed_path(packages.size(),
"");
915 vector<bool> installed(packages.size(),
false);
917 for (
int i = 0; i < packages.size(); ++i) {
918 const string &pkg = packages[i];
920 installed[i] = !installed_path[i].empty();
921 all_installed &= installed[i];
924 if (all_installed)
return;
931 bool read_fd(
int fd,
string * target) {
932 bool got_data =
false;
935 int n = read(fd, buf,
sizeof(buf) - 1);
936 if (n <= 0)
return got_data;
938 target->append(buf, n);
944 CmdFuture::CmdFuture() : cpid(-1), done(false), exit_code(-1) {}
949 std::this_thread::sleep_for(std::chrono::milliseconds(20));
955 if (
done)
return true;
957 string new_output =
"";
958 bool got_data = read_fd(
output_fd, &new_output);
959 if (got_data && !
silent) {
960 std::cout << new_output;
963 if (output && got_data) {
964 *output += new_output;
968 pid_t result = waitpid(
cpid, &status, WNOHANG);
970 if (result == -1) PANIC(
"Error while polling child process: " +
string(strerror(errno)));
972 if (result == 0)
return false;
975 if (WIFEXITED(status))
exit_code = WEXITSTATUS(status);
976 else PANIC(
"Child process did not terminate normally.");
981 if (
cpid < 0)
return false;
983 std::cerr <<
"Failed to kill child process: " << strerror(errno) << std::endl;
994 Cmd::Cmd(vector<string> &&parts, path root) : parts(std::move(parts)), root(root) {}
997 parts.push_back(part);
1002 for (
const auto &part : parts) {
1003 this->parts.push_back(part);
1010 for (
const auto &part : parts) {
1011 if (!result.empty()) result +=
" ";
1015 path rel_root = fs::relative(
root, fs::current_path());
1016 result =
"[from '" + rel_root.string() +
"'] " + result;
1022 if (parts.empty() || parts[0].empty()) {
1023 PANIC(
"No command to run.");
1026 std::cout <<
"CMD: " <<
render() << std::endl;
1030 pid_t cpid = forkpty(&output_fd,
nullptr,
nullptr,
nullptr);
1032 PANIC(
"Could not forkpty: " + std::string(strerror(errno)));
1038 std::vector<char *> args;
1039 for (
auto &part : parts) {
1040 args.push_back(
const_cast<char *
>(part.c_str()));
1042 args.push_back(
nullptr);
1045 if (!
root.empty()) {
1046 if (chdir(
root.c_str()) < 0) {
1047 std::cerr <<
"Could not change directory to " <<
root <<
": " << strerror(errno) << std::endl;
1055 if (execvp(args[0], args.data()) < 0) {
1056 std::cerr <<
"Could not exec child process: " << strerror(errno) << std::endl;
1065 fcntl(output_fd, F_SETFL, O_NONBLOCK);
1069 future.output_fd = output_fd;
1070 future.done =
false;
1082 int exit_code =
run();
1083 if (exit_code != 0) PANIC(
"Command '" +
render() +
"' failed with exit status: " + std::to_string(exit_code));
1100 return fut.exit_code;
1103 bool CmdRunner::populate_slots() {
1104 bool did_work =
false;
1105 for (
size_t i = 0; i < process_count; ++i) {
1106 auto &slot = slots[i];
1108 if (slot.index >= 0) {
1109 bool fut_done =
cmds[slot.index].poll_future(slot.fut);
1110 if (!fut_done)
continue;
1115 set_exit_code(slot);
1117 if (!any_waiting())
continue;
1120 size_t index = cursor++;
1121 Cmd cmd =
cmds[index];
1123 slot.fut = cmd.run_async();
1131 void CmdRunner::await_slots() {
1132 bool all_done =
false;
1135 for (
auto &slot : slots) {
1136 if (slot.index < 0)
continue;
1137 bool done =
cmds[slot.index].poll_future(slot.fut);
1139 set_exit_code(slot);
1143 std::this_thread::sleep_for(std::chrono::milliseconds(20));
1147 void CmdRunner::set_exit_code(CmdRunnerSlot &slot) {
1148 if (slot.index < 0)
return;
1152 bool CmdRunner::any_waiting() {
1153 return cursor <
cmds.size();
1156 void CmdRunner::init_slots() {
1157 for (
auto &slot : slots) {
1158 slot.fut.done =
true;
1159 slot.fut.output_fd = -1;
1165 process_count(process_count),
1166 slots(vector<CmdRunnerSlot>(process_count))
1169 assert(process_count > 0 &&
"Process count must be greater than 0");
1173 process_count(sysconf(_SC_NPROCESSORS_ONLN)),
1174 slots(vector<CmdRunnerSlot>(process_count)),
1175 cmds(std::move(cmds))
1178 if (process_count == 0) process_count = 1;
1182 process_count(process_count),
1183 slots(vector<CmdRunnerSlot>(process_count)),
1184 cmds(std::move(cmds))
1187 if (process_count == 0) process_count = 1;
1191 process_count(sysconf(_SC_NPROCESSORS_ONLN)),
1192 slots(vector<CmdRunnerSlot>(process_count))
1195 if (process_count == 0) process_count = 1;
1204 cmds.push_back(cmd);
1208 for (
const auto &cmd :
cmds) {
1222 while (any_waiting()) {
1223 if (populate_slots())
continue;
1224 std::this_thread::sleep_for(std::chrono::milliseconds(20));
1236 if (exit_code != 0)
return true;
1242 for (
size_t i = 0; i <
cmds.size(); ++i) {
1245 if (!
cmds[i].output_str.empty()) {
1246 std::cerr <<
cmds[i].output_str;
1253 for (
auto &cmd :
cmds) {
1254 cmd.capture_output = capture;
1259 : inputs(inputs), outputs(outputs), func(func) {}
1262 for (
const auto &input :
inputs) {
1263 for (
const auto &output :
outputs) {
1274 vector<bool> exists(
inputs.size(),
true);
1275 bool any_missing =
false;
1276 for (
int i = 0; i <
inputs.size(); ++i) {
1277 if (!fs::exists(
inputs[i])) {
1283 std::cerr <<
"[ERROR] Recipe inputs are missing:" << std::endl;
1284 vector<string> input_strings;
1285 for (
auto &input :
inputs) input_strings.push_back(input.string());
1296 vector<bool> exists(
outputs.size(),
true);
1297 bool any_missing =
false;
1298 for (
int i = 0; i <
outputs.size(); ++i) {
1299 if (!fs::exists(
outputs[i])) {
1305 std::cerr <<
"[ERROR] Recipe did not produce expected outputs:" << std::endl;
1306 vector<string> output_strings;
1307 for (
auto &input :
outputs) output_strings.push_back(input.string());
1315 auto arg_len = [](
const CliFlag &arg) {
1317 if (arg.short_name !=
'\0') len += 2;
1318 if (!arg.long_name.empty()) len += 2 + arg.long_name.length();
1319 if (arg.short_name !=
'\0' && !arg.long_name.empty()) len += 2;
1323 size_t max_arg_length = 0;
1324 for (
const auto &arg : args) {
1325 max_arg_length = std::max(max_arg_length, arg_len(arg));
1328 auto arg_placeholder = [](
const CliFlag &arg) ->
string {
1330 return arg.long_name.empty() ?
"<value>" :
"<" + arg.long_name +
">";;
1333 size_t max_placeholder_length = 0;
1334 for (
const auto &arg : args) {
1335 max_placeholder_length = std::max(max_placeholder_length, arg_placeholder(arg).length());
1338 for (
const auto &arg : args) {
1343 if (arg.short_name !=
'\0') {
1344 std::cout <<
"-" << arg.short_name;
1345 if (!arg.long_name.empty()) std::cout <<
", ";
1349 if (!arg.long_name.empty()) {
1350 std::cout <<
"--" << arg.long_name;
1354 for (
size_t i = 0; i < max_arg_length - arg_len(arg); ++i) {
1359 string placeholder = arg_placeholder(arg);
1360 std::cout <<
" " << placeholder;
1361 for (
size_t i = 0; i < max_placeholder_length - placeholder.length(); ++i) {
1365 std::cout <<
" " << arg.description << std::endl;
1370 long_name(long_name), type(type), description(description) {};
1373 short_name(short_name), type(type), description(description) {};
1376 short_name(short_name), long_name(long_name), type(type), description(description) {};
1387 void CliCommand::cli_panic(
const string &msg) {
1388 std::cerr <<
"[ERROR] " << msg <<
"\n" << std::endl;
1393 string CliCommand::parse_args(
int argc,
string argv[]) {
1394 assert(argc >= 0 &&
"Argc must be non-negative");
1396 for (; argc > 0; --argc, ++argv) {
1398 string arg_name = *argv;
1400 if (arg_name.empty()) {
1401 cli_panic(
"Empty argument found in command line arguments.");
1404 if (arg_name[0] !=
'-') {
1405 if (
is_menu())
return arg_name;
1407 args.push_back(arg_name);
1414 for (CliFlag &arg :
flags) {
1416 bool short_name_matches = arg.short_name !=
'\0'
1417 && arg_name.length() == 2
1418 && arg_name[0] ==
'-'
1419 && arg_name[1] == arg.short_name;
1421 bool long_name_matches = !arg.long_name.empty()
1422 && arg_name.length() > 2
1423 && arg_name.substr(0, 2) ==
"--"
1424 && arg_name.substr(2) == arg.long_name;
1427 if (short_name_matches || long_name_matches) {
1434 if (argc <= 1) cli_panic(
"Expected value for argument: " + arg_name);
1444 if (!found) cli_panic(
"Unknown argument: " + arg_name);
1450 int CliCommand::call_func() {
1451 if (!
func) cli_panic(
"No function set for command: " +
name);
1456 : name(name), func(func), description(description) {}
1459 : CliCommand(name, nullptr, description) {
1460 func = [] (CliCommand &cmd) {
1461 std::cout <<
"No command provided.\n" << std::endl;
1463 return EXIT_FAILURE;
1476 size_t max_name_length = 0;
1478 max_name_length = std::max(max_name_length, cmd.name.length());
1482 std::cout <<
"\nAvailable commands:" << std::endl;
1484 std::cout <<
" " << cmd.name <<
" ";
1485 size_t padding = max_name_length - cmd.name.length();
1486 for (
size_t i = 0; i < padding; ++i) std::cout <<
" ";
1487 std::cout << cmd.description << std::endl;
1491 if (!
flags.empty()) {
1492 std::cout <<
"\nArguments:" << std::endl;
1498 for (CliFlag &arg :
flags) {
1499 if (arg.short_name ==
name)
return &arg;
1505 for (CliFlag &arg :
flags) {
1506 if (arg.long_name ==
name)
return &arg;
1514 if (!help_arg)
return;
1516 if (!help_arg->set)
return;
1523 string subcommand_name = parse_args(argc, argv);
1525 if (subcommand_name.empty())
return call_func();
1527 if (!
is_menu())
return call_func();
1529 vector<string> subcommand_path =
path;
1530 subcommand_path.push_back(subcommand_name);
1534 if (cmd.name == subcommand_name) {
1538 for (
int i =
flags.size() - 1; i >= 0; --i) {
1539 CliFlag &arg =
flags[i];
1542 if (cmd.find_short(arg.short_name))
continue;
1543 if (cmd.find_long(arg.long_name))
continue;
1548 cmd.path = subcommand_path;
1551 return cmd.run(argc-1, argv+1);
1555 cli_panic(
"Unknown command: " + subcommand_name);
1568 alias_cmd.name =
name;
1570 alias_cmd.description =
"Alias for command: " + this->
name;
1601 CliFlag * short_existing =
find_short(arg.short_name);
1602 CliFlag * long_existing =
find_long(arg.long_name);
1604 if (short_existing) PANIC(
"Short argument already exists: " +
string({arg.short_name}));
1605 if (long_existing) PANIC(
"Long argument already exists: " + arg.long_name);
1607 flags.push_back(arg);
1628 void Cli::set_defaults(
int argc,
char* argv[]) {
1629 assert(argc > 0 &&
"No program provided via argv[0]");
1634 raw_args.assign(argv + 1, argv + argc);
1640 Cli::Cli(
int argc,
char* argv[]): CliCommand(
"") {
1641 set_defaults(argc, argv);
1644 Cli::Cli(
string title,
int argc,
char* argv[]) : CliCommand(
"", title) {
1645 set_defaults(argc, argv);
1649 return run(raw_args.size(), raw_args.data());
1655 ioctl(STDOUT_FILENO, TIOCGWINSZ, &w);
1656 return {w.ws_col, w.ws_row};
Represents a CLI command.
Definition bob.hpp:579
int run(int argc, string argv[])
Runs the command with the given arguments.
CliCommand & add_command(const string &name)
Adds a subcommand without a description to this command. Returns a reference to the new command.
CliCommand & add_command(CliCommand command)
CliCommand & add_flag(const string &long_name, CliFlagType type, string description="")
Adds a long flag argument to this command.
string description
Description of the command, e.g. "Run tests".
Definition bob.hpp:591
string name
The name of the command.
Definition bob.hpp:587
CliCommand & add_flag(char short_name, CliFlagType type, string description="")
Adds a short flag argument to this command.
CliCommand alias(const string &name, const string &description="")
Creates a command which is an alias for this command.
CliCommandFunc func
The function that will be called when this command is executed.
Definition bob.hpp:589
void set_default_command(CliCommandFunc f)
Sets the function to be called this command is executed without a subcommand.
CliCommand(const string &name, const string &description="")
Create a CLI command which prints its subcommands and flags when invoked.
vector< string > args
Arguments that are not flags.
Definition bob.hpp:584
vector< CliCommand > commands
Subcommands of this command.
Definition bob.hpp:595
void usage() const
Prints the command's help message.
CliCommand & add_command(const string &name, CliCommandFunc func)
CliCommand & add_flag(char short_name, const string &long_name, CliFlagType type, string description="")
Adds a short and long flag argument to this command.
vector< string > path
Path to the command, e.g. {"./bob", "test", "run"}.
Definition bob.hpp:582
bool is_menu() const
Checks if this command is a menu command (has subcommands).
CliCommand(const string &name, CliCommandFunc func, const string &description="")
Create a CLI command with a command function to be executed when this command is invoked.
CliCommand & add_flag(const CliFlag &arg)
Adds a flag argument to this command.
void handle_help()
Prints the help message if the --help flag is set.
CliFlag * find_long(string name)
Finds a flag by its long name.
CliCommand & add_flag(const string &long_name, char short_name, CliFlagType type, string description="")
Adds a short and long flag argument to this command.
CliCommand & add_command(const string &name, string description, CliCommandFunc func)
Adds a subcommand with a command function to this command. Returns a reference to the new command.
void set_description(const string &desc)
Set the description of the command.
CliCommand & add_command(const string &name, string description)
Adds a subcommand to this command. Returns a reference to the new command.
CliFlag * find_short(char name)
Finds a flag by its short name.
vector< CliFlag > flags
The flags provided to this command.
Definition bob.hpp:593
A command line interface (CLI) that can be used to run commands and subcommands.
Definition bob.hpp:654
Cli(string title, int argc, char *argv[])
Create a new CLI interface with a title.
int serve()
Run the CLI and handle the commands.
Cli(int argc, char *argv[])
Create a new CLI interface.
A class for running many commands in parallel.
Definition bob.hpp:437
CmdRunner(vector< Cmd > cmds, size_t process_count)
vector< int > exit_codes
Exit codes for each command in cmds.
Definition bob.hpp:467
bool run()
Runs all commands in the runner concurrently.
CmdRunner(vector< Cmd > cmds)
CmdRunner(size_t process_count)
Create a CmdRunner with a specified number of processes.
void print_failed()
Prints the output of all commands that failed.
bool any_failed()
Returns true if any command in the runner failed (non-zero exit code).
bool all_succeded()
Returns true if all commands in the runner succeeded (exit code 0).
void push_many(vector< Cmd > cmds)
Push multiple commands to the runner.
void push(Cmd cmd)
Push a single command to the runner.
void clear()
Clears all commands in the runner for reuse.
void capture_output(bool capture=true)
Sets the capture_output flag for all commands in the runner.
size_t size()
Returns the number of commands in the runner.
vector< Cmd > cmds
The commands to be run by this runner.
Definition bob.hpp:465
Represents a command to be executed in the operating system shell.
Definition bob.hpp:287
CmdFuture run_async() const
bool capture_output
If true, the command's output will be captured.
Definition bob.hpp:291
int await_future(CmdFuture &fut)
bool poll_future(CmdFuture &fut)
path root
The root directory from which the command is executed.
Definition bob.hpp:300
Cmd(vector< string > &&parts, path root=".")
Cmd & push(const string &part)
bool silent
If true, the command will not print its output to stdout.
Definition bob.hpp:294
string output_str
The command's output captured during execution (stdout and stderr).
Definition bob.hpp:297
Cmd()=default
Creates an empty command.
Cmd & push_many(const vector< string > &parts)
A build recipe that defiles how to produce outputs from inputs.
Definition bob.hpp:509
Recipe(const Paths &outputs, const Paths &inputs, RecipeFunc func)
Constructs a recipe with the given outputs, inputs, and build function.
Paths outputs
The output files that are expected to be produced by the recipe.
Definition bob.hpp:514
void build() const
Builds the outputs from the inputs using the recipe function.
RecipeFunc func
The function that will be called to build the outputs from the inputs.
Definition bob.hpp:516
bool needs_rebuild() const
Use the modified time of the inputs and outputs to determine if the recipe needs to be rebuilt.
Paths inputs
The input files that are used to build the outputs.
Definition bob.hpp:512
const std::string BG_BLUE
Blue background.
Definition bob.hpp:709
const std::string BG_BRIGHT_GREEN
Bright green background.
Definition bob.hpp:720
const std::string BG_YELLOW
Yellow background.
Definition bob.hpp:708
const std::string BG_BRIGHT_YELLOW
Bright yellow background.
Definition bob.hpp:721
const std::string BRIGHT_YELLOW
Bright yellow text.
Definition bob.hpp:695
const std::string DIM
Dim style.
Definition bob.hpp:732
const std::string BG_BRIGHT_BLACK
Bright black background.
Definition bob.hpp:718
const std::string CYAN
Cyan text.
Definition bob.hpp:685
const std::string BG_RED
Red background.
Definition bob.hpp:706
const std::string BRIGHT_GREEN
Bright green text.
Definition bob.hpp:694
const std::string MAGENTA
Magenta text.
Definition bob.hpp:684
const std::string BLINK
Blink style.
Definition bob.hpp:734
const std::string RED
Red text.
Definition bob.hpp:680
const std::string RESET
Definition bob.hpp:673
TermSize size()
Returns the size of the terminal in characters.
const std::string BG_MAGENTA
Magenta background.
Definition bob.hpp:710
const std::string BG_BRIGHT_MAGENTA
Bright magenta background.
Definition bob.hpp:723
const std::string BLUE
Blue text.
Definition bob.hpp:683
const std::string BG_BRIGHT_WHITE
Definition bob.hpp:725
const std::string BOLD
Bold style.
Definition bob.hpp:731
const std::string GREEN
Green text.
Definition bob.hpp:681
const std::string BG_WHITE
Definition bob.hpp:712
const std::string BG_CYAN
Cyan background.
Definition bob.hpp:711
const std::string BRIGHT_BLACK
Bright black text.
Definition bob.hpp:692
const std::string BRIGHT_RED
Bright red text.
Definition bob.hpp:693
const std::string INVERT
Invert style.
Definition bob.hpp:735
const std::string BLACK
Black text.
Definition bob.hpp:679
const std::string BG_BLACK
Black background.
Definition bob.hpp:705
const std::string BRIGHT_WHITE
Definition bob.hpp:699
const std::string BG_BRIGHT_CYAN
Bright cyan background.
Definition bob.hpp:724
const std::string WHITE
Definition bob.hpp:686
const std::string BRIGHT_BLUE
Bright blue text.
Definition bob.hpp:696
const std::string BG_GREEN
Green background.
Definition bob.hpp:707
const std::string YELLOW
Yellow text.
Definition bob.hpp:682
const std::string HIDDEN
Definition bob.hpp:736
const std::string UNDERLINE
Underline style.
Definition bob.hpp:733
const std::string BRIGHT_CYAN
Bright cyan text.
Definition bob.hpp:698
const std::string BG_BRIGHT_RED
Bright red background.
Definition bob.hpp:719
const std::string BRIGHT_MAGENTA
Bright magenta text.
Definition bob.hpp:697
const std::string BG_BRIGHT_BLUE
Bright blue background.
Definition bob.hpp:722
Contains the functionality of the Bob build system.
Definition bob.hpp:47
std::function< int(CliCommand &)> CliCommandFunc
A function that can be used to handle a command in a CLI.
Definition bob.hpp:574
std::function< void(const vector< path > &, const vector< path > &)> RecipeFunc
A function that can be used in a Recipe to build outputs from inputs.
Definition bob.hpp:506
CliFlagType
Types of command line flags.
Definition bob.hpp:528
@ Value
Option with a value, e.g. -o file.txt or --output file.txt.
@ Bool
Boolean flag, e.g. -v or --verbose.
void checklist(const vector< string > &items, const vector< bool > &statuses)
void ensure_installed(vector< string > packages)
int run_yourself(fs::path bin, int argc, char *argv[])
void go_rebuild_yourself(int argc, char *argv[], path source_file_name)
std::vector< CliFlag > CliFlags
A list of command line flags.
Definition bob.hpp:568
path search_path(const string &bin_name)
bool find_root(path *root, string marker_file)
bool file_needs_rebuild(path input, path output)
bool git_root(path *root)
void print_cli_args(const CliFlags &args)
Prints the command line flags and their descriptions. Used for help output.
std::vector< fs::path > Paths
A list of file paths.
Definition bob.hpp:503
Represents a command flag argument.
Definition bob.hpp:536
string description
Description of the flag, e.g. "Enable verbose output".
Definition bob.hpp:543
bool is_flag() const
Checks if the flag is a boolean flag (no value).
CliFlag(char short_name, const string &long_name, CliFlagType type, string description="")
Create a CLI flag.
CliFlag(const string &long_name, CliFlagType type, string description="")
Create a CLI flag.
string long_name
The long name of the flag, e.g. --verbose.
Definition bob.hpp:541
bool set
Whether the flag is set or not.
Definition bob.hpp:545
CliFlag(char short_name, CliFlagType type, string description="")
Create a CLI flag.
CliFlagType type
The type of the flag..
Definition bob.hpp:551
char short_name
The short name of the flag, e.g. -v.
Definition bob.hpp:539
string value
Definition bob.hpp:549
bool is_option() const
Checks if the flag is an option (has a value).
Represents a command that being executed in the background.
Definition bob.hpp:235
int output_fd
File descriptor for reading the command's output (stdout and stderr).
Definition bob.hpp:243
pid_t cpid
Process ID of the child process.
Definition bob.hpp:237
bool done
Indicates whether the command has completed.
Definition bob.hpp:239
int await(string *output=nullptr)
Blocks until the command completes and returns the exit code.
bool kill()
Kills the command if it is still running.
bool silent
If true, the command's output will not be printed to stdout while it is running.
Definition bob.hpp:245
int exit_code
Exit code of the command. Only valid after the command has completed.
Definition bob.hpp:241
bool poll(string *output=nullptr)
Configuration for rebuilding the current executable.
Definition bob.hpp:88
RebuildConfig(vector< string > &&parts)
Create a rebuild configuration with the given parts.
Definition bob.hpp:102
const std::string_view SOURCE
Special string to represent the source file name in the command parts.
Definition bob.hpp:93
const std::string_view PROGRAM
Special string to represent the program name in the command parts.
Definition bob.hpp:90
RebuildConfig()=default
Create an empty rebuild configuration.
vector< string > parts
A vector of strings representing the command parts before substitution.
Definition bob.hpp:96
Cmd cmd(string source="bob.cpp", string program="bob") const
Terminal size in characters.
Definition bob.hpp:740
size_t h
Terminal Height.
Definition bob.hpp:742
size_t w
Terminal Width.
Definition bob.hpp:741