difftastic/vendor/tree-sitter-hare/example/plan.ha

198 lines
4.0 KiB
Plaintext

use fmt;
use hare::ast;
use hare::module;
use os::exec;
use os;
use path;
use strings;
use temp;
type status = enum {
SCHEDULED,
COMPLETE,
SKIP,
};
type task = struct {
status: status,
depend: []*task,
output: str,
cmd: []str,
};
fn task_free(task: *task) void = {
free(task.depend);
free(task.output);
free(task.cmd);
free(task);
};
type modcache = struct {
hash: u32,
task: *task,
ident: ast::ident,
version: module::version,
};
type plan = struct {
context: *module::context,
workdir: str,
counter: uint,
scheduled: []*task,
complete: []*task,
script: str,
environ: [](str, str),
modmap: [64][]modcache,
};
fn mkplan(ctx: *module::context) plan = {
const rtdir = match (module::lookup(ctx, ["rt"])) {
err: module::error => fmt::fatal("Error resolving rt: {}",
module::strerror(err)),
ver: module::version => ver.basedir,
};
return plan {
context = ctx,
workdir = temp::dir(),
script = path::join(rtdir, "hare.sc"),
environ = alloc([
(strings::dup("HARECACHE"), strings::dup(ctx.cache)),
]),
...
};
};
fn plan_finish(plan: *plan) void = {
os::rmdirall(plan.workdir);
free(plan.workdir);
for (let i = 0z; i < len(plan.complete); i += 1) {
let task = plan.complete[i];
task_free(task);
};
free(plan.complete);
for (let i = 0z; i < len(plan.scheduled); i += 1) {
let task = plan.scheduled[i];
task_free(task);
};
free(plan.scheduled);
for (let i = 0z; i < len(plan.environ); i += 1) {
free(plan.environ[i].0);
free(plan.environ[i].1);
};
free(plan.environ);
for (let i = 0z; i < len(plan.modmap); i += 1) {
free(plan.modmap[i]);
};
};
fn plan_execute(plan: *plan, verbose: bool) void = {
for (len(plan.scheduled) != 0) {
let next: nullable *task = null;
let i = 0z;
for (i < len(plan.scheduled); i += 1) {
let task = plan.scheduled[i];
let eligible = true;
for (let j = 0z; j < len(task.depend); j += 1) {
if (task.depend[j].status == status::SCHEDULED) {
eligible = false;
break;
};
};
if (eligible) {
next = task;
break;
};
};
// TODO: This can be a type assertion
let task = match (next) {
null => abort(),
t: *task => t,
};
match (execute(plan, task, verbose)) {
err: exec::error => fmt::fatal("Error: {}: {}",
task.cmd[0], exec::strerror(err)),
err: exec::exit_status! => fmt::fatal("Error: {}: {}",
task.cmd[0], exec::exitstr(err)),
void => void,
};
task.status = status::COMPLETE;
delete(plan.scheduled[i]);
append(plan.complete, task);
};
update_modcache(plan);
};
fn update_cache(plan: *plan, mod: modcache) void = {
let manifest = module::manifest {
ident = mod.ident,
inputs = mod.version.inputs,
versions = [mod.version],
};
match (module::manifest_write(plan.context, &manifest)) {
err: module::error => fmt::fatal(
"Error updating module cache: {}",
module::strerror(err)),
void => void,
};
};
fn update_modcache(plan: *plan) void = {
for (let i = 0z; i < len(plan.modmap); i += 1) {
let mods = plan.modmap[i];
if (len(mods) == 0) {
continue;
};
for (let j = 0z; j < len(mods); j += 1) {
if (mods[j].task.status == status::COMPLETE) {
update_cache(plan, mods[j]);
};
};
};
};
fn execute(
plan: *plan,
task: *task,
verbose: bool,
) (void | exec::error | exec::exit_status!) = {
if (verbose) {
for (let i = 0z; i < len(task.cmd); i += 1) {
fmt::errorf("{} ", task.cmd[i]);
};
fmt::errorln();
};
let cmd = exec::cmd(task.cmd[0], task.cmd[1..]...)?;
for (let i = 0z; i < len(plan.environ); i += 1) {
let e = plan.environ[i];
exec::setenv(&cmd, e.0, e.1);
};
let proc = exec::start(&cmd)?;
let st = exec::wait(&proc)?;
return exec::check(&st);
};
fn mkfile(plan: *plan, ext: str) str = {
static let namebuf: [32]u8 = [0...];
const name = fmt::bsprintf(namebuf, "temp.{}.{}",
plan.counter, ext);
plan.counter += 1;
return path::join(plan.workdir, name);
};
fn mkdepends(t: *task...) []*task = {
// XXX: This should just be one alloc call
let deps: []*task = alloc([], len(t));
append(deps, ...t);
return deps;
};