OctaBuild/main.cc

501 lines
15 KiB
C++
Raw Normal View History

2015-08-24 05:54:03 +02:00
#include <unistd.h>
2015-08-14 03:54:00 +02:00
2015-08-16 02:06:42 +02:00
#include <ostd/types.hh>
#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>
2015-09-26 16:13:17 +02:00
#include <ostd/platform.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
2015-09-10 19:55:49 +02:00
#include "globs.hh"
#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::Uint32;
using ostd::slice_until;
2015-08-31 15:47:39 +02:00
2015-10-13 22:23:41 +02:00
using cscript::CsState;
using cscript::TvalRange;
using cscript::StackedValue;
2015-08-22 09:31:33 +02:00
/* check funcs */
2015-08-14 03:54:00 +02:00
2015-08-31 15:47:39 +02:00
static bool ob_check_ts(ConstCharRange tname, const Vector<String> &deps) {
auto get_ts = [](ConstCharRange fname) -> time_t {
2015-09-04 19:59:05 +02:00
ostd::FileInfo fi(fname);
if (fi.type() != ostd::FileType::regular)
2015-08-22 09:31:33 +02:00
return 0;
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);
2015-08-14 03:54:00 +02:00
if (!tts) return true;
for (auto &dep: deps.iter()) {
2015-09-04 20:29:51 +02:00
time_t sts = get_ts(dep);
2015-08-31 15:47:39 +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
}
2015-10-31 20:43:39 +01:00
static bool ob_check_exec(ConstCharRange tname, const Vector<String> &deps) {
2015-08-16 02:06:42 +02:00
if (!ob_check_file(tname))
return true;
for (auto &dep: deps.iter())
if (!ob_check_file(dep))
return true;
return ob_check_ts(tname, deps);
}
2015-10-18 01:01:11 +02:00
static ConstCharRange ob_get_env(ConstCharRange ename) {
return getenv(ostd::String(ename).data());
}
2015-08-22 09:31:33 +02:00
/* this lets us properly match % patterns in target names */
2015-08-31 15:47:39 +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 */
if (rep.empty())
2015-08-21 20:14:24 +02:00
return nullptr;
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 */
if (expanded.size() <= fp.size())
2015-08-21 20:14:24 +02:00
return nullptr;
2015-08-14 03:54:00 +02:00
if (expanded.slice(0, fp.size()) != fp)
2015-08-21 20:14:24 +02:00
return nullptr;
2015-08-14 03:54:00 +02:00
/* pop out front part */
expanded.pop_front_n(fp.size());
/* part after % */
rep.pop_front();
if (rep.empty())
return expanded;
/* part after % does not compare, so ignore */
if (expanded.size() <= rep.size())
2015-08-21 20:14:24 +02:00
return nullptr;
2015-10-02 20:25:08 +02:00
ostd::Size es = expanded.size();
if (expanded.slice(es - rep.size(), es) != rep)
2015-08-21 20:14:24 +02:00
return nullptr;
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-10-26 18:58:47 +01:00
static ThreadPool tpool;
2015-08-21 21:02:31 +02:00
struct ObState {
2015-10-13 22:23:41 +02:00
CsState cs;
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;
Uint32 *func;
2015-10-26 18:58:47 +01:00
bool action;
2015-10-16 00:36:39 +02:00
2015-10-26 18:58:47 +01:00
Rule(): target(), deps(), func(nullptr), action(false) {}
Rule(const Rule &r): target(r.target), deps(r.deps), func(r.func),
action(r.action) {
2015-10-16 00:36:39 +02:00
cscript::bcode_ref(func);
}
~Rule() { cscript::bcode_unref(func); }
};
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 {
2015-11-04 18:46:01 +01:00
RuleCounter(): cond(), mtx(), counter(0), result(0) {}
2015-10-16 00:36:39 +02:00
void wait() {
mtx.lock();
while (counter)
cond.wait(mtx);
mtx.unlock();
}
void incr() {
mtx.lock();
++counter;
mtx.unlock();
}
void decr() {
mtx.lock();
if (!--counter) {
mtx.unlock();
cond.broadcast();
} else
mtx.unlock();
}
Cond cond;
Mutex mtx;
volatile int counter;
ostd::AtomicInt result;
};
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();
if (ret) return ret;
ctr.wait();
return ctr.result;
}
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;
}
2015-08-31 15:47:39 +02:00
int exec_list(const Vector<SubRule> &rlist, Vector<String> &subdeps,
ConstCharRange tname) {
String repd;
2015-11-05 19:43:58 +01:00
for (auto &sr: rlist.iter()) for (auto &target: sr.rule->deps.iter()) {
ConstCharRange atgt = target.iter();
repd.clear();
auto lp = ostd::find(atgt, '%');
if (!lp.empty()) {
repd.append(slice_until(atgt, lp));
repd.append(sr.sub);
lp.pop_front();
if (!lp.empty()) repd.append(lp);
atgt = repd.iter();
2015-08-21 21:02:31 +02:00
}
2015-11-05 19:43:58 +01:00
subdeps.push(atgt);
int r = exec_rule(atgt, tname);
if (r) return r;
}
2015-08-21 21:02:31 +02:00
return 0;
}
2015-08-31 15:47:39 +02:00
int exec_func(ConstCharRange tname, const Vector<SubRule> &rlist) {
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);
});
Uint32 *func = nullptr;
bool act = false;
for (auto &sr: rlist.iter()) {
Rule &r = *sr.rule;
if (r.func) {
func = r.func;
act = r.action;
break;
2015-08-21 21:02:31 +02:00
}
}
if ((!ret && (act || ob_check_exec(tname, subdeps))) && func) {
StackedValue targetv, sourcev, sourcesv;
targetv.id = cs.new_ident("target");
if (!cscript::check_alias(targetv.id))
return 1;
targetv.set_cstr(tname);
targetv.push();
if (subdeps.size() > 0) {
sourcev.id = cs.new_ident("source");
sourcesv.id = cs.new_ident("sources");
if (!cscript::check_alias(sourcev.id))
return 1;
if (!cscript::check_alias(sourcesv.id))
2015-08-21 21:02:31 +02:00
return 1;
sourcev.set_cstr(subdeps[0]);
sourcev.push();
auto dsv = ostd::appender<String>();
ostd::concat(dsv, subdeps);
ostd::Size len = dsv.size();
sourcesv.set_str(ostd::CharRange(dsv.get().disown(),
len));
sourcesv.push();
2015-08-21 21:02:31 +02:00
}
return cs.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) {
return cs.run_int(rule->func);
}
int find_rules(ConstCharRange target, Vector<SubRule> &rlist) {
if (!rlist.empty())
return 0;
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) {
if (frule && exact)
return error(1, "redefinition of rule '%s'",
target);
if (!frule)
frule = &rlist.back();
else {
*frule = rlist.back();
rlist.pop();
}
exact = true;
}
2015-08-21 21:02:31 +02:00
continue;
}
if (exact || !rule.func)
continue;
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) {
if (sub.size() == frule->sub.size())
return error(1, "redefinition of rule '%s'",
target);
if (sub.size() < frule->sub.size()) {
*frule = sr;
rlist.pop();
}
} 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);
if (fret)
return fret;
2015-10-26 18:58:47 +01:00
if ((rlist.size() == 1) && rlist[0].rule->action)
return exec_action(rlist[0].rule);
2015-08-21 21:02:31 +02:00
if (rlist.empty() && !ob_check_file(target)) {
if (from.empty())
return error(1, "no rule to run target '%s'", target);
else
return error(1, "no rule to run target '%s' (needed by '%s')",
target, from);
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
}
2015-11-23 22:26:07 +01:00
void rule_add(ConstCharRange tgt, ConstCharRange dep, ostd::Uint32 *body,
2015-10-26 18:58:47 +01: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;
2015-10-16 00:36:39 +02:00
if (body) {
r.func = body;
cscript::bcode_ref(body);
}
2015-11-23 22:26:51 +01:00
r.deps = cscript::util::list_explode(dep);
2015-10-16 00:36:39 +02:00
}
}
2015-11-24 21:14:37 +01:00
void rule_dup(ConstCharRange tgt, ConstCharRange ptgt, ConstCharRange dep,
bool inherit_deps) {
Rule *oldr = nullptr;
for (auto &rule: rules.iter())
if (ptgt == rule.target)
oldr = &rule;
if (!oldr)
return;
Rule &r = rules.push();
r.target = tgt;
r.action = oldr->action;
r.func = oldr->func;
2015-11-24 21:14:37 +01:00
r.deps = inherit_deps ? oldr->deps : cscript::util::list_explode(dep);
}
2015-08-21 21:02:31 +02:00
};
2015-08-14 03:54:00 +02:00
2015-11-09 19:51:55 +01:00
static ConstCharRange deffile = "obuild.cfg";
2015-08-31 15:47:39 +02:00
static int ob_print_help(ConstCharRange a0, ostd::Stream &os, int v) {
2015-11-09 19:51:55 +01:00
os.writeln("Usage: ", a0, " [options] [action]\n",
2015-08-24 05:54:03 +02:00
"Options:\n"
" -C DIRECTORY\tChange to DIRECTORY before running.\n",
2015-11-09 19:51:55 +01:00
" -f FILE\tSpecify the file to run (default: ", deffile, ").\n"
2015-08-24 05:54:03 +02:00
" -h\t\tPrint this message.\n"
2015-09-20 21:54:19 +02:00
" -j N\t\tSpecify the number of jobs to use (default: 1).\n"
2015-11-09 19:51:55 +01:00
" -e STR\tEvaluate a string instead of a file.\n"
2015-09-21 21:44:48 +02:00
" -E\t\tIgnore environment variables.");
2015-08-24 05:54:03 +02:00
return v;
}
2015-08-14 03:54:00 +02:00
int main(int argc, char **argv) {
2015-08-16 02:06:42 +02:00
ObState os;
2015-08-31 15:47:39 +02:00
ConstCharRange pn = argv[0];
ConstCharRange lslash = ostd::find_last(pn, '/');
2015-08-16 20:35:39 +02:00
if (!lslash.empty()) {
lslash.pop_front();
os.progname = lslash;
2015-10-31 20:43:39 +01:00
} else {
2015-08-16 20:35:39 +02:00
os.progname = pn;
2015-10-31 20:43:39 +01:00
}
2015-08-16 02:06:42 +02:00
cscript::init_lib_base(os.cs);
cscript::init_lib_io(os.cs);
cscript::init_lib_math(os.cs);
cscript::init_lib_string(os.cs);
cscript::init_lib_list(os.cs);
2015-09-26 16:13:17 +02:00
int ncpus = ostd::cpu_count_get();
2015-11-17 21:19:58 +01:00
os.cs.add_ident(cscript::ID_VAR, "numcpus", 4096, 1, &ncpus);
os.cs.add_ident(cscript::ID_VAR, "numjobs", 4096, 1, &os.jobs);
2015-09-26 16:13:17 +02:00
2015-09-20 21:54:19 +02:00
ConstCharRange fcont;
2015-08-24 05:54:03 +02:00
int ac;
2015-09-21 21:44:48 +02:00
while ((ac = getopt(argc, argv, "C:f:hj:e:E")) >= 0) {
2015-08-24 05:54:03 +02:00
switch (ac) {
case 'C':
2015-09-14 02:31:50 +02:00
if (!ostd::directory_change(optarg))
2015-08-24 05:54:03 +02:00
return os.error(1, "failed changing directory: %s", optarg);
break;
case 'f':
2015-11-09 19:51:55 +01:00
deffile = optarg;
2015-08-24 05:54:03 +02:00
break;
2015-09-20 21:54:19 +02:00
case 'e':
fcont = optarg;
break;
2015-08-24 05:54:03 +02:00
case 'h':
return ob_print_help(argv[0], ostd::out, 0);
2015-09-26 16:13:17 +02:00
case 'j': {
int val = atoi(optarg);
if (!val) val = ncpus;
2015-09-26 16:13:17 +02:00
os.jobs = ostd::max(1, val);
2015-08-24 05:54:03 +02:00
break;
2015-09-26 16:13:17 +02:00
}
2015-09-21 21:44:48 +02:00
case 'E':
2015-10-16 00:09:16 +02:00
os.ignore_env = true;
2015-09-21 21:44:48 +02:00
break;
2015-08-24 05:54:03 +02:00
default:
return ob_print_help(argv[0], ostd::err, 1);
}
}
2015-10-26 18:58:47 +01:00
tpool.init(os.jobs);
2015-08-24 20:16:55 +02:00
2015-10-13 22:23:41 +02:00
os.cs.add_command("shell", "C", [](CsState &cs, ConstCharRange s) {
2015-10-16 00:36:39 +02:00
auto cnt = ((ObState &)cs).counters.back();
2015-08-24 20:16:55 +02:00
cnt->incr();
2015-11-01 19:45:03 +01:00
String ds = s;
2015-11-01 19:47:31 +01:00
/* in c++14 we can use generalized lambda captures to move the str */
2015-10-26 18:58:47 +01:00
tpool.push([cnt, ds]() {
2015-11-01 19:45:03 +01:00
int ret = system(ds.data());
2015-08-24 20:16:55 +02:00
if (ret && !cnt->result)
cnt->result = ret;
cnt->decr();
});
cs.result->set_int(0);
2015-08-14 03:54:00 +02:00
});
2015-10-16 00:36:39 +02:00
os.cs.add_command("rule", "sseN", [](CsState &cs, const char *tgt,
2015-08-21 21:05:24 +02:00
const char *dep, ostd::Uint32 *body,
int *numargs) {
2015-10-16 00:36:39 +02:00
((ObState &)cs).rule_add(tgt, dep, (*numargs > 2) ? body : nullptr);
2015-08-21 21:05:24 +02:00
});
2015-08-14 03:54:00 +02:00
2015-10-26 18:58:47 +01:00
os.cs.add_command("action", "se", [](CsState &cs, const char *an,
ostd::Uint32 *body) {
((ObState &)cs).rule_add(an, nullptr, body, true);
});
os.cs.add_command("depend", "ss", [](CsState &cs, const char *file,
const char *deps) {
((ObState &)cs).rule_add(file, deps, nullptr);
});
2015-11-20 17:49:23 +01:00
os.cs.add_command("duprule", "sssN", [](CsState &cs, const char *tgt,
const char *ptgt, const char *dep,
int *numargs) {
2015-11-24 21:14:37 +01:00
((ObState &)cs).rule_dup(tgt, ptgt, dep, *numargs <= 2);
});
os.cs.add_commandn("getenv", "ss", [](CsState &cs, TvalRange args) {
2015-10-16 00:09:16 +02:00
if (((ObState &)cs).ignore_env) {
2015-09-21 21:44:48 +02:00
cs.result->set_cstr("");
return;
}
2015-10-18 01:01:11 +02:00
auto ret = ob_get_env(args[0].get_str());
if (ret.empty()) {
if (!args[1].get_str().empty())
cs.result->set_str_dup(args[1].get_str());
else
cs.result->set_cstr("");
} else {
2015-10-18 01:01:11 +02:00
cs.result->set_str_dup(ret);
}
2015-09-21 21:44:48 +02:00
});
2015-11-07 17:57:29 +01:00
os.cs.add_command("extreplace", "sss", [](cscript::CsState &cs,
const char *lst,
const char *oldext,
const char *newext) {
String ret;
if (oldext[0] == '.') ++oldext;
if (newext[0] == '.') ++newext;
auto fnames = cscript::util::list_explode(lst);
for (ConstCharRange it: fnames.iter()) {
if (!ret.empty()) ret += ' ';
auto dot = ostd::find_last(it, '.');
if (!dot.empty() && ((dot + 1) == oldext)) {
ret += ostd::slice_until(it, dot);
ret += '.';
ret += newext;
} else {
ret += it;
}
}
cs.result->set_str_dup(ret);
2015-11-07 17:57:29 +01:00
});
os.cs.add_command("invoke", "s", [](CsState &cs, const char *name) {
cs.result->set_int(((ObState &)cs).exec_main(name));
});
2015-09-10 19:55:49 +02:00
cs_register_globs(os.cs);
2015-08-23 10:03:30 +02:00
2015-11-09 19:51:55 +01:00
if ((!fcont.empty() && !os.cs.run_bool(fcont)) || !os.cs.run_file(deffile))
2015-08-20 21:59:15 +02:00
return os.error(1, "failed creating rules");
2015-08-16 02:06:42 +02:00
2015-10-16 00:36:39 +02:00
if (os.rules.empty())
2015-08-20 21:59:15 +02:00
return os.error(1, "no targets");
2015-08-16 02:06:42 +02:00
2015-10-27 19:49:46 +01:00
return os.exec_main((optind < argc) ? argv[optind] : "default");
2015-08-14 03:54:00 +02:00
}