forked from OctaForge/OctaBuild
utilize build::make
parent
389a446021
commit
4ee9ef89ea
394
main.cc
394
main.cc
|
@ -1,10 +1,5 @@
|
||||||
#include <utility>
|
#include <utility>
|
||||||
#include <thread>
|
|
||||||
#include <unordered_map>
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <stack>
|
|
||||||
#include <queue>
|
|
||||||
#include <future>
|
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
|
|
||||||
#include <ostd/string.hh>
|
#include <ostd/string.hh>
|
||||||
|
@ -13,9 +8,10 @@
|
||||||
#include <ostd/io.hh>
|
#include <ostd/io.hh>
|
||||||
#include <ostd/platform.hh>
|
#include <ostd/platform.hh>
|
||||||
#include <ostd/environ.hh>
|
#include <ostd/environ.hh>
|
||||||
#include <ostd/thread_pool.hh>
|
|
||||||
#include <ostd/argparse.hh>
|
#include <ostd/argparse.hh>
|
||||||
|
|
||||||
|
#include <ostd/build/make.hh>
|
||||||
|
|
||||||
#include <cubescript/cubescript.hh>
|
#include <cubescript/cubescript.hh>
|
||||||
|
|
||||||
using ostd::string_range;
|
using ostd::string_range;
|
||||||
|
@ -29,370 +25,81 @@ using cscript::cs_bcode_ref;
|
||||||
using cscript::cs_bcode;
|
using cscript::cs_bcode;
|
||||||
|
|
||||||
namespace fs = ostd::fs;
|
namespace fs = ostd::fs;
|
||||||
|
namespace build = ostd::build;
|
||||||
struct build_error: std::runtime_error {
|
|
||||||
using std::runtime_error::runtime_error;
|
|
||||||
|
|
||||||
template<typename ...A>
|
|
||||||
build_error(string_range fmt, A const &...args):
|
|
||||||
build_error(
|
|
||||||
ostd::format(ostd::appender<std::string>(), fmt, args...).get()
|
|
||||||
)
|
|
||||||
{}
|
|
||||||
};
|
|
||||||
|
|
||||||
/* check funcs */
|
|
||||||
|
|
||||||
static bool ob_check_exec(
|
|
||||||
string_range tname, std::vector<std::string> const &deps
|
|
||||||
) {
|
|
||||||
if (!fs::exists(tname)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
for (auto &dep: deps) {
|
|
||||||
if (!fs::exists(dep)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
auto get_ts = [](string_range fname) {
|
|
||||||
path p{fname};
|
|
||||||
if (!fs::is_regular_file(p)) {
|
|
||||||
return fs::file_time_t{};
|
|
||||||
}
|
|
||||||
return fs::last_write_time(p);
|
|
||||||
};
|
|
||||||
auto tts = get_ts(tname);
|
|
||||||
if (tts == fs::file_time_t{}) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
for (auto &dep: deps) {
|
|
||||||
auto sts = get_ts(dep);
|
|
||||||
if ((sts != fs::file_time_t{}) && (tts < sts)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* this lets us properly match % patterns in target names */
|
|
||||||
static string_range ob_compare_subst(
|
|
||||||
string_range expanded, string_range toexpand
|
|
||||||
) {
|
|
||||||
auto rep = ostd::find(toexpand, '%');
|
|
||||||
/* no subst found */
|
|
||||||
if (rep.empty()) {
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
/* get the part before % */
|
|
||||||
auto fp = toexpand.slice(0, &rep[0] - &toexpand[0]);
|
|
||||||
/* part before % does not compare, so ignore */
|
|
||||||
if (expanded.size() <= fp.size()) {
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
if (expanded.slice(0, fp.size()) != fp) {
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
/* pop out front part */
|
|
||||||
expanded = expanded.slice(fp.size(), expanded.size());
|
|
||||||
/* part after % */
|
|
||||||
++rep;
|
|
||||||
if (rep.empty()) {
|
|
||||||
return expanded;
|
|
||||||
}
|
|
||||||
/* part after % does not compare, so ignore */
|
|
||||||
if (expanded.size() <= rep.size()) {
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
size_t es = expanded.size();
|
|
||||||
if (expanded.slice(es - rep.size(), es) != rep) {
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
/* cut off latter part */
|
|
||||||
expanded = expanded.slice(0, expanded.size() - rep.size());
|
|
||||||
/* we got what we wanted... */
|
|
||||||
return expanded;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct ob_state: cs_state {
|
struct ob_state: cs_state {
|
||||||
bool ignore_env = false;
|
void rule_add(
|
||||||
|
build::make &mk, string_range tgt, string_range dep, cs_bcode *body,
|
||||||
/* represents a rule definition, possibly with a function */
|
bool action = false
|
||||||
struct Rule {
|
|
||||||
std::string target;
|
|
||||||
std::vector<std::string> deps;
|
|
||||||
cs_bcode_ref func;
|
|
||||||
bool action;
|
|
||||||
|
|
||||||
Rule(): target(), deps(), func(), action(false) {}
|
|
||||||
Rule(Rule const &r):
|
|
||||||
target(r.target), deps(r.deps), func(r.func), action(r.action)
|
|
||||||
{}
|
|
||||||
};
|
|
||||||
|
|
||||||
std::vector<Rule> rules;
|
|
||||||
|
|
||||||
struct SubRule {
|
|
||||||
string_range sub;
|
|
||||||
Rule *rule;
|
|
||||||
};
|
|
||||||
|
|
||||||
std::unordered_map<string_range, std::vector<SubRule>> cache;
|
|
||||||
|
|
||||||
ostd::thread_pool tpool;
|
|
||||||
std::stack<std::queue<std::future<void>> *> waiting;
|
|
||||||
|
|
||||||
template<typename F>
|
|
||||||
void wait_result(F func) {
|
|
||||||
std::queue<std::future<void>> waits;
|
|
||||||
waiting.push(&waits);
|
|
||||||
try {
|
|
||||||
func();
|
|
||||||
} catch (...) {
|
|
||||||
waiting.pop();
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
waiting.pop();
|
|
||||||
for (; !waits.empty(); waits.pop()) {
|
|
||||||
try {
|
|
||||||
waits.front().get();
|
|
||||||
} catch (build_error const &) {
|
|
||||||
waits.pop();
|
|
||||||
ostd::writeln("waiting for the remaining tasks to finish...");
|
|
||||||
for (; !waits.empty(); waits.pop()) {
|
|
||||||
try {
|
|
||||||
waits.front().get();
|
|
||||||
} catch (build_error const &) {
|
|
||||||
/* no rethrow */
|
|
||||||
}
|
|
||||||
}
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename F>
|
|
||||||
void push_task(F &&func) {
|
|
||||||
waiting.top()->push(tpool.push(std::forward<F>(func)));
|
|
||||||
}
|
|
||||||
|
|
||||||
void exec_list(
|
|
||||||
std::vector<SubRule> const &rlist, std::vector<std::string> &subdeps,
|
|
||||||
string_range tname
|
|
||||||
) {
|
) {
|
||||||
std::string repd;
|
cscript::util::ListParser p{*this, tgt};
|
||||||
for (auto &sr: rlist) {
|
while (p.parse()) {
|
||||||
for (auto &target: sr.rule->deps) {
|
auto &rl = mk.rule(p.get_item()).action(action);
|
||||||
string_range atgt = ostd::iter(target);
|
cscript::util::ListParser lp{*this, dep};
|
||||||
repd.clear();
|
while (lp.parse()) {
|
||||||
auto lp = ostd::find(atgt, '%');
|
rl.depend(lp.get_item());
|
||||||
if (!lp.empty()) {
|
|
||||||
repd.append(atgt.slice(0, &lp[0] - &atgt[0]));
|
|
||||||
repd.append(sr.sub);
|
|
||||||
++lp;
|
|
||||||
if (!lp.empty()) {
|
|
||||||
repd.append(lp);
|
|
||||||
}
|
}
|
||||||
atgt = ostd::iter(repd);
|
if (cscript::cs_code_is_empty(body)) {
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
subdeps.push_back(std::string{atgt});
|
rl.body([body = cs_bcode_ref(body), this](auto tgt, auto srcs) {
|
||||||
exec_rule(atgt, tname);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void exec_func(string_range tname, std::vector<SubRule> const &rlist) {
|
|
||||||
std::vector<std::string> subdeps;
|
|
||||||
if ((rlist.size() > 1) || !rlist[0].rule->deps.empty()) {
|
|
||||||
wait_result([&rlist, &subdeps, &tname, this]() {
|
|
||||||
exec_list(rlist, subdeps, tname);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
cs_bcode_ref *func = nullptr;
|
|
||||||
bool act = false;
|
|
||||||
for (auto &sr: rlist) {
|
|
||||||
if (sr.rule->func) {
|
|
||||||
func = &sr.rule->func;
|
|
||||||
act = sr.rule->action;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ((act || ob_check_exec(tname, subdeps)) && func) {
|
|
||||||
cs_stacked_value targetv, sourcev, sourcesv;
|
cs_stacked_value targetv, sourcev, sourcesv;
|
||||||
|
|
||||||
if (!targetv.set_alias(new_ident("target"))) {
|
if (!targetv.set_alias(new_ident("target"))) {
|
||||||
throw build_error{
|
throw build::make_error{
|
||||||
"internal error: could not set alias 'target'"
|
"internal error: could not set alias 'target'"
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
targetv.set_cstr(tname);
|
targetv.set_cstr(tgt);
|
||||||
targetv.push();
|
targetv.push();
|
||||||
|
|
||||||
if (!subdeps.empty()) {
|
if (!srcs.empty()) {
|
||||||
if (!sourcev.set_alias(new_ident("source"))) {
|
if (!sourcev.set_alias(new_ident("source"))) {
|
||||||
throw build_error{
|
throw build::make_error{
|
||||||
"internal error: could not set alias 'source'"
|
"internal error: could not set alias 'source'"
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
if (!sourcesv.set_alias(new_ident("sources"))) {
|
if (!sourcesv.set_alias(new_ident("sources"))) {
|
||||||
throw build_error{
|
throw build::make_error{
|
||||||
"internal error: could not set alias 'sources'"
|
"internal error: could not set alias 'sources'"
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
sourcev.set_cstr(subdeps[0]);
|
sourcev.set_cstr(srcs[0]);
|
||||||
sourcev.push();
|
sourcev.push();
|
||||||
|
|
||||||
auto dsv = ostd::appender<std::string>();
|
auto dsv = ostd::appender<std::string>();
|
||||||
ostd::format(dsv, "%(%s %)", subdeps);
|
ostd::format(dsv, "%(%s %)", srcs);
|
||||||
sourcesv.set_str(std::move(dsv.get()));
|
sourcesv.set_str(std::move(dsv.get()));
|
||||||
sourcesv.push();
|
sourcesv.push();
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
run(*func);
|
this->run(body);
|
||||||
} catch (cscript::cs_error const &e) {
|
} catch (cscript::cs_error const &e) {
|
||||||
throw build_error{e.what()};
|
throw build::make_error{e.what()};
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void find_rules(string_range target, std::vector<SubRule> &rlist) {
|
|
||||||
if (!rlist.empty()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
SubRule *frule = nullptr;
|
|
||||||
bool exact = false;
|
|
||||||
for (auto &rule: rules) {
|
|
||||||
if (target == string_range{rule.target}) {
|
|
||||||
rlist.emplace_back();
|
|
||||||
rlist.back().rule = &rule;
|
|
||||||
if (rule.func) {
|
|
||||||
if (frule && exact) {
|
|
||||||
throw build_error{"redefinition of rule '%s'", target};
|
|
||||||
}
|
|
||||||
if (!frule) {
|
|
||||||
frule = &rlist.back();
|
|
||||||
} else {
|
|
||||||
*frule = rlist.back();
|
|
||||||
rlist.pop_back();
|
|
||||||
}
|
|
||||||
exact = true;
|
|
||||||
}
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (exact || !rule.func) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
string_range sub = ob_compare_subst(target, rule.target);
|
|
||||||
if (!sub.empty()) {
|
|
||||||
rlist.emplace_back();
|
|
||||||
SubRule &sr = rlist.back();
|
|
||||||
sr.rule = &rule;
|
|
||||||
sr.sub = sub;
|
|
||||||
if (frule) {
|
|
||||||
if (sub.size() == frule->sub.size()) {
|
|
||||||
throw build_error{"redefinition of rule '%s'", target};
|
|
||||||
}
|
|
||||||
if (sub.size() < frule->sub.size()) {
|
|
||||||
*frule = sr;
|
|
||||||
rlist.pop_back();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
frule = &sr;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void exec_rule(string_range target, string_range from = nullptr) {
|
void register_rulecmds(build::make &mk) {
|
||||||
std::vector<SubRule> &rlist = cache[target];
|
new_command("rule", "sse", [this, &mk](auto &, auto args, auto &) {
|
||||||
find_rules(target, rlist);
|
|
||||||
if (rlist.empty() && !fs::exists(target)) {
|
|
||||||
if (from.empty()) {
|
|
||||||
throw build_error{"no rule to run target '%s'", target};
|
|
||||||
} else {
|
|
||||||
throw build_error{
|
|
||||||
"no rule to run target '%s' (needed by '%s')", target, from
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
exec_func(target, rlist);
|
|
||||||
}
|
|
||||||
|
|
||||||
void exec_main(string_range target) {
|
|
||||||
wait_result([&target, this]() { exec_rule(target); });
|
|
||||||
}
|
|
||||||
|
|
||||||
void rule_add(
|
|
||||||
cs_state &cs, string_range tgt, string_range dep, cs_bcode *body,
|
|
||||||
bool action = false
|
|
||||||
) {
|
|
||||||
cscript::util::ListParser p{cs, tgt};
|
|
||||||
while (p.parse()) {
|
|
||||||
rules.emplace_back();
|
|
||||||
Rule &r = rules.back();
|
|
||||||
r.target = p.get_item();
|
|
||||||
r.action = action;
|
|
||||||
r.func = cscript::cs_code_is_empty(body) ? nullptr : body;
|
|
||||||
cscript::util::ListParser lp{cs, dep};
|
|
||||||
while (lp.parse()) {
|
|
||||||
r.deps.push_back(lp.get_item());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void rule_dup(
|
|
||||||
cs_state &cs, string_range tgt, string_range ptgt,
|
|
||||||
string_range dep, bool inherit_deps
|
|
||||||
) {
|
|
||||||
Rule *oldr = nullptr;
|
|
||||||
for (auto &rule: rules) {
|
|
||||||
if (ptgt == string_range{rule.target}) {
|
|
||||||
oldr = &rule;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!oldr) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
rules.emplace_back();
|
|
||||||
Rule &r = rules.back();
|
|
||||||
r.target = tgt;
|
|
||||||
r.action = oldr->action;
|
|
||||||
r.func = oldr->func;
|
|
||||||
if (inherit_deps) {
|
|
||||||
r.deps = oldr->deps;
|
|
||||||
} else {
|
|
||||||
cscript::util::ListParser p{cs, dep};
|
|
||||||
while (p.parse()) {
|
|
||||||
r.deps.push_back(p.get_item());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void register_rulecmds() {
|
|
||||||
new_command("rule", "sse", [this](auto &cs, auto args, auto &) {
|
|
||||||
this->rule_add(
|
this->rule_add(
|
||||||
cs, args[0].get_strr(), args[1].get_strr(), args[2].get_code()
|
mk, args[0].get_strr(), args[1].get_strr(), args[2].get_code()
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
new_command("action", "se", [this](auto &cs, auto args, auto &) {
|
new_command("action", "se", [this, &mk](auto &, auto args, auto &) {
|
||||||
this->rule_add(
|
this->rule_add(
|
||||||
cs, args[0].get_strr(), nullptr, args[1].get_code(), true
|
mk, args[0].get_strr(), nullptr, args[1].get_code(), true
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
new_command("depend", "ss", [this](auto &cs, auto args, auto &) {
|
new_command("depend", "ss", [this, &mk](auto &, auto args, auto &) {
|
||||||
this->rule_add(cs, args[0].get_strr(), args[1].get_strr(), nullptr);
|
this->rule_add(mk, args[0].get_strr(), args[1].get_strr(), nullptr);
|
||||||
});
|
});
|
||||||
|
|
||||||
new_command("duprule", "sssN", [this](auto &cs, auto args, auto &) {
|
|
||||||
this->rule_dup(
|
|
||||||
cs, args[0].get_strr(), args[1].get_strr(),
|
|
||||||
args[2].get_strr(), args[3].get_int() <= 2
|
|
||||||
);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -400,6 +107,8 @@ void do_main(int argc, char **argv) {
|
||||||
ob_state os;
|
ob_state os;
|
||||||
os.init_libs();
|
os.init_libs();
|
||||||
|
|
||||||
|
bool ignore_env = false;
|
||||||
|
|
||||||
int ncpus = std::thread::hardware_concurrency();
|
int ncpus = std::thread::hardware_concurrency();
|
||||||
os.new_ivar("numcpus", 4096, 1, ncpus);
|
os.new_ivar("numcpus", 4096, 1, ncpus);
|
||||||
|
|
||||||
|
@ -433,7 +142,7 @@ void do_main(int argc, char **argv) {
|
||||||
|
|
||||||
ap.add_optional("-E", "--ignore-env", 0)
|
ap.add_optional("-E", "--ignore-env", 0)
|
||||||
.help("ignore environment variables")
|
.help("ignore environment variables")
|
||||||
.action(ostd::arg_store_true(os.ignore_env));
|
.action(ostd::arg_store_true(ignore_env));
|
||||||
|
|
||||||
std::string action = "default";
|
std::string action = "default";
|
||||||
ap.add_positional("action", ostd::arg_value::OPTIONAL)
|
ap.add_positional("action", ostd::arg_value::OPTIONAL)
|
||||||
|
@ -445,7 +154,7 @@ void do_main(int argc, char **argv) {
|
||||||
} catch (ostd::arg_error const &e) {
|
} catch (ostd::arg_error const &e) {
|
||||||
ostd::cerr.writefln("%s: %s", argv[0], e.what());
|
ostd::cerr.writefln("%s: %s", argv[0], e.what());
|
||||||
ap.print_help(ostd::cerr.iter());
|
ap.print_help(ostd::cerr.iter());
|
||||||
throw build_error{""};
|
throw build::make_error{""};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (help.used()) {
|
if (help.used()) {
|
||||||
|
@ -462,31 +171,30 @@ void do_main(int argc, char **argv) {
|
||||||
fs::current_path(curdir);
|
fs::current_path(curdir);
|
||||||
}
|
}
|
||||||
} catch (fs::fs_error const &e) {
|
} catch (fs::fs_error const &e) {
|
||||||
throw build_error{
|
throw build::make_error{
|
||||||
"failed changing directory: %s (0s)", curdir, e.what()
|
"failed changing directory: %s (0s)", curdir, e.what()
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
os.new_ivar("numjobs", 4096, 1, jobs);
|
os.new_ivar("numjobs", 4096, 1, jobs);
|
||||||
|
|
||||||
os.tpool.start(jobs);
|
build::make mk{build::make_task_simple, jobs};
|
||||||
|
os.register_rulecmds(mk);
|
||||||
os.register_rulecmds();
|
|
||||||
|
|
||||||
os.new_command("echo", "C", [](auto &, auto args, auto &) {
|
os.new_command("echo", "C", [](auto &, auto args, auto &) {
|
||||||
writeln(args[0].get_strr());
|
writeln(args[0].get_strr());
|
||||||
});
|
});
|
||||||
|
|
||||||
os.new_command("shell", "C", [&os](auto &, auto args, auto &) {
|
os.new_command("shell", "C", [&mk](auto &, auto args, auto &) {
|
||||||
os.push_task([ds = std::string(args[0].get_strr())]() {
|
mk.push_task([ds = std::string(args[0].get_strr())]() {
|
||||||
if (system(ds.data())) {
|
if (system(ds.data())) {
|
||||||
throw build_error{""};
|
throw build::make_error{""};
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
os.new_command("getenv", "ss", [&os](auto &, auto args, auto &res) {
|
os.new_command("getenv", "ss", [ignore_env](auto &, auto args, auto &res) {
|
||||||
if (os.ignore_env) {
|
if (ignore_env) {
|
||||||
res.set_cstr("");
|
res.set_cstr("");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -525,8 +233,8 @@ void do_main(int argc, char **argv) {
|
||||||
res.set_str(std::move(ret));
|
res.set_str(std::move(ret));
|
||||||
});
|
});
|
||||||
|
|
||||||
os.new_command("invoke", "s", [&os](auto &, auto args, auto &) {
|
os.new_command("invoke", "s", [&mk](auto &, auto args, auto &) {
|
||||||
os.exec_main(args[0].get_strr());
|
mk.exec(args[0].get_strr());
|
||||||
});
|
});
|
||||||
|
|
||||||
os.new_command("glob", "C", [](auto &cs, auto args, auto &res) {
|
os.new_command("glob", "C", [](auto &cs, auto args, auto &res) {
|
||||||
|
@ -541,20 +249,16 @@ void do_main(int argc, char **argv) {
|
||||||
});
|
});
|
||||||
|
|
||||||
if ((!fcont.empty() && !os.run_bool(fcont)) || !os.run_file(deffile)) {
|
if ((!fcont.empty() && !os.run_bool(fcont)) || !os.run_file(deffile)) {
|
||||||
throw build_error{"failed creating rules"};
|
throw build::make_error{"failed creating rules"};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (os.rules.empty()) {
|
mk.exec(action);
|
||||||
throw build_error{"no targets"};
|
|
||||||
}
|
|
||||||
|
|
||||||
os.exec_main(action);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, char **argv) {
|
int main(int argc, char **argv) {
|
||||||
try {
|
try {
|
||||||
do_main(argc, argv);
|
do_main(argc, argv);
|
||||||
} catch (build_error const &e) {
|
} catch (build::make_error const &e) {
|
||||||
auto s = e.what();
|
auto s = e.what();
|
||||||
if (s[0]) {
|
if (s[0]) {
|
||||||
ostd::cerr.writefln("%s: %s", argv[0], s);
|
ostd::cerr.writefln("%s: %s", argv[0], s);
|
||||||
|
|
Loading…
Reference in New Issue