OctaBuild/main.cc

270 lines
7.8 KiB
C++
Raw 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 <cubescript/cubescript.hh>
2015-08-14 03:54:00 +02:00
2017-02-16 20:05:00 +01:00
using ostd::string_range;
2018-04-18 11:03:50 +02:00
using ostd::path;
2015-08-31 15:47:39 +02:00
2017-02-13 18:14:19 +01:00
using cscript::cs_state;
using cscript::cs_value_r;
using cscript::cs_value;
using cscript::cs_stacked_value;
using cscript::cs_bcode_ref;
using cscript::cs_bcode;
2015-10-13 22:23:41 +02:00
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
2017-05-07 16:38:53 +02:00
struct ob_state: cs_state {
2018-04-24 00:06:05 +02:00
void rule_add(
build::make &mk, string_range tgt, string_range dep, cs_bcode *body,
bool action = false
2016-08-01 01:09:29 +02:00
) {
2018-04-24 00:06:05 +02:00
cscript::util::ListParser p{*this, tgt};
while (p.parse()) {
auto &rl = mk.rule(p.get_item()).action(action);
cscript::util::ListParser lp{*this, dep};
while (lp.parse()) {
rl.depend(lp.get_item());
2016-08-01 01:09:29 +02:00
}
2018-04-24 00:06:05 +02:00
if (cscript::cs_code_is_empty(body)) {
continue;
2016-08-01 01:09:29 +02:00
}
2018-04-24 00:06:05 +02:00
rl.body([body = cs_bcode_ref(body), this](auto tgt, auto srcs) {
cs_stacked_value targetv, sourcev, sourcesv;
2018-04-24 00:06:05 +02:00
if (!targetv.set_alias(new_ident("target"))) {
throw build::make_error{
"internal error: could not set alias 'target'"
2017-06-08 19:17:16 +02:00
};
2016-08-01 01:09:29 +02:00
}
2018-04-24 00:06:05 +02:00
targetv.set_cstr(tgt);
targetv.push();
if (!srcs.empty()) {
if (!sourcev.set_alias(new_ident("source"))) {
throw build::make_error{
"internal error: could not set alias 'source'"
};
2016-08-01 01:09:29 +02:00
}
2018-04-24 00:06:05 +02:00
if (!sourcesv.set_alias(new_ident("sources"))) {
throw build::make_error{
"internal error: could not set alias 'sources'"
};
}
2015-10-16 00:36:39 +02:00
2018-04-24 00:06:05 +02:00
sourcev.set_cstr(srcs[0]);
sourcev.push();
2015-10-21 22:09:21 +02:00
2018-04-24 00:06:05 +02:00
auto dsv = ostd::appender<std::string>();
ostd::format(dsv, "%(%s %)", srcs);
sourcesv.set_str(std::move(dsv.get()));
sourcesv.push();
}
2018-04-24 00:06:05 +02:00
try {
this->run(body);
} catch (cscript::cs_error const &e) {
throw build::make_error{e.what()};
}
});
2016-10-08 14:38:07 +02:00
}
}
2015-11-25 21:36:46 +01:00
2018-04-24 00:06:05 +02:00
void register_rulecmds(build::make &mk) {
new_command("rule", "sse", [this, &mk](auto &, auto args, auto &) {
2017-01-24 00:07:41 +01:00
this->rule_add(
2018-04-24 00:06:05 +02:00
mk, 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
});
2018-04-24 00:06:05 +02:00
new_command("action", "se", [this, &mk](auto &, auto args, auto &) {
2017-01-24 00:07:41 +01:00
this->rule_add(
2018-04-24 00:06:05 +02:00
mk, args[0].get_strr(), nullptr, args[1].get_code(), true
2017-01-24 00:07:41 +01:00
);
2015-11-25 21:36:46 +01:00
});
2018-04-24 00:06:05 +02:00
new_command("depend", "ss", [this, &mk](auto &, auto args, auto &) {
this->rule_add(mk, args[0].get_strr(), args[1].get_strr(), nullptr);
2015-11-25 21:36:46 +01:00
});
}
2015-12-03 20:09:44 +01:00
};
2015-08-24 05:54:03 +02:00
2017-06-08 19:17:16 +02:00
void do_main(int argc, char **argv) {
2017-05-07 16:38:53 +02:00
ob_state os;
2016-08-18 20:47:46 +02:00
os.init_libs();
2015-08-16 02:06:42 +02:00
2018-04-24 00:06:05 +02:00
bool ignore_env = false;
2017-01-24 00:32:16 +01:00
int ncpus = std::thread::hardware_concurrency();
2016-09-02 19:03:42 +02:00
os.new_ivar("numcpus", 4096, 1, ncpus);
2015-09-26 16:13:17 +02:00
2017-06-08 19:17:16 +02:00
ostd::arg_parser ap;
2015-08-24 05:54:03 +02:00
2017-05-24 22:09:28 +02:00
auto &help = ap.add_optional("-h", "--help", 0)
.help("print this message and exit")
.action(ostd::arg_print_help(ap));
int jobs = 1;
ap.add_optional("-j", "--jobs", 1)
.help("specify the number of jobs to use (default: 1)")
.action(ostd::arg_store_format("%d", jobs));
std::string curdir;
ap.add_optional("-C", "--change-directory", 1)
.help("change to DIRECTORY before running")
.metavar("DIRECTORY")
2017-05-25 00:04:10 +02:00
.action(ostd::arg_store_str(curdir));
2017-05-24 22:09:28 +02:00
std::string deffile = "obuild.cfg";
ap.add_optional("-f", "--file", 1)
.help("specify the file to run (default: obuild.cfg)")
.action(ostd::arg_store_str(deffile));
std::string fcont;
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")
2018-04-24 00:06:05 +02:00
.action(ostd::arg_store_true(ignore_env));
2017-05-24 22:09:28 +02:00
std::string action = "default";
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) {
2017-06-14 21:43:39 +02:00
ostd::cerr.writefln("%s: %s", argv[0], e.what());
2017-05-24 22:09:28 +02:00
ap.print_help(ostd::cerr.iter());
2018-04-24 00:06:05 +02:00
throw build::make_error{""};
2017-05-24 22:09:28 +02:00
}
if (help.used()) {
2017-06-08 19:17:16 +02:00
return;
2015-08-24 05:54:03 +02:00
}
2017-05-24 22:09:28 +02:00
if (!jobs) {
jobs = ncpus;
}
jobs = std::max(1, jobs);
2017-05-25 00:04:10 +02:00
try {
if (!curdir.empty()) {
fs::current_path(curdir);
}
2018-04-18 11:03:50 +02:00
} catch (fs::fs_error const &e) {
2018-04-24 00:06:05 +02:00
throw build::make_error{
2017-06-08 19:17:16 +02:00
"failed changing directory: %s (0s)", curdir, e.what()
};
2017-05-25 00:04:10 +02:00
}
2016-09-02 19:03:42 +02:00
os.new_ivar("numjobs", 4096, 1, jobs);
2016-08-22 19:44:21 +02:00
2018-04-24 00:06:05 +02:00
build::make mk{build::make_task_simple, jobs};
os.register_rulecmds(mk);
2015-11-25 21:36:46 +01:00
2016-10-16 22:00:07 +02:00
os.new_command("echo", "C", [](auto &, auto args, auto &) {
2016-09-20 22:12:19 +02:00
writeln(args[0].get_strr());
2016-09-07 18:59:22 +02:00
});
2018-04-24 00:06:05 +02:00
os.new_command("shell", "C", [&mk](auto &, auto args, auto &) {
mk.push_task([ds = std::string(args[0].get_strr())]() {
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
});
2018-04-24 00:06:05 +02:00
os.new_command("getenv", "ss", [ignore_env](auto &, auto args, auto &res) {
if (ignore_env) {
2016-08-11 19:14:42 +02:00
res.set_cstr("");
2015-09-21 21:44:48 +02:00
return;
}
2017-01-24 00:32:16 +01:00
res.set_str(std::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-10-16 22:00:07 +02:00
os.new_command("extreplace", "sss", [](auto &cs, auto args, auto &res) {
2017-02-16 20:05:00 +01:00
string_range lst = args[0].get_strr();
string_range oldext = args[1].get_strr();
string_range newext = args[2].get_strr();
2017-01-30 01:18:44 +01:00
std::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
}
2016-10-23 19:31:48 +02:00
cscript::util::ListParser p{cs, lst};
while (p.parse()) {
2017-01-24 00:07:41 +01:00
auto elem = p.get_item();
2017-02-16 20:05:00 +01:00
string_range it = ostd::iter(elem);
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, '.');
2017-04-01 01:02:48 +02:00
if (!dot.empty() && (dot.slice(1, dot.size()) == oldext)) {
2017-03-31 03:18:55 +02:00
ret += it.slice(0, &dot[0] - &it[0]);
2015-11-07 17:57:29 +01:00
ret += '.';
ret += newext;
} else {
ret += it;
}
}
2017-01-24 00:32:16 +01:00
res.set_str(std::move(ret));
2015-11-07 17:57:29 +01:00
});
2018-04-24 00:06:05 +02:00
os.new_command("invoke", "s", [&mk](auto &, auto args, auto &) {
mk.exec(args[0].get_strr());
});
2018-04-18 11:03:50 +02:00
os.new_command("glob", "C", [](auto &cs, auto args, auto &res) {
2017-06-10 21:18:38 +02:00
auto ret = ostd::appender<std::string>();
2018-04-18 11:03:50 +02:00
auto app = ostd::appender<std::vector<path>>();
2016-10-23 19:31:48 +02:00
cscript::util::ListParser p{cs, args[0].get_strr()};
while (p.parse()) {
2018-04-18 11:03:50 +02:00
fs::glob_match(app, p.get_item());
2016-10-23 19:31:48 +02:00
}
2017-06-10 21:18:38 +02:00
ostd::format(ret, "%(%s %)", app.get());
res.set_str(std::move(ret.get()));
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)) {
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 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
}