OctaBuild/main.cc

714 lines
20 KiB
C++
Raw Normal View History

2015-08-16 02:06:42 +02:00
#include <ostd/types.hh>
2015-11-27 23:11:46 +01:00
#include <ostd/functional.hh>
2015-08-16 02:06:42 +02:00
#include <ostd/string.hh>
2015-08-14 03:54:00 +02:00
#include <ostd/vector.hh>
#include <ostd/map.hh>
2015-08-24 20:16:55 +02:00
#include <ostd/atomic.hh>
2015-09-04 19:59:05 +02:00
#include <ostd/filesystem.hh>
2016-02-28 23:21:47 +01:00
#include <ostd/io.hh>
2015-09-26 16:13:17 +02:00
#include <ostd/platform.hh>
2015-11-27 23:11:46 +01:00
#include <ostd/utility.hh>
2016-03-18 22:54:24 +01:00
#include <ostd/environ.hh>
#include <ostd/mutex.hh>
#include <ostd/condition.hh>
2015-08-14 03:54:00 +02:00
2015-08-16 02:06:42 +02:00
#include <cubescript.hh>
2015-08-14 03:54:00 +02:00
#include "tpool.hh"
2015-08-31 15:47:39 +02:00
using ostd::ConstCharRange;
using ostd::Vector;
using ostd::Map;
2015-08-31 15:47:39 +02:00
using ostd::String;
2015-09-01 19:50:00 +02:00
using ostd::slice_until;
using ostd::UniqueLock;
using ostd::Mutex;
using ostd::Condition;
2015-08-31 15:47:39 +02:00
2015-10-13 22:23:41 +02:00
using cscript::CsState;
2016-08-18 20:39:04 +02:00
using cscript::CsValueRange;
using cscript::CsValue;
2015-10-13 22:23:41 +02:00
using cscript::StackedValue;
2016-08-06 19:38:23 +02:00
using cscript::BytecodeRef;
2016-08-06 20:13:06 +02:00
using cscript::Bytecode;
2015-10-13 22:23:41 +02:00
2016-08-02 23:37:26 +02:00
/* glob matching code */
static void ob_get_path_parts(
Vector<ConstCharRange> &parts, ConstCharRange elem
) {
ConstCharRange star = ostd::find(elem, '*');
while (!star.empty()) {
ConstCharRange ep = slice_until(elem, star);
if (!ep.empty()) {
parts.push(ep);
}
parts.push("*");
elem = star;
++elem;
star = ostd::find(elem, '*');
}
if (!elem.empty()) {
parts.push(elem);
}
}
static bool ob_path_matches(
ConstCharRange fn, Vector<ConstCharRange> const &parts
) {
for (auto it = parts.iter(); !it.empty(); ++it) {
ConstCharRange elem = it.front();
if (elem == "*") {
++it;
/* skip multiple stars if present */
while (!it.empty() && ((elem = it.front()) == "*")) {
++it;
}
/* trailing stars, we match */
if (it.empty()) {
return true;
}
/* skip about as much as we can until the elem part matches */
while (fn.size() > elem.size()) {
if (fn.slice(0, elem.size()) == elem) {
break;
}
++fn;
}
}
/* non-star here */
if (fn.size() < elem.size()) {
return false;
}
if (fn.slice(0, elem.size()) != elem) {
return false;
}
fn += elem.size();
}
/* if there are no chars in the fname remaining, we fully matched */
return fn.empty();
}
static bool ob_expand_glob(String &ret, ConstCharRange src, bool ne = false);
static bool ob_expand_dir(
String &ret, ConstCharRange dir, Vector<ConstCharRange> const &parts,
ConstCharRange slash
) {
ostd::DirectoryStream d(dir);
bool appended = false;
if (!d.is_open()) {
return false;
}
for (auto fi: d.iter()) {
ConstCharRange fn = fi.filename();
/* check if filename matches */
if (!ob_path_matches(fn, parts)) {
continue;
}
String afn((dir == ".") ? "" : "./");
afn.append(fn);
/* if we reach this, we match; try recursively matching */
if (!slash.empty()) {
afn.append(slash);
ConstCharRange psl = slash + 1;
if (!ostd::find(psl, '*').empty()) {
if (!appended) {
appended = ob_expand_glob(ret, afn.iter());
}
continue;
}
/* no further star, just do file test */
if (!ostd::FileStream(afn, ostd::StreamMode::read).is_open()) {
continue;
}
if (!ret.empty()) {
ret.push(' ');
}
ret.append(afn);
appended = true;
continue;
}
if (!ret.empty()) {
ret.push(' ');
}
ret.append(afn);
appended = true;
}
return appended;
}
static bool ob_expand_glob(String &ret, ConstCharRange src, bool ne) {
ConstCharRange star = ostd::find(src, '*');
/* no star use as-is */
if (star.empty()) {
if (ne) return false;
if (!ret.empty()) {
ret.push(' ');
}
ret.append(src);
return false;
}
/* part before star */
ConstCharRange prestar = slice_until(src, star);
/* try finding slash before star */
ConstCharRange slash = ostd::find_last(prestar, '/');
/* directory to scan */
ConstCharRange dir = ".";
/* part of name before star */
ConstCharRange fnpre = prestar;
if (!slash.empty()) {
/* there was slash, adjust directory + prefix accordingly */
dir = slice_until(src, slash);
fnpre = slash + 1;
}
/* part after star */
ConstCharRange fnpost = star + 1;
/* if a slash follows, adjust */
ConstCharRange nslash = ostd::find(fnpost, '/');
if (!nslash.empty()) {
fnpost = slice_until(fnpost, nslash);
}
/* retrieve the single element with whatever stars in it, chop it up */
Vector<ConstCharRange> parts;
ob_get_path_parts(parts, ConstCharRange(&fnpre[0], &fnpost[fnpost.size()]));
/* do a directory scan and match */
if (!ob_expand_dir(ret, dir, parts, nslash)) {
if (ne) {
return false;
}
if (!ret.empty()) {
ret.push(' ');
}
ret.append(src);
return false;
}
return true;
}
static String ob_expand_globs(Vector<String> const &src) {
String ret;
for (auto &s: src.iter()) {
ob_expand_glob(ret, s.iter());
}
return ret;
}
2015-08-22 09:31:33 +02:00
/* check funcs */
2015-08-14 03:54:00 +02:00
2016-08-01 01:09:29 +02:00
static bool ob_check_ts(ConstCharRange tname, Vector<String> const &deps) {
2015-12-23 13:52:27 +01:00
auto get_ts = [](ConstCharRange fname) {
2015-09-04 19:59:05 +02:00
ostd::FileInfo fi(fname);
2016-08-01 01:09:29 +02:00
if (fi.type() != ostd::FileType::regular) {
2015-12-23 13:52:27 +01:00
return time_t(0);
2016-08-01 01:09:29 +02:00
}
2015-09-04 19:59:05 +02:00
return fi.mtime();
2015-08-22 09:31:33 +02:00
};
2015-09-04 20:29:51 +02:00
time_t tts = get_ts(tname);
2016-08-01 01:09:29 +02:00
if (!tts) {
return true;
}
2015-08-14 03:54:00 +02:00
for (auto &dep: deps.iter()) {
2015-09-04 20:29:51 +02:00
time_t sts = get_ts(dep);
2016-08-01 01:09:29 +02:00
if (sts && (tts < sts)) {
return true;
}
2015-08-14 03:54:00 +02:00
}
return false;
}
2015-08-31 15:47:39 +02:00
static bool ob_check_file(ConstCharRange fname) {
2015-08-16 02:06:42 +02:00
return ostd::FileStream(fname, ostd::StreamMode::read).is_open();
2015-08-14 03:54:00 +02:00
}
2016-08-01 01:09:29 +02:00
static bool ob_check_exec(ConstCharRange tname, Vector<String> const &deps) {
if (!ob_check_file(tname)) {
2015-08-16 02:06:42 +02:00
return true;
2016-08-01 01:09:29 +02:00
}
for (auto &dep: deps.iter()) {
if (!ob_check_file(dep)) {
2015-08-16 02:06:42 +02:00
return true;
2016-08-01 01:09:29 +02:00
}
}
2015-08-16 02:06:42 +02:00
return ob_check_ts(tname, deps);
}
2015-08-22 09:31:33 +02:00
/* this lets us properly match % patterns in target names */
2016-08-01 01:09:29 +02:00
static ConstCharRange ob_compare_subst(
ConstCharRange expanded, ConstCharRange toexpand
) {
2015-08-14 03:54:00 +02:00
auto rep = ostd::find(toexpand, '%');
/* no subst found */
2016-08-01 01:09:29 +02:00
if (rep.empty()) {
2015-08-21 20:14:24 +02:00
return nullptr;
2016-08-01 01:09:29 +02:00
}
2015-08-14 03:54:00 +02:00
/* get the part before % */
2015-09-01 19:50:00 +02:00
auto fp = slice_until(toexpand, rep);
2015-08-14 03:54:00 +02:00
/* part before % does not compare, so ignore */
2016-08-01 01:09:29 +02:00
if (expanded.size() <= fp.size()) {
2015-08-21 20:14:24 +02:00
return nullptr;
2016-08-01 01:09:29 +02:00
}
if (expanded.slice(0, fp.size()) != fp) {
2015-08-21 20:14:24 +02:00
return nullptr;
2016-08-01 01:09:29 +02:00
}
2015-08-14 03:54:00 +02:00
/* pop out front part */
2015-12-14 20:20:43 +01:00
expanded += fp.size();
2015-08-14 03:54:00 +02:00
/* part after % */
2015-12-14 20:20:43 +01:00
++rep;
2016-08-01 01:09:29 +02:00
if (rep.empty()) {
2015-08-14 03:54:00 +02:00
return expanded;
2016-08-01 01:09:29 +02:00
}
2015-08-14 03:54:00 +02:00
/* part after % does not compare, so ignore */
2016-08-01 01:09:29 +02:00
if (expanded.size() <= rep.size()) {
2015-08-21 20:14:24 +02:00
return nullptr;
2016-08-01 01:09:29 +02:00
}
2015-10-02 20:25:08 +02:00
ostd::Size es = expanded.size();
2016-08-01 01:09:29 +02:00
if (expanded.slice(es - rep.size(), es) != rep) {
2015-08-21 20:14:24 +02:00
return nullptr;
2016-08-01 01:09:29 +02:00
}
2015-08-14 03:54:00 +02:00
/* cut off latter part */
expanded.pop_back_n(rep.size());
/* we got what we wanted... */
return expanded;
}
2015-12-07 22:41:37 +01:00
struct ObState: CsState {
2015-08-31 15:47:39 +02:00
ConstCharRange progname;
2015-08-24 05:54:03 +02:00
int jobs = 1;
2015-10-16 00:09:16 +02:00
bool ignore_env = false;
2015-08-21 21:02:31 +02:00
2015-10-16 00:36:39 +02:00
/* represents a rule definition, possibly with a function */
struct Rule {
String target;
Vector<String> deps;
2016-08-06 19:38:23 +02:00
BytecodeRef func;
2015-10-26 18:58:47 +01:00
bool action;
2015-10-16 00:36:39 +02:00
2015-12-19 16:06:11 +01:00
Rule(): target(), deps(), func(), action(false) {}
2016-08-01 01:09:29 +02:00
Rule(Rule const &r):
target(r.target), deps(r.deps), func(r.func), action(r.action)
{}
2015-10-16 00:36:39 +02:00
};
Vector<Rule> rules;
2015-08-22 09:31:33 +02:00
struct SubRule {
2015-08-31 15:47:39 +02:00
ConstCharRange sub;
2015-08-22 09:31:33 +02:00
Rule *rule;
};
Map<ConstCharRange, Vector<SubRule>> cache;
2015-10-16 00:36:39 +02:00
struct RuleCounter {
2016-08-02 20:54:05 +02:00
RuleCounter(): p_cond(), p_mtx(), p_counter(0), p_result(0) {}
2015-10-16 00:36:39 +02:00
void wait() {
2016-08-02 20:54:05 +02:00
UniqueLock<Mutex> l(p_mtx);
while (p_counter) {
p_cond.wait(l);
2016-08-01 01:09:29 +02:00
}
2015-10-16 00:36:39 +02:00
}
void incr() {
2016-08-02 20:54:05 +02:00
UniqueLock<Mutex> l(p_mtx);
++p_counter;
2015-10-16 00:36:39 +02:00
}
void decr() {
2016-08-02 20:54:05 +02:00
UniqueLock<Mutex> l(p_mtx);
if (!--p_counter) {
l.unlock();
2016-08-02 20:54:05 +02:00
p_cond.broadcast();
}
2015-10-16 00:36:39 +02:00
}
2016-08-02 20:54:05 +02:00
ostd::AtomicInt &get_result() { return p_result; }
2016-08-02 23:49:21 +02:00
private:
2016-08-02 20:54:05 +02:00
Condition p_cond;
Mutex p_mtx;
int p_counter;
ostd::AtomicInt p_result;
2015-10-16 00:36:39 +02:00
};
Vector<RuleCounter *> counters;
2015-11-04 18:46:01 +01:00
template<typename F>
int wait_result(F func) {
RuleCounter ctr;
counters.push(&ctr);
int ret = func();
counters.pop();
2016-08-01 01:09:29 +02:00
if (ret) {
return ret;
}
2015-11-04 18:46:01 +01:00
ctr.wait();
2016-08-02 20:54:05 +02:00
return ctr.get_result();
2015-11-04 18:46:01 +01:00
}
2015-08-21 21:02:31 +02:00
template<typename ...A>
2015-08-31 15:47:39 +02:00
int error(int retcode, ConstCharRange fmt, A &&...args) {
2015-08-21 21:02:31 +02:00
ostd::err.write(progname, ": ");
ostd::err.writefln(fmt, ostd::forward<A>(args)...);
return retcode;
}
2016-08-01 01:09:29 +02:00
int exec_list(
Vector<SubRule> const &rlist, Vector<String> &subdeps,
ConstCharRange tname
) {
2015-08-31 15:47:39 +02:00
String repd;
2016-08-02 23:40:24 +02:00
for (auto &sr: rlist.iter()) {
for (auto &target: sr.rule->deps.iter()) {
ConstCharRange atgt = target.iter();
repd.clear();
auto lp = ostd::find(atgt, '%');
2016-08-01 01:09:29 +02:00
if (!lp.empty()) {
2016-08-02 23:40:24 +02:00
repd.append(slice_until(atgt, lp));
repd.append(sr.sub);
++lp;
if (!lp.empty()) {
repd.append(lp);
}
atgt = repd.iter();
}
subdeps.push(atgt);
int r = exec_rule(atgt, tname);
if (r) {
return r;
2016-08-01 01:09:29 +02:00
}
}
2015-11-05 19:43:58 +01:00
}
2015-08-21 21:02:31 +02:00
return 0;
}
2016-08-01 01:09:29 +02:00
int exec_func(ConstCharRange tname, Vector<SubRule> const &rlist) {
2015-08-31 15:47:39 +02:00
Vector<String> subdeps;
2015-11-04 18:46:01 +01:00
int ret = wait_result([&rlist, &subdeps, &tname, this]() {
return exec_list(rlist, subdeps, tname);
});
2016-08-06 19:38:23 +02:00
BytecodeRef *func = nullptr;
bool act = false;
2016-08-01 01:09:29 +02:00
for (auto &sr: rlist.iter()) {
if (sr.rule->func) {
func = &sr.rule->func;
act = sr.rule->action;
break;
}
}
if ((!ret && (act || ob_check_exec(tname, subdeps))) && func) {
2016-08-12 05:13:52 +02:00
StackedValue targetv, sourcev, sourcesv;
2016-08-18 00:07:00 +02:00
if (!targetv.set_alias(new_ident("target"))) {
return 1;
2016-08-01 01:09:29 +02:00
}
targetv.set_cstr(tname);
targetv.push();
2015-12-23 13:52:27 +01:00
if (!subdeps.empty()) {
2016-08-18 00:07:00 +02:00
if (!sourcev.set_alias(new_ident("source"))) {
return 1;
2016-08-01 01:09:29 +02:00
}
2016-08-18 00:07:00 +02:00
if (!sourcesv.set_alias(new_ident("sources"))) {
2015-08-21 21:02:31 +02:00
return 1;
2016-08-01 01:09:29 +02:00
}
sourcev.set_cstr(subdeps[0]);
sourcev.push();
auto dsv = ostd::appender<String>();
ostd::concat(dsv, subdeps);
2016-07-13 19:50:31 +02:00
sourcesv.set_str(ostd::move(dsv.get()));
sourcesv.push();
2015-08-21 21:02:31 +02:00
}
2015-12-20 00:21:39 +01:00
return run_int(*func);
2015-08-14 03:54:00 +02:00
}
2015-11-03 19:34:16 +01:00
return ret;
2015-08-14 03:54:00 +02:00
}
2015-08-21 21:02:31 +02:00
2015-10-26 18:58:47 +01:00
int exec_action(Rule *rule) {
2015-12-07 23:37:38 +01:00
return run_int(rule->func);
2015-10-26 18:58:47 +01:00
}
int find_rules(ConstCharRange target, Vector<SubRule> &rlist) {
2016-08-01 01:09:29 +02:00
if (!rlist.empty()) {
return 0;
2016-08-01 01:09:29 +02:00
}
SubRule *frule = nullptr;
bool exact = false;
2015-08-21 21:02:31 +02:00
for (auto &rule: rules.iter()) {
if (target == rule.target) {
rlist.push().rule = &rule;
if (rule.func) {
2016-08-01 01:09:29 +02:00
if (frule && exact) {
return error(1, "redefinition of rule '%s'", target);
}
if (!frule) {
frule = &rlist.back();
2016-08-01 01:09:29 +02:00
} else {
*frule = rlist.back();
rlist.pop();
}
exact = true;
}
2015-08-21 21:02:31 +02:00
continue;
}
2016-08-01 01:09:29 +02:00
if (exact || !rule.func) {
continue;
2016-08-01 01:09:29 +02:00
}
2015-08-31 15:47:39 +02:00
ConstCharRange sub = ob_compare_subst(target, rule.target);
2015-08-21 21:02:31 +02:00
if (!sub.empty()) {
SubRule &sr = rlist.push();
sr.rule = &rule;
sr.sub = sub;
if (frule) {
2016-08-01 01:09:29 +02:00
if (sub.size() == frule->sub.size()) {
return error(1, "redefinition of rule '%s'", target);
}
if (sub.size() < frule->sub.size()) {
*frule = sr;
rlist.pop();
}
2016-08-01 01:09:29 +02:00
} else {
frule = &sr;
}
2015-08-21 21:02:31 +02:00
}
}
return 0;
}
int exec_rule(ConstCharRange target, ConstCharRange from = nullptr) {
Vector<SubRule> &rlist = cache[target];
int fret = find_rules(target, rlist);
2016-08-01 01:09:29 +02:00
if (fret) {
return fret;
2016-08-01 01:09:29 +02:00
}
if ((rlist.size() == 1) && rlist[0].rule->action) {
2015-10-26 18:58:47 +01:00
return exec_action(rlist[0].rule);
2016-08-01 01:09:29 +02:00
}
2015-08-21 21:02:31 +02:00
if (rlist.empty() && !ob_check_file(target)) {
2016-08-01 01:09:29 +02:00
if (from.empty()) {
2015-08-21 21:02:31 +02:00
return error(1, "no rule to run target '%s'", target);
2016-08-01 01:09:29 +02:00
} else {
return error(
1, "no rule to run target '%s' (needed by '%s')",
target, from
);
}
2015-08-21 21:02:31 +02:00
return 1;
}
return exec_func(target, rlist);
2015-08-16 02:06:42 +02:00
}
2015-10-16 00:36:39 +02:00
2015-10-21 22:09:21 +02:00
int exec_main(ConstCharRange target) {
2015-11-04 18:46:01 +01:00
return wait_result([&target, this]() { return exec_rule(target); });
2015-10-21 22:09:21 +02:00
}
2016-08-01 01:09:29 +02:00
void rule_add(
2016-08-06 20:13:06 +02:00
ConstCharRange tgt, ConstCharRange dep, Bytecode *body,
2016-08-01 01:09:29 +02:00
bool action = false
) {
2015-10-16 00:36:39 +02:00
auto targets = cscript::util::list_explode(tgt);
for (auto &target: targets.iter()) {
Rule &r = rules.push();
r.target = target;
2015-10-26 18:58:47 +01:00
r.action = action;
r.func = cscript::code_is_empty(body) ? nullptr : body;
2015-11-23 22:26:51 +01:00
r.deps = cscript::util::list_explode(dep);
2015-10-16 00:36:39 +02:00
}
}
2016-08-01 01:09:29 +02:00
void rule_dup(
ConstCharRange tgt, ConstCharRange ptgt, ConstCharRange dep,
bool inherit_deps
) {
Rule *oldr = nullptr;
2016-08-01 01:09:29 +02:00
for (auto &rule: rules.iter()) {
2015-12-27 18:20:47 +01:00
if (ptgt == rule.target) {
oldr = &rule;
2015-12-27 18:20:47 +01:00
break;
}
2016-08-01 01:09:29 +02:00
}
if (!oldr) {
return;
2016-08-01 01:09:29 +02:00
}
Rule &r = rules.push();
r.target = tgt;
r.action = oldr->action;
2015-12-19 16:06:11 +01:00
r.func = oldr->func;
2015-11-24 21:14:37 +01:00
r.deps = inherit_deps ? oldr->deps : cscript::util::list_explode(dep);
}
2015-11-25 21:36:46 +01:00
void register_rulecmds() {
2016-08-18 20:39:04 +02:00
add_command("rule", "sse", [this](CsValueRange args, CsValue &) {
2016-08-01 20:17:44 +02:00
rule_add(
args[0].get_strr(), args[1].get_strr(), args[2].get_code()
2016-07-31 01:50:00 +02:00
);
2015-11-25 21:36:46 +01:00
});
2016-08-18 20:39:04 +02:00
add_command("action", "se", [this](CsValueRange args, CsValue &) {
2016-08-01 20:17:44 +02:00
rule_add(args[0].get_strr(), nullptr, args[1].get_code(), true);
2015-11-25 21:36:46 +01:00
});
2016-08-18 20:39:04 +02:00
add_command("depend", "ss", [this](CsValueRange args, CsValue &) {
2016-08-02 23:49:21 +02:00
rule_add(args[0].get_strr(), args[1].get_strr(), nullptr);
2015-11-25 21:36:46 +01:00
});
2016-08-18 20:39:04 +02:00
add_command("duprule", "sssN", [this](CsValueRange args, CsValue &) {
2016-08-01 20:17:44 +02:00
rule_dup(
2016-07-31 01:50:00 +02:00
args[0].get_strr(), args[1].get_strr(),
args[2].get_strr(), args[3].get_int() <= 2
);
2015-11-25 21:36:46 +01:00
});
}
2015-11-09 19:51:55 +01:00
2016-08-01 20:51:07 +02:00
int print_help(bool error, ConstCharRange deffile) {
2015-12-26 14:29:55 +01:00
ostd::Stream &os = error ? ostd::err : ostd::out;
2016-08-01 01:09:29 +02:00
os.writeln(
"Usage: ", progname, " [options] [action]\n",
"Options:\n"
" -C DIRECTORY\tChange to DIRECTORY before running.\n",
" -f FILE\tSpecify the file to run (default: ", deffile, ").\n"
" -h\t\tPrint this message.\n"
" -j N\t\tSpecify the number of jobs to use (default: 1).\n"
" -e STR\tEvaluate a string instead of a file.\n"
" -E\t\tIgnore environment variables."
);
2015-12-26 14:29:55 +01:00
return error;
2015-12-03 20:09:44 +01:00
}
};
2015-08-24 05:54:03 +02:00
2015-08-14 03:54:00 +02:00
int main(int argc, char **argv) {
2016-08-01 20:17:44 +02:00
ObState os;
2015-08-31 15:47:39 +02:00
ConstCharRange pn = argv[0];
ConstCharRange lslash = ostd::find_last(pn, '/');
2016-08-01 20:17:44 +02:00
os.progname = lslash.empty() ? pn : (lslash + 1);
2015-08-16 02:06:42 +02:00
2016-08-18 20:47:46 +02:00
os.init_libs();
2015-08-16 02:06:42 +02:00
2016-01-26 20:02:32 +01:00
int ncpus = ostd::Thread::hardware_concurrency();
2016-08-20 17:54:13 +02:00
os.add_ident<cscript::Ivar>("numcpus", 4096, 1, &ncpus);
os.add_ident<cscript::Ivar>("numjobs", 4096, 1, &os.jobs);
2015-09-26 16:13:17 +02:00
2015-09-20 21:54:19 +02:00
ConstCharRange fcont;
2016-08-01 20:51:07 +02:00
ConstCharRange deffile = "obuild.cfg";
2015-08-24 05:54:03 +02:00
int posarg = argc;
2016-08-01 01:09:29 +02:00
for (int i = 1; i < argc; ++i) {
if (argv[i][0] == '-') {
char argn = argv[i][1];
if (argn == 'E') {
2016-08-01 20:17:44 +02:00
os.ignore_env = true;
2016-08-01 01:09:29 +02:00
continue;
} else if ((argn == 'h') || (!argv[i][2] && ((i + 1) >= argc))) {
2016-08-01 20:51:07 +02:00
return os.print_help(argn != 'h', deffile);
2016-08-01 01:09:29 +02:00
}
ConstCharRange val = (argv[i][2] == '\0') ? argv[++i] : &argv[i][2];
switch (argn) {
2016-08-03 17:50:37 +02:00
case 'C':
if (!ostd::directory_change(val)) {
return os.error(1, "failed changing directory: %s", val);
}
break;
case 'f':
deffile = val;
break;
case 'e':
fcont = val;
break;
case 'j': {
int ival = atoi(val.data());
if (!ival) {
ival = ncpus;
}
os.jobs = ostd::max(1, ival);
break;
2016-08-01 01:09:29 +02:00
}
2016-08-03 17:50:37 +02:00
default:
return os.print_help(true, deffile);
2016-08-01 01:09:29 +02:00
}
} else {
posarg = i;
2015-08-24 05:54:03 +02:00
break;
2015-09-26 16:13:17 +02:00
}
2015-08-24 05:54:03 +02:00
}
2016-08-02 20:56:34 +02:00
ThreadPool tpool;
2016-08-01 20:17:44 +02:00
tpool.init(os.jobs);
2015-08-24 20:16:55 +02:00
2016-08-01 20:17:44 +02:00
os.register_rulecmds();
2015-11-25 21:36:46 +01:00
2016-08-11 19:14:42 +02:00
os.add_command("shell", "C", [&os, &tpool](
2016-08-18 20:39:04 +02:00
CsValueRange args, CsValue &res
2016-08-11 19:14:42 +02:00
) {
2015-12-08 22:36:55 +01:00
auto cnt = os.counters.back();
2015-08-24 20:16:55 +02:00
cnt->incr();
2016-08-01 01:34:26 +02:00
tpool.push([cnt, ds = String(args[0].get_strr())]() {
2015-11-01 19:45:03 +01:00
int ret = system(ds.data());
2016-08-02 20:54:05 +02:00
if (ret && !cnt->get_result()) {
cnt->get_result() = ret;
2016-08-01 01:09:29 +02:00
}
2015-08-24 20:16:55 +02:00
cnt->decr();
});
2016-08-11 19:14:42 +02:00
res.set_int(0);
2015-08-14 03:54:00 +02:00
});
2016-08-18 20:39:04 +02:00
os.add_command("getenv", "ss", [&os](CsValueRange args, CsValue &res) {
2015-12-08 22:36:55 +01:00
if (os.ignore_env) {
2016-08-11 19:14:42 +02:00
res.set_cstr("");
2015-09-21 21:44:48 +02:00
return;
}
2016-08-11 19:14:42 +02:00
res.set_str(ostd::move(
2016-08-01 01:09:29 +02:00
ostd::env_get(args[0].get_str()).value_or(args[1].get_str())
));
2015-09-21 21:44:48 +02:00
});
2016-08-18 20:39:04 +02:00
os.add_command("extreplace", "sss", [&os](CsValueRange args, CsValue &res) {
2016-08-01 01:16:22 +02:00
ConstCharRange lst = args[0].get_strr();
ConstCharRange oldext = args[1].get_strr();
ConstCharRange newext = args[2].get_strr();
2015-11-07 17:57:29 +01:00
String ret;
2016-08-01 01:16:22 +02:00
if (oldext.front() == '.') {
oldext.pop_front();
2016-08-01 01:09:29 +02:00
}
2016-08-01 01:16:22 +02:00
if (newext.front() == '.') {
newext.pop_front();
2016-08-01 01:09:29 +02:00
}
2015-11-07 17:57:29 +01:00
auto fnames = cscript::util::list_explode(lst);
for (ConstCharRange it: fnames.iter()) {
2016-08-01 01:09:29 +02:00
if (!ret.empty()) {
ret += ' ';
}
2015-11-07 17:57:29 +01:00
auto dot = ostd::find_last(it, '.');
if (!dot.empty() && ((dot + 1) == oldext)) {
2016-08-05 17:59:17 +02:00
ret += slice_until(it, dot);
2015-11-07 17:57:29 +01:00
ret += '.';
ret += newext;
} else {
ret += it;
}
}
2016-08-11 19:14:42 +02:00
res.set_str(ostd::move(ret));
2015-11-07 17:57:29 +01:00
});
2016-08-18 20:39:04 +02:00
os.add_command("invoke", "s", [&os](CsValueRange args, CsValue &res) {
2016-08-11 19:14:42 +02:00
res.set_int(os.exec_main(args[0].get_strr()));
});
2016-08-18 20:39:04 +02:00
os.add_command("glob", "C", [&os](CsValueRange args, CsValue &res) {
2016-08-02 23:37:26 +02:00
auto fnames = cscript::util::list_explode(args[0].get_strr());
2016-08-17 22:21:36 +02:00
res.set_str(ostd::move(ob_expand_globs(fnames)));
2016-08-02 23:37:26 +02:00
});
2015-08-23 10:03:30 +02:00
2016-08-01 20:17:44 +02:00
if ((!fcont.empty() && !os.run_bool(fcont)) || !os.run_file(deffile)) {
return os.error(1, "failed creating rules");
2016-08-01 01:09:29 +02:00
}
2015-08-16 02:06:42 +02:00
2016-08-01 20:17:44 +02:00
if (os.rules.empty()) {
return os.error(1, "no targets");
2016-08-01 01:09:29 +02:00
}
2015-08-16 02:06:42 +02:00
2016-08-01 20:17:44 +02:00
return os.exec_main((posarg < argc) ? argv[posarg] : "default");
2016-02-07 22:20:28 +01:00
}