colla/tools/noblin.c
2025-09-28 16:31:22 +02:00

308 lines
8.2 KiB
C

#define COLLA_NO_CONDITION_VARIABLE 1
#define COLLA_NO_NET 1
#include "../colla.c"
typedef enum optimise_level_e {
OPTIMISE_NONE,
OPTIMISE_FAST,
OPTIMISE_SMALL,
OPTIMISE__COUNT,
} optimise_level_e;
typedef enum warning_level_e {
WARNING_NONE,
WARNING_DEFAULT,
WARNING_ALL,
WARNING__COUNT,
} warning_level_e;
typedef enum sanitiser_e {
SANITISER_NONE,
SANITISER_ADDRESS,
SANITISER__COUNT,
} sanitiser_e;
typedef enum cversion_e {
CVERSION_LATEST,
CVERSION_17,
CVERSION_11,
CVERSION__COUNT
} cversion_e;
typedef struct options_t options_t;
struct options_t {
strview_t input_fname;
strview_t out_fname;
optimise_level_e optimisation;
warning_level_e warnings;
bool warnings_as_error;
sanitiser_e sanitiser;
bool fast_math;
bool debug;
strv_list_t *defines;
cversion_e cstd;
bool run;
strv_list_t *run_args;
bool is_cpp;
};
void print_help_message(void) {
puts("usage:");
puts(" -r / -run [input.c] [args...] compiles and runs <input.c>, forwards <args...>");
puts(" -h / -help print this message");
puts(" -o / -out [filename] output filename (default: build/<file>.exe)");
puts(" -O / -optimise [fast,small] optimisation level");
puts(" -w / -warning [default,all] warning level");
puts(" -werror treat warnings as errors");
puts(" -fsanitize [address] turn on sanitiser");
puts(" -fastmath turn on fast math");
puts(" -g / -debug generate debug information");
puts(" -D / -define [key=value,key] add a preprocessor define ");
puts(" -std [c11,c17,clatest] select c standard (default: clatest)");
puts(" -cpp compile c++ instead of c");
exit(0);
}
optimise_level_e get_optimisation_level(strview_t arg) {
if (strv_equals(arg, strv("fast"))) {
return OPTIMISE_FAST;
}
else if (strv_equals(arg, strv("small"))) {
return OPTIMISE_SMALL;
}
warn("unrecognised optimisation level: (%v)", arg);
return OPTIMISE_NONE;
}
warning_level_e get_warning_level(strview_t arg) {
if (strv_equals(arg, strv("default"))) {
return WARNING_DEFAULT;
}
else if (strv_equals(arg, strv("all"))) {
return WARNING_ALL;
}
warn("unrecognised warning level: (%v)", arg);
return WARNING_NONE;
}
sanitiser_e get_sanitiser(strview_t arg) {
if (strv_equals(arg, strv("address"))) {
return SANITISER_ADDRESS;
}
warn("unrecognised sanitiser: (%v)", arg);
return SANITISER_NONE;
}
cversion_e get_cversion(strview_t arg) {
if (strv_equals(arg, strv("clatest"))) {
return CVERSION_LATEST;
}
else if (strv_equals(arg, strv("c17"))) {
return CVERSION_17;
}
else if (strv_equals(arg, strv("c11"))) {
return CVERSION_11;
}
warn("unrecognised c std version: (%v)", arg);
return CVERSION_LATEST;
}
options_t parse_options(arena_t *arena, int argc, char **argv) {
options_t out = {0};
for (int i = 1; i < argc; ++i) {
strview_t arg = strv(argv[i]);
#define CHECK_OPT_BEG() if (false) {}
#define CHECK_OPT1(opt) else if (strv_equals(arg, strv("-" opt)))
#define CHECK_OPT2(small, big) else if (strv_equals(arg, strv("-" small)) || strv_equals(arg, strv("-" big)))
#define GET_NEXT_ARG() (i + 1) < argc ? strv(argv[++i]) : STRV_EMPTY
CHECK_OPT_BEG()
CHECK_OPT2("h", "help") {
print_help_message();
}
CHECK_OPT2("o", "out") {
strview_t out_fname = GET_NEXT_ARG();
str_t out_fname_str = str_fmt(arena, "build/%v", out_fname);
out.out_fname = strv(out_fname_str);
}
CHECK_OPT2("O", "optimise") {
out.optimisation = get_optimisation_level(GET_NEXT_ARG());
}
CHECK_OPT2("w", "warning") {
out.warnings = get_warning_level(GET_NEXT_ARG());
}
CHECK_OPT1("werror") {
out.warnings_as_error = true;
}
CHECK_OPT1("fsanitize") {
out.sanitiser = get_sanitiser(GET_NEXT_ARG());
}
CHECK_OPT1("fastmath") {
out.fast_math = true;
}
CHECK_OPT2("g", "debug") {
out.debug = true;
}
CHECK_OPT2("D", "define") {
darr_push(arena, out.defines, GET_NEXT_ARG());
}
CHECK_OPT1("std") {
out.cstd = get_cversion(GET_NEXT_ARG());
}
CHECK_OPT1("cpp") {
out.is_cpp = true;
}
CHECK_OPT2("r", "run") {
out.run = true;
out.input_fname = GET_NEXT_ARG();
for (i += 1; i < argc; ++i) {
darr_push(arena, out.run_args, strv(argv[i]));
}
}
else {
out.input_fname = arg;
}
}
#undef CHECK_OPT_BEG
#undef CHECK_OPT1
#undef CHECK_OPT2
#undef GET_NEXT_ARG
if (strv_is_empty(out.out_fname)) {
strview_t name;
os_file_split_path(out.input_fname, NULL, &name, NULL);
str_t out_fname = str_fmt(arena, "build\\%v", name);
out.out_fname = strv(out_fname);
}
return out;
}
int main(int argc, char **argv) {
os_init();
arena_t arena = arena_make(ARENA_VIRTUAL, GB(1));
if (argc < 2) {
print_help_message();
}
options_t opt = parse_options(&arena, argc, argv);
if (!os_dir_exists(strv("build/"))) {
info("creating build folder");
mkdir("build", 755);
}
{
arena_t scratch = arena;
os_cmd_t *cmd = NULL;
if (opt.is_cpp) {
darr_push(&scratch, cmd, strv("c++"));
}
else {
darr_push(&scratch, cmd, strv("cc"));
}
str_t output = str_fmt(&scratch, "-o %v", opt.out_fname);
darr_push(&scratch, cmd, strv(output));
strview_t optimisations[OPTIMISE__COUNT] = {
strv("-O0"), // disabled
strv("-O3"), // fast code
strv("-Os"), // small code
};
darr_push(&scratch, cmd, optimisations[opt.optimisation]);
strview_t warnings[WARNING__COUNT] = {
strv("-w"),
strv("-Weverything"),
strv("-Wpedantic"),
};
darr_push(&scratch, cmd, warnings[opt.warnings]);
if (opt.warnings_as_error) {
darr_push(&scratch, cmd, strv("-Werror"));
}
if (opt.sanitiser) {
strview_t sanitisers[SANITISER__COUNT] = {
strv(""),
strv("-fsanitize=address"),
};
darr_push(&scratch, cmd, sanitisers[opt.sanitiser]);
}
if (opt.fast_math) {
darr_push(&scratch, cmd, strv("-ffast-math"));
}
if (opt.debug) {
darr_push(&scratch, cmd, strv("-g"));
darr_push(&scratch, cmd, strv("-D_DEBUG"));
}
for_each (def, opt.defines) {
for (int i = 0; i < def->count; ++i) {
str_t define = str_fmt(&scratch, "-D%v", def->items[i]);
darr_push(&scratch, cmd, strv(define));
}
}
strview_t cversion[CVERSION__COUNT] = {
strv("c23"),
strv("c17"),
strv("c11"),
};
str_t cstd = str_fmt(&scratch, "-std %v", cversion[opt.cstd]);
darr_push(&scratch, cmd, strv(cstd));
darr_push(&scratch, cmd, opt.input_fname);
oshandle_t hout = os_handle_zero();
bool compilation_result = os_run_cmd(
scratch,
cmd,
&(os_cmd_options_t){ .out = &hout, }
);
str_t result = os_file_read_all_str_fp(&scratch, hout);
info("result:\n%v\n", result);
if (!compilation_result) {
return 1;
}
}
if (opt.run) {
arena_t scratch = arena;
os_cmd_t *cmd = NULL;
darr_push(&scratch, cmd, opt.out_fname);
for_each (arg, opt.run_args) {
for (int i = 0; i < arg->count; ++i) {
darr_push(&scratch, cmd, arg->items[i]);
}
}
if (!os_run_cmd(scratch, cmd, NULL)) {
return 1;
}
}
arena_cleanup(&arena);
os_cleanup();
}