OctaBuild/main.cc

289 lines
8.1 KiB
C++
Raw Permalink Normal View History

2017-01-24 00:32:16 +01:00
#include <utility>
2017-04-23 15:35:37 +02:00
#include <vector>
2017-06-08 19:17:16 +02:00
#include <stdexcept>
2017-01-24 00:32:16 +01:00
2015-08-16 02:06:42 +02:00
#include <ostd/string.hh>
2017-02-20 18:43:00 +01:00
#include <ostd/format.hh>
2018-04-18 11:03:50 +02:00
#include <ostd/path.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>
2016-03-18 22:54:24 +01:00
#include <ostd/environ.hh>
2017-05-24 22:09:28 +02:00
#include <ostd/argparse.hh>
2015-08-14 03:54:00 +02:00
2018-04-24 00:06:05 +02:00
#include <ostd/build/make.hh>
#include <ostd/build/make_coroutine.hh>
2018-04-24 00:06:05 +02:00
#include <cubescript/cubescript.hh>
2015-08-14 03:54:00 +02:00
2021-04-04 20:33:13 +02:00
namespace cs = cubescript;
2018-04-18 11:03:50 +02:00
namespace fs = ostd::fs;
2018-04-24 00:06:05 +02:00
namespace build = ostd::build;
2015-08-14 03:54:00 +02:00
2018-04-24 01:50:55 +02:00
static void rule_add(
2021-04-04 20:33:13 +02:00
cs::state &cs, build::make &mk,
std::string_view target, std::string_view depends,
cs::bcode_ref body, bool action = false
2018-04-24 01:50:55 +02:00
) {
2018-04-26 00:47:25 +02:00
build::make_rule::body_func bodyf{};
2021-04-04 20:33:13 +02:00
if (!body.empty()) {
bodyf = [body, &cs](auto tgt, auto srcs) {
auto ts = cs.new_thread();
2021-05-04 03:16:12 +02:00
cs::alias_local target{ts, "target"};
cs::alias_local source{ts, "source"};
cs::alias_local sources{ts, "sources"};
2021-04-04 20:33:13 +02:00
2021-05-04 03:16:12 +02:00
cs::any_value idv{};
idv.set_string(tgt, ts);
2021-04-04 20:33:13 +02:00
target.set(std::move(idv));
2018-04-24 01:50:55 +02:00
if (!srcs.empty()) {
2021-05-04 03:16:12 +02:00
idv.set_string(srcs[0], ts);
2021-04-04 20:33:13 +02:00
source.set(std::move(idv));
2015-11-25 21:36:46 +01:00
2018-04-24 01:50:55 +02:00
auto dsv = ostd::appender<std::string>();
ostd::format(dsv, "%(%s %)", srcs);
2021-05-04 03:16:12 +02:00
idv.set_string(dsv.get(), cs);
2021-04-04 20:33:13 +02:00
sources.set(std::move(idv));
2018-04-24 01:50:55 +02:00
}
2015-11-25 21:36:46 +01:00
2018-04-24 01:50:55 +02:00
try {
2021-05-04 03:16:12 +02:00
body.call(ts);
2021-04-04 20:33:13 +02:00
} catch (cs::error const &e) {
2018-04-24 01:50:55 +02:00
throw build::make_error{e.what()};
}
2018-04-26 00:47:25 +02:00
};
}
2021-04-04 20:33:13 +02:00
cs::list_parser p{cs, target};
while (p.parse()) {
cs::list_parser lp{cs, depends};
auto &r = mk.rule(
std::string_view{p.get_item()}
).action(action).body(bodyf);
while (lp.parse()) {
r.depend(std::string_view{lp.get_item()});
}
2017-05-24 22:09:28 +02:00
}
2018-04-24 01:50:55 +02:00
}
2017-05-24 22:09:28 +02:00
2021-04-04 20:33:13 +02:00
static void init_rulelib(cs::state &s, build::make &mk) {
2021-05-04 03:16:12 +02:00
s.new_command("rule", "ssb", [&mk](auto &css, auto args, auto &) {
2018-04-24 01:50:55 +02:00
rule_add(
2021-05-04 03:16:12 +02:00
css, mk, args[0].get_string(css),
args[1].get_string(css), args[2].get_code()
2018-04-24 01:50:55 +02:00
);
});
2017-05-25 00:04:10 +02:00
2021-05-04 03:16:12 +02:00
s.new_command("action", "sb", [&mk](auto &css, auto args, auto &) {
2018-04-24 01:50:55 +02:00
rule_add(
2021-05-04 03:16:12 +02:00
css, mk, args[0].get_string(css), std::string_view{},
2021-04-04 20:33:13 +02:00
args[1].get_code(), true
2018-04-24 01:50:55 +02:00
);
});
2016-08-22 19:44:21 +02:00
2021-04-04 20:33:13 +02:00
s.new_command("depend", "ss", [&mk](auto &css, auto args, auto &) {
2021-05-04 03:16:12 +02:00
rule_add(
css, mk, args[0].get_string(css), args[1].get_string(css),
cs::bcode_ref{}
);
2018-04-24 01:50:55 +02:00
});
}
2015-11-25 21:36:46 +01:00
2021-04-04 20:33:13 +02:00
static void init_baselib(cs::state &s, build::make &mk, bool ignore_env) {
2021-05-04 03:16:12 +02:00
s.new_command("echo", "...", [](auto &css, auto args, auto &) {
ostd::writeln(cs::concat_values(css, args, " ").view());
2016-09-07 18:59:22 +02:00
});
2021-05-04 03:16:12 +02:00
s.new_command("shell", "...", [&mk](auto &css, auto args, auto &) {
mk.push_task([
ds = std::string{cs::concat_values(css, args, " ").view()}
]() {
2017-06-08 19:17:16 +02:00
if (system(ds.data())) {
2018-04-24 00:06:05 +02:00
throw build::make_error{""};
2017-06-08 19:17:16 +02:00
}
2017-06-08 19:49:17 +02:00
});
2015-08-14 03:54:00 +02:00
});
2021-05-04 03:16:12 +02:00
s.new_command("getenv", "ss", [ignore_env](auto &css, auto args, auto &res) {
2018-04-24 00:06:05 +02:00
if (ignore_env) {
2021-05-04 03:16:12 +02:00
res.set_string("", css);
2015-09-21 21:44:48 +02:00
return;
}
2021-05-04 03:16:12 +02:00
res.set_string(ostd::env_get(
std::string_view{args[0].get_string(css)}
).value_or(
std::string{std::string_view{args[1].get_string(css)}}
), css);
2015-09-21 21:44:48 +02:00
});
2021-05-04 03:16:12 +02:00
s.new_command("invoke", "s", [&mk](auto &css, auto args, auto &) {
mk.exec(std::string_view{args[0].get_string(css)});
2018-04-24 01:50:55 +02:00
});
}
2021-04-04 20:33:13 +02:00
static void init_pathlib(cs::state &s) {
s.new_command("extreplace", "sss", [](auto &css, auto args, auto &res) {
2021-05-04 03:16:12 +02:00
ostd::string_range oldext = std::string_view{args[1].get_string(css)};
ostd::string_range newext = std::string_view{args[2].get_string(css)};
2017-01-30 01:18:44 +01:00
std::string ret;
2021-05-04 03:16:12 +02:00
cs::list_parser p{css, args[0].get_string(css)};
2021-04-04 20:33:13 +02:00
while (p.parse()) {
ostd::path np{std::string_view{p.get_item()}};
2016-08-01 01:09:29 +02:00
if (!ret.empty()) {
ret += ' ';
}
2018-04-24 22:18:23 +02:00
ret += (
(np.suffixes() == oldext) ? np.with_suffixes(newext) : np
).string();
2015-11-07 17:57:29 +01:00
}
2021-05-04 03:16:12 +02:00
res.set_string(ret, css);
2015-11-07 17:57:29 +01:00
});
2021-05-04 03:16:12 +02:00
s.new_command("glob", "...", [](auto &css, auto args, auto &res) {
2021-04-04 20:33:13 +02:00
auto app = ostd::appender<std::vector<ostd::path>>();
2021-05-04 03:16:12 +02:00
cs::list_parser p{css, cs::concat_values(css, args, " ")};
2021-04-04 20:33:13 +02:00
while (p.parse()) {
auto it = p.get_item();
fs::glob_match(app, std::string_view{it});
2016-10-23 19:31:48 +02:00
}
2021-05-04 03:16:12 +02:00
res.set_string(ostd::format(
2018-04-26 00:35:44 +02:00
ostd::appender<std::string>(), "%(%s %)", app.get()
2021-05-04 03:16:12 +02:00
).get(), css);
2016-08-02 23:37:26 +02:00
});
2018-04-24 01:50:55 +02:00
}
2021-04-04 20:33:13 +02:00
static bool do_run_file(cs::state &s, std::string_view fname) {
ostd::file_stream f{fname};
if (!f.is_open()) {
return false;
}
f.seek(0, ostd::stream_seek::END);
auto len = f.tell();
f.seek(0);
auto buf = std::make_unique<char[]>(len + 1);
if (!buf) {
return false;
}
if (f.read_bytes(buf.get(), len) != std::size_t(len)) {
return false;
}
buf[len] = '\0';
2021-05-04 03:16:12 +02:00
s.compile(std::string_view{buf.get(), std::size_t(len)}, fname).call(s);
2021-04-04 20:33:13 +02:00
return true;
}
2018-04-24 01:50:55 +02:00
void do_main(int argc, char **argv) {
/* cubescript interpreter */
2021-04-04 20:33:13 +02:00
cs::state s;
2021-05-04 03:16:12 +02:00
cs::std_init_all(s);
2018-04-24 01:50:55 +02:00
/* arg values */
std::string action = "default";
std::string deffile = "obuild.cfg";
std::string curdir;
std::string fcont;
bool ignore_env = false;
int jobs = 1;
/* input options */
{
ostd::arg_parser ap;
auto &help = ap.add_optional("-h", "--help", 0)
.help("print this message and exit")
.action(ostd::arg_print_help(ap));
ap.add_optional("-j", "--jobs", 1)
.help("specify the number of jobs to use (default: 1)")
.action(ostd::arg_store_format("%d", jobs));
ap.add_optional("-C", "--change-directory", 1)
.help("change to DIRECTORY before running")
.metavar("DIRECTORY")
.action(ostd::arg_store_str(curdir));
ap.add_optional("-f", "--file", 1)
.help("specify the file to run (default: obuild.cfg)")
.action(ostd::arg_store_str(deffile));
ap.add_optional("-e", "--execute", 1)
.help("evaluate a string instead of a file")
.metavar("STR")
.action(ostd::arg_store_str(fcont));
ap.add_optional("-E", "--ignore-env", 0)
.help("ignore environment variables")
.action(ostd::arg_store_true(ignore_env));
ap.add_positional("action", ostd::arg_value::OPTIONAL)
.help("the action to perform")
.action(ostd::arg_store_str(action));
try {
ap.parse(argc, argv);
} catch (ostd::arg_error const &e) {
ostd::cerr.writefln("%s: %s", argv[0], e.what());
ap.print_help(ostd::cerr.iter());
throw build::make_error{""};
}
if (help.used()) {
return;
}
}
int ncpus = std::thread::hardware_concurrency();
jobs = std::max(1, jobs ? jobs : ncpus);
/* core cubescript variables */
2021-05-04 03:16:12 +02:00
s.new_var("numcpus", ncpus, true);
s.new_var("numjobs", jobs, true);
2018-04-24 01:50:55 +02:00
/* switch to target directory */
try {
if (!curdir.empty()) {
fs::current_path(curdir);
}
} catch (fs::fs_error const &e) {
throw build::make_error{
"failed changing directory: %s (%s)", curdir, e.what()
};
}
/* init buildsystem, use coroutine tasks */
build::make mk{build::make_task_coroutine, jobs};
2018-04-24 01:50:55 +02:00
/* octabuild cubescript libs */
2021-04-04 20:33:13 +02:00
init_rulelib(s, mk);
init_baselib(s, mk, ignore_env);
init_pathlib(s);
2015-08-23 10:03:30 +02:00
2018-04-24 01:50:55 +02:00
/* parse rules */
2021-04-04 20:33:13 +02:00
if ((
2021-05-04 03:16:12 +02:00
!fcont.empty() && !s.compile(fcont).call(s).get_bool()
2021-04-04 20:33:13 +02:00
) || !do_run_file(s, deffile)) {
2018-04-24 00:06:05 +02:00
throw build::make_error{"failed creating rules"};
2016-08-01 01:09:29 +02:00
}
2015-08-16 02:06:42 +02:00
2018-04-24 01:50:55 +02:00
/* make */
2018-04-24 00:06:05 +02:00
mk.exec(action);
2017-06-08 19:17:16 +02:00
}
int main(int argc, char **argv) {
try {
do_main(argc, argv);
2018-04-24 00:06:05 +02:00
} catch (build::make_error const &e) {
2017-06-08 19:17:16 +02:00
auto s = e.what();
if (s[0]) {
ostd::cerr.writefln("%s: %s", argv[0], s);
}
return 1;
}
return 0;
2016-02-07 22:20:28 +01:00
}