OctaBuild/main.cc

511 lines
15 KiB
C++
Raw Normal View History

2015-08-14 03:54:00 +02:00
#include <sys/stat.h>
2015-08-23 10:03:30 +02:00
#include <dirent.h>
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>
2015-08-24 20:16:55 +02:00
#include <ostd/atomic.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::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-08-22 09:31:33 +02:00
/* represents a rule definition, possibly with a function */
2015-08-14 03:54:00 +02:00
struct Rule {
2015-08-31 15:47:39 +02:00
String target;
Vector<String> deps;
2015-09-01 19:50:00 +02:00
Uint32 *func;
2015-08-14 03:54:00 +02:00
Rule(): target(), deps(), func(nullptr) {}
Rule(const Rule &r): target(r.target), deps(r.deps), func(r.func) {
cscript::bcode_ref(func);
}
~Rule() { cscript::bcode_unref(func); }
};
2015-08-31 15:47:39 +02:00
Vector<Rule> rules;
2015-08-20 21:59:15 +02:00
2015-08-24 20:16:55 +02:00
struct RuleCounter;
2015-08-31 15:47:39 +02:00
Vector<RuleCounter *> counters;
2015-08-24 20:16:55 +02:00
struct RuleCounter {
RuleCounter(): cond(), mtx(), counter(0), result(0) {
counters.push(this);
}
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();
}
int wait_result(int ret) {
counters.pop();
if (ret)
return ret;
wait();
ret = result;
if (ret)
return ret;
return 0;
}
Cond cond;
Mutex mtx;
volatile int counter;
ostd::AtomicInt result;
};
ThreadPool tpool;
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-08-22 09:31:33 +02:00
struct stat st;
2015-08-31 15:47:39 +02:00
if (stat(String(fname).data(), &st) < 0)
2015-08-22 09:31:33 +02:00
return 0;
return st.st_mtime;
};
time_t tts = get_ts(tname.data());
2015-08-14 03:54:00 +02:00
if (!tts) return true;
for (auto &dep: deps.iter()) {
2015-08-22 09:31:33 +02:00
time_t sts = get_ts(dep.data());
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-08-31 15:47:39 +02: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-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-08-14 03:54:00 +02:00
if (expanded.slice(expanded.size() - rep.size(), expanded.size()) != 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-08-23 10:03:30 +02:00
/* expand globs */
2015-08-31 15:47:39 +02:00
static void ob_get_path_parts(Vector<ConstCharRange> &parts,
ConstCharRange elem) {
2015-09-01 19:50:00 +02:00
ConstCharRange star = ostd::find(elem, '*');
2015-08-23 20:54:23 +02:00
while (!star.empty()) {
2015-09-01 19:50:00 +02:00
ConstCharRange ep = slice_until(elem, star);
2015-08-23 20:54:23 +02:00
if (!ep.empty())
parts.push(ep);
parts.push("*");
elem = star;
elem.pop_front();
star = ostd::find(elem, '*');
}
if (!elem.empty())
parts.push(elem);
}
2015-08-31 15:47:39 +02:00
static bool ob_path_matches(ConstCharRange fn,
const Vector<ConstCharRange> &parts) {
2015-08-23 20:54:23 +02:00
auto it = parts.iter();
while (!it.empty()) {
2015-08-31 15:47:39 +02:00
ConstCharRange elem = it.front();
2015-08-23 20:54:23 +02:00
if (elem == "*") {
it.pop_front();
/* skip multiple stars if present */
while (!it.empty() && ((elem = it.front()) == "*"))
it.pop_front();
/* 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.pop_front();
}
}
/* non-star here */
if (fn.size() < elem.size())
return false;
if (fn.slice(0, elem.size()) != elem)
return false;
fn.pop_front_n(elem.size());
/* try next element */
it.pop_front();
}
/* if there are no chars in the fname remaining, we fully matched */
return fn.empty();
2015-08-23 10:03:30 +02:00
}
2015-08-31 15:47:39 +02:00
static bool ob_expand_glob(String &ret, ConstCharRange src, bool ne = false);
2015-08-23 10:03:30 +02:00
2015-08-31 15:47:39 +02:00
static bool ob_expand_dir(String &ret, ConstCharRange dir,
const Vector<ConstCharRange> &parts,
ConstCharRange slash) {
DIR *d = opendir(String(dir).data());
2015-08-23 10:03:30 +02:00
bool appended = false;
if (!d)
return false;
struct dirent *dirp;
while ((dirp = readdir(d))) {
2015-08-31 15:47:39 +02:00
ConstCharRange fn = (const char *)dirp->d_name;
2015-08-23 10:03:30 +02:00
if (fn.empty() || (fn == ".") || (fn == ".."))
continue;
2015-08-23 20:54:23 +02:00
/* check if filename matches */
if (!ob_path_matches(fn, parts))
continue;
2015-08-31 15:47:39 +02:00
String afn((dir == ".") ? "" : "./");
2015-08-23 10:03:30 +02:00
afn.append(fn);
/* if we reach this, we match; try recursively matching */
if (!slash.empty()) {
afn.append(slash);
2015-08-31 15:47:39 +02:00
ConstCharRange psl = slash;
2015-08-23 10:03:30 +02:00
psl.pop_front();
if (!ostd::find(psl, '*').empty()) {
if (!appended)
appended = ob_expand_glob(ret, afn.iter());
continue;
}
/* no further star, just do file test */
if (!ob_check_file(afn.iter()))
continue;
if (!ret.empty())
ret.push(' ');
ret.append(afn);
appended = true;
continue;
}
if (!ret.empty())
ret.push(' ');
ret.append(afn);
appended = true;
}
closedir(d);
return appended;
}
2015-08-31 15:47:39 +02:00
static bool ob_expand_glob(String &ret, ConstCharRange src, bool ne) {
ConstCharRange star = ostd::find(src, '*');
2015-08-23 20:54:23 +02:00
/* no star use as-is */
if (star.empty()) {
2015-08-23 10:03:30 +02:00
if (ne) return false;
if (!ret.empty())
ret.push(' ');
ret.append(src);
return false;
}
/* part before star */
2015-09-01 19:50:00 +02:00
ConstCharRange prestar = slice_until(src, star);
2015-08-23 10:03:30 +02:00
/* try finding slash before star */
2015-08-31 15:47:39 +02:00
ConstCharRange slash = ostd::find_last(prestar, '/');
2015-08-23 10:03:30 +02:00
/* directory to scan */
2015-08-31 15:47:39 +02:00
ConstCharRange dir = ".";
2015-08-23 10:03:30 +02:00
/* part of name before star */
2015-08-31 15:47:39 +02:00
ConstCharRange fnpre = prestar;
2015-08-23 10:03:30 +02:00
if (!slash.empty()) {
/* there was slash, adjust directory + prefix accordingly */
2015-09-01 19:50:00 +02:00
dir = slice_until(src, slash);
2015-08-23 10:03:30 +02:00
fnpre = slash;
fnpre.pop_front();
}
/* part after star */
2015-08-31 15:47:39 +02:00
ConstCharRange fnpost = star;
2015-08-23 10:03:30 +02:00
fnpost.pop_front();
/* if a slash follows, adjust */
2015-08-31 15:47:39 +02:00
ConstCharRange nslash = ostd::find(fnpost, '/');
2015-08-23 10:03:30 +02:00
if (!nslash.empty())
2015-09-01 19:50:00 +02:00
fnpost = slice_until(fnpost, nslash);
2015-08-23 20:54:23 +02:00
/* retrieve the single element with whatever stars in it, chop it up */
2015-08-31 15:47:39 +02:00
Vector<ConstCharRange> parts;
ob_get_path_parts(parts, ConstCharRange(&fnpre[0], &fnpost[fnpost.size()]));
2015-08-23 10:03:30 +02:00
/* do a directory scan and match */
2015-08-23 20:54:23 +02:00
if (!ob_expand_dir(ret, dir, parts, nslash)) {
2015-08-23 10:03:30 +02:00
if (ne) return false;
if (!ret.empty())
ret.push(' ');
ret.append(src);
return false;
}
return true;
}
2015-08-31 15:47:39 +02:00
static String ob_expand_globs(const Vector<String> &src) {
String ret;
2015-08-23 10:03:30 +02:00
for (auto &s: src.iter())
ob_expand_glob(ret, s.iter());
return ret;
}
2015-08-21 21:02:31 +02:00
struct ObState {
cscript::CsState cs;
2015-08-31 15:47:39 +02:00
ConstCharRange progname;
2015-08-24 05:54:03 +02:00
int jobs = 1;
2015-08-21 21:02:31 +02:00
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;
};
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-08-21 21:02:31 +02:00
for (auto &sr: rlist.iter()) for (auto &target: sr.rule->deps.iter()) {
2015-08-31 15:47:39 +02:00
ConstCharRange atgt = target.iter();
2015-08-21 21:02:31 +02:00
repd.clear();
auto lp = ostd::find(atgt, '%');
if (!lp.empty()) {
2015-09-01 19:50:00 +02:00
repd.append(slice_until(atgt, lp));
2015-08-21 21:02:31 +02:00
repd.append(sr.sub);
lp.pop_front();
if (!lp.empty()) repd.append(lp);
atgt = repd.iter();
}
subdeps.push(atgt);
int r = exec_rule(atgt, tname);
if (r) return r;
2015-08-14 03:54:00 +02:00
}
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-08-24 20:16:55 +02:00
/* new scope for early destruction */
{
RuleCounter depcnt;
int r = depcnt.wait_result(exec_list(rlist, subdeps, tname));
if (r)
return r;
}
2015-08-21 21:02:31 +02:00
if (ob_check_exec(tname, subdeps)) {
2015-09-01 19:50:00 +02:00
Uint32 *func = nullptr;
2015-08-21 21:02:31 +02:00
for (auto &sr: rlist.iter()) {
Rule &r = *sr.rule;
if (!r.func)
continue;
if (func)
return error(1, "redefinition of rule '%s'", tname);
func = r.func;
}
if (func) {
cscript::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))
return 1;
sourcev.set_cstr(subdeps[0]);
sourcev.push();
2015-08-31 15:47:39 +02:00
auto dsv = ostd::appender<String>();
2015-08-21 21:02:31 +02:00
ostd::concat(dsv, subdeps);
ostd::Size len = dsv.size();
sourcesv.set_str(ostd::CharRange(dsv.get().disown(), len));
sourcesv.push();
}
return cs.run_int(func);
}
2015-08-14 03:54:00 +02:00
}
2015-08-21 21:02:31 +02:00
return 0;
2015-08-14 03:54:00 +02:00
}
2015-08-21 21:02:31 +02:00
2015-08-31 15:47:39 +02:00
int exec_rule(ConstCharRange target, ConstCharRange from = nullptr) {
Vector<SubRule> rlist;
2015-08-21 21:02:31 +02:00
for (auto &rule: rules.iter()) {
if (target == rule.target) {
rlist.push().rule = &rule;
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 (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-08-21 21:02:31 +02:00
};
2015-08-14 03:54:00 +02:00
2015-08-31 15:47:39 +02:00
static int ob_print_help(ConstCharRange a0, ostd::Stream &os, int v) {
2015-08-24 05:54:03 +02:00
os.writeln("Usage: ", a0, " [options] [target]\n",
"Options:\n"
" -C DIRECTORY\tChange to DIRECTORY before running.\n",
" -f FILE\tSpecify the file to run (default: cubefile).\n"
" -h\t\tPrint this message.\n"
" -j N\t\tSpecify the number of jobs to use (default: 1).");
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;
} else
os.progname = pn;
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-08-31 15:47:39 +02:00
ConstCharRange fname = "cubefile";
2015-08-24 05:54:03 +02:00
int ac;
while ((ac = getopt(argc, argv, "C:f:hj:")) >= 0) {
switch (ac) {
case 'C':
if (chdir(optarg) < 0)
return os.error(1, "failed changing directory: %s", optarg);
break;
case 'f':
fname = optarg;
break;
case 'h':
return ob_print_help(argv[0], ostd::out, 0);
case 'j':
os.jobs = ostd::max(1, atoi(optarg));
break;
default:
return ob_print_help(argv[0], ostd::err, 1);
break;
}
}
2015-08-24 20:16:55 +02:00
tpool.init(os.jobs);
2015-08-31 15:47:39 +02:00
os.cs.add_command("shell", "C", [](cscript::CsState &cs, ConstCharRange s) {
2015-08-24 20:16:55 +02:00
RuleCounter *cnt = counters.back();
cnt->incr();
2015-08-31 15:47:39 +02:00
char *ds = String(s).disown();
2015-08-25 09:19:44 +02:00
tpool.push([cnt, ds]() {
int ret = system(ds);
delete[] ds;
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-08-21 21:05:24 +02:00
os.cs.add_command("rule", "sseN", [](cscript::CsState &, const char *tgt,
const char *dep, ostd::Uint32 *body,
int *numargs) {
2015-08-31 15:47:39 +02:00
auto targets = cscript::util::list_explode(tgt);
auto deps = cscript::util::list_explode(dep);
2015-08-21 21:05:24 +02:00
for (auto &target: targets.iter()) {
Rule &r = rules.push();
r.target = target;
if (*numargs > 2) {
r.func = body;
cscript::bcode_ref(body);
}
for (auto &dep: deps.iter())
r.deps.push(dep);
}
});
2015-08-14 03:54:00 +02:00
2015-08-31 15:47:39 +02:00
os.cs.add_command("glob", "C", [](cscript::CsState &cs, ConstCharRange lst) {
auto fnames = cscript::util::list_explode(lst);
2015-08-23 10:03:30 +02:00
cs.result->set_str(ob_expand_globs(fnames).disown());
});
2015-08-24 05:54:03 +02:00
if (!os.cs.run_file(fname, true))
2015-08-20 21:59:15 +02:00
return os.error(1, "failed creating rules");
2015-08-16 02:06:42 +02:00
if (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-08-24 20:16:55 +02:00
RuleCounter maincnt;
int ret = os.exec_rule((optind < argc) ? argv[optind] : "all");
ret = maincnt.wait_result(ret);
if (ret)
return ret;
tpool.destroy();
return 0;
2015-08-14 03:54:00 +02:00
}