// -*- c++ -*- // Distributed under the BSD 2-Clause License. // See accompanying file LICENSE for details. #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "libctor.h" #include "settings.h" #include "configure.h" #include "rebuild.h" #include "tasks.h" #include "build.h" #include "unittest.h" int main(int argc, char* argv[]) { if(argc > 1 && std::string(argv[1]) == "configure") { return configure(argc, argv); } Settings settings{}; settings.builddir = getConfiguration(cfg::builddir, "build"); settings.parallel_processes = std::max(1u, std::thread::hardware_concurrency() * 2 - 1); settings.verbose = 0; bool write_compilation_database{false}; std::string compilation_database; bool print_configure_cmd{false}; bool print_configure_db{false}; std::vector add_files; std::vector remove_files; bool no_default_build{false}; // set to true to prevent empty arg list building 'all' target. bool list_files{false}; bool list_targets{false}; bool no_relaunch{false}; // true means no re-launch after rebuild. bool run_unittests{false}; dg::Options opt; int key{128}; opt.add("jobs", required_argument, 'j', "Number of parallel jobs. (default: cpucount * 2 - 1)", [&]() { try { settings.parallel_processes = std::stoi(optarg); } catch(...) { std::cerr << "Not a number\n"; return 1; } return 0; }); opt.add("build-dir", required_argument, 'b', "Overload output directory for build files (default: '" + settings.builddir + "').", [&]() { settings.builddir = optarg; return 0; }); opt.add("verbose", no_argument, 'v', "Be verbose. Add multiple times for more verbosity.", [&]() { settings.verbose++; return 0; }); opt.add("add", required_argument, 'a', "Add specified file to the build configurations.", [&]() { no_relaunch = true; add_files.push_back(optarg); return 0; }); opt.add("remove", required_argument, 'r', "Remove specified file from the build configurations.", [&]() { no_relaunch = true; remove_files.push_back(optarg); return 0; }); opt.add("list-files", no_argument, 'L', "List files in the build configurations.", [&]() { no_relaunch = true; list_files = true; return 0; }); opt.add("list-targets", no_argument, 'l', "List targets.", [&]() { no_relaunch = true; list_targets = true; return 0; }); opt.add("configure-cmd", no_argument, key++, "Print commandline for last configure.", [&]() { no_relaunch = true; print_configure_cmd = true; return 0; }); opt.add("configure-db", no_argument, key++, "Print entire configure parameter database.", [&]() { no_relaunch = true; print_configure_db = true; return 0; }); opt.add("database", required_argument, 'd', "Write compilation database json file.", [&]() { no_relaunch = true; write_compilation_database = true; compilation_database = optarg; return 0; }); opt.add("help", no_argument, 'h', "Print this help text.", [&]() { std::cout << "Usage: " << argv[0] << " [options] [target] ...\n"; std::cout << R"_( where target can be either: configure - run configuration step (cannot be used with other targets). clean - clean all generated files. all - build all targets (default) or the name of a target which will be built along with its dependencies. Use '-l' to see a list of possible target names. Options: )_"; opt.help(); exit(0); return 0; }); opt.process(argc, argv); auto verbose_env = std::getenv("V"); if(verbose_env) { settings.verbose = std::atoi(verbose_env); } if(list_files) { no_default_build = true; std::set files; for(std::size_t i = 0; i < numConfigFiles; ++i) { files.insert(configFiles[i].file); } for(const auto& file : files) { std::cout << file << "\n"; } } if(!add_files.empty() || !remove_files.empty()) { no_default_build = true; for(const auto& add_file : add_files) { reg(add_file.data(), [](){ return std::vector{};}); } for(const auto& remove_file : remove_files) { unreg(remove_file.data()); } // Force rebuild if files were added recompileCheck(settings, 1, argv, true, no_relaunch == false); } recompileCheck(settings, argc, argv); std::filesystem::path builddir(settings.builddir); std::filesystem::create_directories(builddir); auto all_tasks = getTasks(settings); if(list_targets) { no_default_build = true; auto& targets = getTargets(settings); for(const auto& target : targets) { std::cout << target.config.target << "\n"; } } if(write_compilation_database) { std::ofstream istr(compilation_database); istr << "["; bool first{true}; for(auto task : all_tasks) { auto s = task->toJSON(); if(!s.empty()) { if(!first) { istr << ",\n"; } else { istr << "\n"; } first = false; istr << s; } } istr << "\n]\n"; } if(print_configure_cmd) { no_default_build = true; std::cout << getConfiguration("cmd") << "\n"; } if(print_configure_db) { no_default_build = true; const auto& c = configuration(); for(const auto& config : c) { std::cout << config.first << ": " << config.second << "\n"; } } for(auto task : all_tasks) { if(task->registerDepTasks(all_tasks)) { return 1; } } bool build_all{!no_default_build}; for(const auto& arg : opt.arguments()) { if(arg == "configure") { std::cerr << "The 'configure' target must be the first argument.\n"; return 1; } if(arg == "clean") { build_all = false; std::cout << "Cleaning\n"; for(auto& task : all_tasks) { if(task->clean() != 0) { return 1; } } } else if(arg == "check") { build_all = false; run_unittests = true; std::vector unittest_targets; auto& targets = getTargets(settings); for(const auto& target : targets) { if(target.config.type == TargetType::UnitTest) { unittest_targets.push_back(target); } } auto ret = build(settings, "check", unittest_targets, all_tasks); if(ret != 0) { return ret; } } else { build_all = false; if(arg == "all") { std::vector non_unittest_targets; auto& targets = getTargets(settings); for(const auto& target : targets) { if(target.config.type != TargetType::UnitTest) { non_unittest_targets.push_back(target); } } auto ret = build(settings, "all", non_unittest_targets, all_tasks); if(ret != 0) { return ret; } } else { auto ret = build(settings, arg, all_tasks); if(ret != 0) { return ret; } } } } if(build_all) { std::vector non_unittest_targets; auto& targets = getTargets(settings); for(const auto& target : targets) { if(target.config.type != TargetType::UnitTest) { non_unittest_targets.push_back(target); } } auto ret = build(settings, "all", non_unittest_targets, all_tasks); if(ret != 0) { return ret; } } if(run_unittests) { return runUnitTests(all_tasks, settings); } return 0; }