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>
|
|
|
|
|
2016-09-10 23:10:23 +02:00
|
|
|
#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
|
|
|
|
2018-04-24 01:50:55 +02:00
|
|
|
static void rule_add(
|
|
|
|
cs_state &cs, build::make &mk,
|
|
|
|
string_range target, string_range depends,
|
|
|
|
cs_bcode *body, bool action = false
|
|
|
|
) {
|
|
|
|
cscript::util::ListParser p{cs, target};
|
|
|
|
while (p.parse()) {
|
|
|
|
auto &rl = mk.rule(p.get_item()).action(action);
|
|
|
|
cscript::util::ListParser lp{cs, depends};
|
|
|
|
while (lp.parse()) {
|
|
|
|
rl.depend(lp.get_item());
|
|
|
|
}
|
|
|
|
if (cscript::cs_code_is_empty(body)) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
rl.body([body = cs_bcode_ref(body), &cs](auto tgt, auto srcs) {
|
|
|
|
cs_stacked_value targetv, sourcev, sourcesv;
|
|
|
|
|
|
|
|
if (!targetv.set_alias(cs.new_ident("target"))) {
|
|
|
|
throw build::make_error{
|
|
|
|
"internal error: could not set alias 'target'"
|
|
|
|
};
|
2016-08-01 01:09:29 +02:00
|
|
|
}
|
2018-04-24 01:50:55 +02:00
|
|
|
targetv.set_cstr(tgt);
|
|
|
|
targetv.push();
|
2015-11-21 19:30:11 +01:00
|
|
|
|
2018-04-24 01:50:55 +02:00
|
|
|
if (!srcs.empty()) {
|
|
|
|
if (!sourcev.set_alias(cs.new_ident("source"))) {
|
2018-04-24 00:06:05 +02:00
|
|
|
throw build::make_error{
|
2018-04-24 01:50:55 +02:00
|
|
|
"internal error: could not set alias 'source'"
|
2017-06-08 19:17:16 +02:00
|
|
|
};
|
2016-08-01 01:09:29 +02:00
|
|
|
}
|
2018-04-24 01:50:55 +02:00
|
|
|
if (!sourcesv.set_alias(cs.new_ident("sources"))) {
|
|
|
|
throw build::make_error{
|
|
|
|
"internal error: could not set alias 'sources'"
|
|
|
|
};
|
2018-04-24 00:06:05 +02:00
|
|
|
}
|
2015-11-25 21:36:46 +01:00
|
|
|
|
2018-04-24 01:50:55 +02:00
|
|
|
sourcev.set_cstr(srcs[0]);
|
|
|
|
sourcev.push();
|
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);
|
|
|
|
sourcesv.set_str(std::move(dsv.get()));
|
|
|
|
sourcesv.push();
|
|
|
|
}
|
2015-11-25 21:36:46 +01:00
|
|
|
|
2018-04-24 01:50:55 +02:00
|
|
|
try {
|
|
|
|
cs.run(body);
|
|
|
|
} catch (cscript::cs_error const &e) {
|
|
|
|
throw build::make_error{e.what()};
|
|
|
|
}
|
2015-11-25 21:36:46 +01:00
|
|
|
});
|
2017-05-24 22:09:28 +02:00
|
|
|
}
|
2018-04-24 01:50:55 +02:00
|
|
|
}
|
2017-05-24 22:09:28 +02:00
|
|
|
|
2018-04-24 01:50:55 +02:00
|
|
|
static void init_rulelib(cs_state &cs, build::make &mk) {
|
|
|
|
cs.new_command("rule", "sse", [&cs, &mk](auto &, auto args, auto &) {
|
|
|
|
rule_add(
|
|
|
|
cs, mk, args[0].get_strr(), args[1].get_strr(), args[2].get_code()
|
|
|
|
);
|
|
|
|
});
|
2017-05-25 00:04:10 +02:00
|
|
|
|
2018-04-24 01:50:55 +02:00
|
|
|
cs.new_command("action", "se", [&cs, &mk](auto &, auto args, auto &) {
|
|
|
|
rule_add(
|
|
|
|
cs, mk, args[0].get_strr(), nullptr, args[1].get_code(), true
|
|
|
|
);
|
|
|
|
});
|
2016-08-22 19:44:21 +02:00
|
|
|
|
2018-04-24 01:50:55 +02:00
|
|
|
cs.new_command("depend", "ss", [&cs, &mk](auto &, auto args, auto &) {
|
|
|
|
rule_add(cs, mk, args[0].get_strr(), args[1].get_strr(), nullptr);
|
|
|
|
});
|
|
|
|
}
|
2015-11-25 21:36:46 +01:00
|
|
|
|
2018-04-24 01:50:55 +02:00
|
|
|
static void init_baselib(cs_state &cs, build::make &mk, bool ignore_env) {
|
|
|
|
cs.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 01:50:55 +02:00
|
|
|
cs.new_command("shell", "C", [&mk](auto &, auto args, auto &) {
|
2018-04-24 00:06:05 +02:00
|
|
|
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 01:50:55 +02:00
|
|
|
cs.new_command("getenv", "ss", [ignore_env](auto &, auto args, auto &res) {
|
2018-04-24 00:06:05 +02:00
|
|
|
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
|
|
|
});
|
|
|
|
|
2018-04-24 01:50:55 +02:00
|
|
|
cs.new_command("invoke", "s", [&mk](auto &, auto args, auto &) {
|
|
|
|
mk.exec(args[0].get_strr());
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
static void init_pathlib(cs_state &cs) {
|
|
|
|
cs.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 01:50:55 +02:00
|
|
|
cs.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
|
|
|
});
|
2018-04-24 01:50:55 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void do_main(int argc, char **argv) {
|
|
|
|
/* cubescript interpreter */
|
|
|
|
cs_state cs;
|
|
|
|
cs.init_libs();
|
|
|
|
|
|
|
|
/* 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 */
|
|
|
|
cs.new_ivar("numcpus", 4096, 1, ncpus);
|
|
|
|
cs.new_ivar("numjobs", 4096, 1, jobs);
|
|
|
|
|
|
|
|
/* 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 simple tasks, cubescript cannot into coroutines */
|
|
|
|
build::make mk{build::make_task_simple, jobs};
|
|
|
|
|
|
|
|
/* octabuild cubescript libs */
|
|
|
|
init_rulelib(cs, mk);
|
|
|
|
init_baselib(cs, mk, ignore_env);
|
|
|
|
init_pathlib(cs);
|
2015-08-23 10:03:30 +02:00
|
|
|
|
2018-04-24 01:50:55 +02:00
|
|
|
/* parse rules */
|
|
|
|
if ((!fcont.empty() && !cs.run_bool(fcont)) || !cs.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 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
|
|
|
}
|