slcc/frontend.c

914 lines
29 KiB
C
Raw Normal View History

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#ifdef _WIN32
#include <process.h> // for _spawnvp on Windows
#endif
#define BRUTAL_MAX_FILES 1000
int backend_main(int argc, char** argv);
int system(const char* cmd);
char* strdup(const char* str);
void brutal_string_append(char** buffer, char* str) {
//fprintf(stderr, "appending '%s' to '%s'\n", str, buffer[0]);
int len1 = (buffer[0] == NULL) ? 0 : strlen(buffer[0]);
//fprintf(stderr, "len1=%d\n", len1);
int len2 = strlen(str);
//fprintf(stderr, "len2=%d\n", len2);
char* nbuf = calloc(len1 + len2 + 1, 1);
if (nbuf == NULL) {
fprintf(stderr, "ERROR: Out of memory!\n");
exit(1);
}
int i;
for (i = 0; i < len1+len2; i++) {
if (i < len1) {
nbuf[i] = buffer[0][i];
} else {
nbuf[i] = str[i-len1];
}
}
if (buffer[0] != NULL) {
free(buffer[0]);
}
buffer[0] = nbuf;
}
int brutal_args_count(char** argv) {
if (argv==NULL) {
return 0;
}
int i;
for (i = 0; argv[i] != NULL; i++) {
// Just count...
}
return i;
}
void brutal_args_append(char*** buffer, char* str) {
int len = brutal_args_count(buffer[0]);
char** nbuf = calloc(len + 2, sizeof(void*));
if (nbuf == NULL) {
fprintf(stderr, "ERROR: Out of memory!\n");
exit(1);
}
int i;
for (i = 0; i < len+1; i++) {
if (i < len) {
nbuf[i] = buffer[0][i];
} else {
nbuf[i] = str;
}
}
if (buffer[0] != NULL) {
free(buffer[0]);
}
buffer[0] = nbuf;
}
void brutal_args_append_all(char*** buffer, char** moreargs) {
int i;
for (i = 0; moreargs[i] != NULL; i++) {
brutal_args_append(buffer, moreargs[i]);
}
}
int brutal_needs_quotes(char* arg) {
int i;
for (i = 0; arg[i] != 0; i++) {
char c = arg[i];
if (c == ' ') {
return 1;
}
}
return 0;
}
int brutal_setenv(char* name, char* value, int replace, int echo, int fake) {
if (echo) {
fprintf(stderr, "SETENV: Using (%s) environment variable '%s' to '%s'\n", replace ? "replacing" : "if unset", name, value);
}
if (!fake) {
return setenv(name, value, replace);
}
return 0;
}
int brutal_exec(char** argv, int echo, int fake) {
char* cmd = NULL;
int i;
for (i = 0; argv[i] != NULL; i++) {
int q = brutal_needs_quotes(argv[i]);
if (q) {
brutal_string_append(&cmd, i == 0 ? "\"" : " \"");
} else if (i != 0) {
brutal_string_append(&cmd, " ");
}
brutal_string_append(&cmd, argv[i]);
if (q) brutal_string_append(&cmd, "\"");
}
if (echo) {
fprintf(stderr, "%s command: '%s'\n", fake ? "NOT executing" : "executing", cmd);
}
return fake ? 0 : system(cmd);
}
int brutal_startswith(const char* start, char* str) {
int i = 0;
while (start[i] != 0) {
if (str[i] != start[i]) {
return 0;
}
i++;
}
return 1;
}
int brutal_endswith(char* str, const char* end) {
size_t len1 = strlen(str);
size_t len2 = strlen(end);
if (len1 < len2) {
return 0;
}
int i = (len1-len2);
int j = 0;
while (str[i] != 0) {
if (str[i] != end[j]) {
return 0;
}
i++;
j++;
}
return 1;
}
typedef struct compilerjob compilerjob_t;
struct compilerjob {
char* selfcmd;
char** inputs;
char** incdirs;
char** defines;
int numinputs;
int numincdirs;
int numdefines;
int skipcompiler;
int skiplinker;
int skipassembler;
int echocmd;
int fakecmd;
int keeptmp;
int useyasm;
int usefasm;
int usenasm;
int usezasm;
int useas;
int nasmsyntax;
int gc;
int thread;
int mm;
int riscv;
int linkstatic;
int usemold;
int use101;
char* output;
char* tmpdir;
char** tmpfiles;
char* modinitfile; // If not null, should be added as an input
char* modinitfunc; // Name of function in modinit
char* sym_prefix;
};
char* brutal_path_end(compilerjob_t* job, char* path) {
char* lastpart = path;
int i;
for (i = 0; path[i] != 0; i++) {
if (path[i] == '/' || path[i] == '\\') {
lastpart = path+i+1;
}
}
return lastpart;
}
char* brutal_path_sanitise(compilerjob_t* job, char* path) {
path = strdup(brutal_path_end(job, path));
int i = 0;
while (path[i] != 0) {
char c = path[i];
if (((c >= 'a') && (c <= 'z')) || ((c >= 'A') && (c <= 'Z')) || ((c >= '0') && (c <= '9'))) {
// Keep it.
} else {
path[i] = '_';
}
i++;
}
return path;
}
/* I guess the most portable way of checking if a file "exists" is trying to open it for reading
* (If it exists but we can't open it for reading, then it's unlikely that we'll be able to accidentally overwrite it either!)
*/
int brutal_exists(char* fname) {
FILE* f = fopen(fname, "rb");
if (f == NULL) {
return 0;
} else {
fclose(f);
return 1;
}
}
char* brutal_tmp(compilerjob_t* job, char* namehint1, char* namehint2);
char* brutal_tmp(compilerjob_t* job, char* namehint1, char* namehint2) {
char* result = NULL;
namehint1 = brutal_path_end(job, namehint1);
namehint2 = brutal_path_end(job, namehint2);
brutal_string_append(&result, job->tmpdir);
brutal_string_append(&result, "/");
if (!brutal_startswith("brutal-", namehint1)) {
brutal_string_append(&result, "brutal-");
}
brutal_string_append(&result, namehint1);
brutal_string_append(&result, ".tmp");
brutal_string_append(&result, namehint2);
// If the file already exists, we'll print a notice that you've probably got temporary files hanging around and try a new name
if (brutal_exists(result)) {
char* newhint1 = NULL;
fprintf(stderr, "WARNING: File '%s' already exists, you probably have temporary files laying around. I'll try a new name just in case it's a user-modified file!.\n", result);
brutal_string_append(&newhint1, namehint1);
brutal_string_append(&newhint1, "2");
return brutal_tmp(job, newhint1, namehint2);
}
// Slight workaround while compiler bugs are being fixed, create a temporary copy of the tmpfiles pointer...
char** tmpfiles = job->tmpfiles;
// Add the result to tmpfiles
brutal_args_append(&tmpfiles, result);
// Other end of workaround...
job->tmpfiles = tmpfiles;
// And we're done!
return result;
}
int brutal_exec_cp(compilerjob_t* job, char* input, char* output) {
char** args = NULL;
brutal_args_append(&args, "cp");
brutal_args_append(&args, input);
brutal_args_append(&args, output);
return brutal_exec(args, job->echocmd, job->fakecmd);
}
int brutal_exec_rm(compilerjob_t* job, char* fname) {
char** args = NULL;
brutal_args_append(&args, "rm");
brutal_args_append(&args, fname);
return brutal_exec(args, job->echocmd, job->fakecmd);
}
int brutal_exec_preprocessor(compilerjob_t* job, char* input, char* output, int isasm) {
char** args = NULL;
brutal_args_append(&args, job->selfcmd);
brutal_args_append(&args, "-P");
int i;
for (i = 0; i < job->numincdirs; i++) {
//char* tmp = NULL;
//brutal_string_append(&tmp, "-I");
//brutal_string_append(&tmp, job->incdirs[i]);
brutal_args_append(&args, "-I");
brutal_args_append(&args, job->incdirs[i]);//tmp);
}
for (i = 0; i < job->numdefines; i++) {
char* tmp = NULL;
brutal_string_append(&tmp, "-D");
brutal_string_append(&tmp, job->defines[i]);
brutal_args_append(&args, tmp);
}
if (isasm) {
char* tmp = NULL;
brutal_string_append(&tmp, "-D");
brutal_string_append(&tmp, "__ASSEMBLER__");
brutal_args_append(&args, tmp);
}
brutal_args_append(&args, input);
brutal_args_append(&args, output);
return brutal_exec(args, job->echocmd, job->fakecmd);
}
int brutal_exec_compiler(compilerjob_t* job, char* input, char* output, char* modname) {
char** args = NULL;
brutal_args_append(&args, job->selfcmd);
brutal_args_append(&args, "-B");
if (job->use101) {
brutal_args_append(&args, "--101");
}
if (modname != NULL) {
brutal_args_append(&args, "--mod");
brutal_args_append(&args, modname);
}
if (job->sym_prefix != NULL) {
brutal_args_append(&args, "--prefix");
brutal_args_append(&args, job->sym_prefix);
}
brutal_args_append(&args, "--input");
brutal_args_append(&args, input);
brutal_args_append(&args, "--output");
brutal_args_append(&args, output);
return brutal_exec(args, job->echocmd, job->fakecmd);
}
int brutal_exec_assembler(compilerjob_t* job, char* input, char* output) {
char** args = NULL;
if (job->useyasm) {
brutal_args_append(&args, "yasm");
if (!job->nasmsyntax) {
brutal_args_append(&args, "-p");
brutal_args_append(&args, "gnu");
}
brutal_args_append(&args, "-f");
brutal_args_append(&args, "elf64");
} else if (job->usenasm) {
brutal_args_append(&args, "nasm");
brutal_args_append(&args, "-f");
brutal_args_append(&args, "elf64");
} else if (job->usefasm) {
brutal_args_append(&args, "fasm");
brutal_args_append(&args, "-m");
brutal_args_append(&args, "500000"); // Set a memory limit a bit under 512mb
} else if (job->usezasm) {
brutal_args_append(&args, "zasm");
brutal_args_append(&args, "--mode");
brutal_args_append(&args, "rv64");
} else if (job->useas) {
if (job->riscv) {
#ifdef __riscv
brutal_args_append(&args, "as");
#else
brutal_args_append(&args, "riscv64-linux-gnu-as");
#endif
brutal_args_append(&args, "--traditional-format");
brutal_args_append(&args, "-fno-pic");
brutal_args_append(&args, "-march=rv64imafd");
//brutal_args_append(&args, "-march=rv64ifd"); // NOTE: These settings need to be fine-tuned... floating-point ABI is not currently matching the default for Ubuntu cross-compilation
brutal_args_append(&args, "-mabi=lp64d");
//-march=rv64ifd -mabi=lp64d
} else {
brutal_args_append(&args, "as");
brutal_args_append(&args, "--64");
}
} else {
if (job->riscv) {
#ifdef __riscv
brutal_args_append(&args, "cc");
#else
brutal_args_append(&args, "riscv64-linux-gnu-gcc");
#endif
} else {
brutal_args_append(&args, "cc");
}
brutal_args_append(&args, "-c");
}
brutal_args_append(&args, input);
if (!job->usefasm) {
brutal_args_append(&args, "-o");
}
brutal_args_append(&args, output);
return brutal_exec(args, job->echocmd, job->fakecmd);
}
int brutal_exec_linker(compilerjob_t* job, char** inputs, char* output) {
char** args = NULL;
if (job->usemold) {
brutal_args_append(&args, "mold");
brutal_args_append(&args, "-L/usr/lib/x86_64-linux-gnu");
brutal_args_append(&args, "-L/usr/lib/gcc/x86_64-linux-gnu/11");
brutal_args_append(&args, "-lc");
brutal_args_append(&args, "-lgcc");
} else if (job->riscv) {
#ifdef __riscv
brutal_args_append(&args, "cc");
#else
brutal_args_append(&args, "riscv64-linux-gnu-gcc");
#endif
} else {
brutal_args_append(&args, "cc");
}
if (job->linkstatic) {
brutal_args_append(&args, "--static");
}
if (job->thread) {
brutal_args_append(&args, "-pthread");
}
brutal_args_append_all(&args, inputs);
if (job->gc) {
brutal_args_append(&args, "-lgc");
}
if (job->mm) {
// TODO: It probably makes more sense to dynamically link SOME of this stuff (or, at least, to have more options)
// But in any case, it seemed to make sense to have a shortcut to link a multimedia-enabled program
brutal_args_append(&args, "-lSDL2");
brutal_args_append(&args, "-lcairo");
}
brutal_args_append(&args, "-o");
brutal_args_append(&args, output);
return brutal_exec(args, job->echocmd, job->fakecmd);
}
int brutal_mkmodinit(compilerjob_t* job, char** modnames) {
if (job->fakecmd) {
return 0;
}
FILE* o = fopen(job->modinitfile, "w");
if (o == NULL) {
return 1;
}
int i;
for (i = 0; modnames[i] != NULL; i++) {
fprintf(o, "void __module__%s__init();\n", modnames[i]);
}
fprintf(o, "void __oop_init_begin();\n");
fprintf(o, "void __oop_init_end();\n");
fprintf(o, "\n");
fprintf(o, "void %s() {\n", job->modinitfunc);
fprintf(o, " __oop_init_begin();\n");
for (i = 0; modnames[i] != NULL; i++) {
fprintf(o, " __module__%s__init();\n", modnames[i]);
}
fprintf(o, " __oop_init_end();\n");
fprintf(o, "}\n");
fclose(o);
return 0;
}
int brutal_run(compilerjob_t* job) {
char** tolink = NULL;
char** toinit = NULL;
int result = 0;
char* last = NULL;
//fprintf(stderr, "Attempting to run...\n");
int i;
for (i = 0; i < job->numinputs; i++) {
char* input = job->inputs[i];
//fprintf(stderr, "Doing input '%s'\n", input);
char* modname = NULL;
int isasm = 0;
int shouldprep = 1;
if (brutal_endswith(input, ".m") || brutal_endswith(input, ".M")) {
modname = brutal_path_sanitise(job, input);
fprintf(stderr, "NOTE: Using module name '%s'\n", modname);
brutal_args_append(&toinit, modname);
} else if (brutal_endswith(input, ".s")) {
isasm = 1;
shouldprep = 0;
} else if (brutal_endswith(input, ".S")) {
isasm = 1;
}
/* If this is the modinit file, we need to create it before trying to compile it! */
if (job->modinitfile != NULL && !strcmp(input, job->modinitfile)) {
if (brutal_exists(job->modinitfile)) {
fprintf(stderr, "ERROR: Module init file already exists, will not overwrite existing file!\n");
exit(1);
}
if (brutal_mkmodinit(job, toinit) != 0) {
fprintf(stderr, "ERROR: Failed to create module init file!\n");
exit(1);
}
}
if (shouldprep) {
char* tmp = brutal_tmp(job, input, isasm ? ".preprocessed.s" : ".preprocessed.c");
result = brutal_exec_preprocessor(job, input, tmp, isasm);
if (result != 0) goto cleanup;
input = tmp;
}
if (!isasm && !job->skipcompiler) {
char* tmp = brutal_tmp(job, input, ".S");
result = brutal_exec_compiler(job, input, tmp, modname);
if (result != 0) goto cleanup;
input = tmp;
}
if (!job->skipassembler) {
char* tmp = brutal_tmp(job, input, ".o");
result = brutal_exec_assembler(job, input, tmp);
if (result != 0) goto cleanup;
input = tmp;
}
brutal_args_append(&tolink, input);
last = input;
}
//fprintf(stderr, "Finished first part...\n");
if (job->skiplinker) {
result = brutal_exec_cp(job, last, job->output);
if (result != 0) goto cleanup;
} else {
result = brutal_exec_linker(job, tolink, job->output);
if (result != 0) goto cleanup;
}
cleanup:
if (job->tmpfiles != NULL && !job->keeptmp) {
for (i = 0; job->tmpfiles[i] != NULL; i++) {
result = brutal_exec_rm(job, job->tmpfiles[i]);
if (result != 0) {
fprintf(stderr, "WARNING: Failed to remove temporary file '%s'\n", job->tmpfiles[i]);
}
}
}
return result;
}
int brutal_usage(int argc, char** argv, int argi, char* problem) {
FILE* o = (problem == NULL) ? stdout : stderr;
const char* n = argv[0];
fprintf(o, "USAGE:\n");
fprintf(o, " %s [options] input1.c [input2.c ...]\n", n);
fprintf(o, "\n");
fprintf(o, "COMMON OPTIONS:\n");
fprintf(o, " -I Add a preprocessor include directory (requires an argument)\n");
fprintf(o, " -D Add a preprocessor definition (requires an argument)\n");
fprintf(o, " -o Set the output filename (requires an argument)\n");
fprintf(o, " -c Skip linker (only produce assembled file, no executable)\n");
fprintf(o, " -S Skip assembler (only produce assembly code, no assembled file)\n");
fprintf(o, " -E Skip compiler (only produce preprocessed code, no compiled code)\n");
fprintf(o, "\n");
fprintf(o, "TARGET/TOOLCHAIN OPTIONS:\n");
fprintf(o, " --static Build with/for static linking\n");
fprintf(o, " --dynamic Build with/for dynamic linking\n");
fprintf(o, " --use-fasm Use FASM assembler (default will pass assembly files to the platform's 'cc')\n");
fprintf(o, " --use-nasm Use NASM assembler (default will pass assembly files to the platform's 'cc')\n");
fprintf(o, " --use-yasm Use YASM assembler (default will pass assembly files to the platform's 'cc')\n");
fprintf(o, " --use-zasm Use ZASM assembler (default will pass assembly files to the platform's 'cc')\n");
fprintf(o, " --use-as Use the 'as' assembler directly (this will usually be the same backend used in the default mode)\n");
fprintf(o, " --nasm-syntax Use NASM syntax in assembly output (this can be used with --use-yasm, otherwise GNU syntax will be default)\n");
fprintf(o, " --RV64 Use RISC-V target (this assumes a suitable GNU toolchain for assembly & linkage)\n");
fprintf(o, " --AMD64 Use AMD64/x86-64 target (this assumes a suitable GNU toolchain for assembly & linkage)\n");
fprintf(o, "\n");
fprintf(o, "SPECIAL CODE GENERATION OPTIONS:\n");
fprintf(o, " --prefix Add a prefix to standard C symbols (requires an argument)\n");
fprintf(o, " --101 Use __classic_call calling convention by default (only for 1337 h4x0r5)\n");
fprintf(o, "\n");
fprintf(o, "BACKEND INVOCATION OPTIONS:\n");
fprintf(o, " --echo Echo command invocations & environment settings to the shell\n");
fprintf(o, " --fake Fake command invocations (can be used in conjunction with --echo to just show which commands would be used to compile something)\n");
fprintf(o, " --keeptmp Keep temporary files\n");
fprintf(o, " --compiler Invoke the internal compiler directly (must be first option, passes all options to backend)\n");
fprintf(o, " --preprocessor Invoke the internal preprocessor directly (must be first option, passes all options to preprocessor)\n");
fprintf(o, "\n");
fprintf(o, "EXTENSION OPTIONS:\n");
fprintf(o, " --gc Enable garbage collection\n");
fprintf(o, " --thread Enable multithreading\n");
fprintf(o, " (aliases: -pthread, --pthread)\n");
fprintf(o, " --mm Enable multimedia (requires SDL2, cairo libraries)\n");
fprintf(o, "\n");
fprintf(o, "TOOLCHAIN INFORMATION:\n");
fprintf(o, " --usage Shows usage information\n");
fprintf(o, " (aliases: --help, -h, -u, -H, -U)\n");
fprintf(o, " --version Shows version information\n");
fprintf(o, "\n");
if (problem != NULL) {
fprintf(o, "ERROR:\n");
fprintf(o, " Around arg #%d: %s\n", argi, problem);
}
return (problem == NULL) ? 0 : 1;
}
void brutal_xdefine(compilerjob_t* job, const char* def) {
if (job->numdefines < BRUTAL_MAX_FILES) {
job->defines[job->numdefines] = def;
job->numdefines++;
} else {
fprintf(stderr, "ERROR: Too many defines\n");
exit(1);
}
}
int main(int argc, char** argv, char** envp) {
compilerjob_t* job;
/* Shortcuts to invoke the backend features directly. */
if (argc > 1) {
if (!strcmp(argv[1], "-B") || !strcmp(argv[1], "--compiler") || !strcmp(argv[1], "--backend")) {
argv[1] = "-B";
return backend_main(argc, argv);
} else if (!strcmp(argv[1], "-P") || !strcmp(argv[1], "--preprocessor")) {
argv[1] = "-P";
return backend_main(argc, argv);
#ifdef TOOL_MK
} else if (!strcmp(argv[1], "-M") || !strcmp(argv[1], "--make")) {
argv[1] = "-M";
mk_main(argc, argv, envp);
return 0;
#endif
}
}
fprintf(stderr, "SecureLang C Compiler Frontend\n");
job = calloc(1, sizeof(compilerjob_t));
if (job == NULL) {
fprintf(stderr, "ERROR: Out of memory!\n");
return 1;
}
job->inputs = calloc(BRUTAL_MAX_FILES, sizeof(void*));
job->incdirs = calloc(BRUTAL_MAX_FILES, sizeof(void*));
job->defines = calloc(BRUTAL_MAX_FILES, sizeof(void*));
if (job->inputs == NULL || job->incdirs == NULL || job->defines == NULL) {
fprintf(stderr, "ERROR: Out of memory!\n");
return 1;
}
job->selfcmd = strdup(argv[0]);
job->linkstatic = 0;
job->use101 = 0;
job->sym_prefix = NULL;
#ifdef __riscv
job->riscv = 1;
#else
job->riscv = 0;
#endif
int specifiedlinkage = 0;
int needsmodinit = 0;
int argi = 1;
while (argi < argc) {
char* a = argv[argi];
//fprintf(stderr, "Processing argument '%s'\n", a);
if (brutal_startswith("-o", a)) {
if (strlen(a) > 2) {
job->output = strdup(a+2);
} else if (argi+1 < argc) {
argi++;
job->output = strdup(argv[argi]);
} else {
return brutal_usage(argc, argv, argi, "Expected output filename following -o");
}
} else if (brutal_startswith("--prefix", a)) {
if (argi+1 < argc) {
argi++;
job->sym_prefix = strdup(argv[argi]);
} else {
return brutal_usage(argc, argv, argi, "Expected symbol prefix following --prefix");
}
} else if (brutal_startswith("-I", a)) {
char* dir = NULL;
if (strlen(a) > 2) {
dir = strdup(a+2);
} else if (argi+1 < argc) {
argi++;
dir = strdup(argv[argi]);
} else {
return brutal_usage(argc, argv, argi, "Expected directory name following -I");
}
//fprintf(stderr, "Got include directory '%s'\n", dir);
if (job->numincdirs < BRUTAL_MAX_FILES) {
job->incdirs[job->numincdirs] = dir;
job->numincdirs++;
} else {
return brutal_usage(argc, argv, argi, "Too many include directories");
}
} else if (brutal_startswith("-D", a)) {
char* def = NULL;
if (strlen(a) > 2) {
def = strdup(a+2);
} else if (argi+1 < argc) {
argi++;
def = strdup(argv[argi]);
} else {
return brutal_usage(argc, argv, argi, "Expected preprocessor definition following -D");
}
if (job->numdefines < BRUTAL_MAX_FILES) {
job->defines[job->numdefines] = def;
job->numdefines++;
} else {
return brutal_usage(argc, argv, argi, "Too many defines");
}
} else if (!strcmp("--static", a)) {
job->linkstatic = 1;
specifiedlinkage = 1;
} else if (!strcmp("--dynamic", a)) {
job->linkstatic = 0;
specifiedlinkage = 1;
} else if (brutal_startswith("--RV", a) || brutal_startswith("--rv", a)) {
fprintf(stderr, "WARNING: Current RISC-V backend is being tested for 64-bit with minimal floating-point support\n");
job->riscv = 1;
} else if (brutal_startswith("--AMD", a) || brutal_startswith("--amd", a)) {
job->riscv = 0;
} else if (!strcmp("--use-as", a)) {
job->useas = 1;
} else if (!strcmp("--use-yasm", a)) {
job->useyasm = 1;
} else if (!strcmp("--use-fasm", a)) {
job->usefasm = 1;
} else if (!strcmp("--use-nasm", a)) {
job->usenasm = 1;
job->nasmsyntax = 1;
} else if (!strcmp("--use-zasm", a)) {
job->usezasm = 1;
} else if (!strcmp("--nasm-syntax", a)) {
job->nasmsyntax = 1;
} else if (!strcmp("--use-mold", a)) {
job->usemold = 1;
} else if (!strcmp("--version", a)) {
fprintf(stderr, "Too early to tell.\n");
return 0;
} else if (!strcmp("--usage", a) || !strcmp("--help", a) || !strcmp("-h", a) || !strcmp("-u", a) || !strcmp("-H", a) || !strcmp("-U", a)) {
return brutal_usage(argc, argv, argi, NULL);
} else if (!strcmp("--version", a)) {
fprintf(stderr, "Too early to tell.\n");
return 0;
} else if (!strcmp("-E", a)) {
job->skipcompiler = 1;
job->skipassembler = 1;
job->skiplinker = 1;
} else if (!strcmp("-S", a)) {
job->skipassembler = 1;
job->skiplinker = 1;
} else if (!strcmp("-c", a)) {
job->skiplinker = 1;
} else if (!strcmp("--101", a)) {
job->use101 = 1;
} else if (!strcmp("--echo", a)) {
job->echocmd = 1;
} else if (!strcmp("--fake", a)) {
job->fakecmd = 1;
} else if (!strcmp("--keeptmp", a)) {
job->keeptmp = 1;
} else if (!strcmp("--gc", a)) {
job->gc = 1;
} else if (!strcmp("--thread", a) || !strcmp("-pthread", a) || !strcmp("--pthread", a)) {
job->thread = 1;
} else if (!strcmp("--mm", a)) {
job->mm = 1;
} else if (brutal_startswith("-", a)) {
return brutal_usage(argc, argv, argi, "Bad option");
} else {
if (job->numinputs < BRUTAL_MAX_FILES) {
char* cp = strdup(a);
if (brutal_endswith(cp, ".m") || brutal_endswith(cp, ".M")) {
needsmodinit = 1;
}
//fprintf(stderr, "Got strings '%s' '%s'\n", a, cp);
job->inputs[job->numinputs] = cp;
job->numinputs = job->numinputs + 1;
} else {
return brutal_usage(argc, argv, argi, "Too many input files");
}
}
argi++;
}
//fprintf(stderr, "Finished processing commands...\n");
if (needsmodinit && (job->skipassembler || job->skiplinker)) {
needsmodinit = false;
}
if (job->numinputs < 1) {
return brutal_usage(argc, argv, argi, "Nothing to do (expected input file)");
} else {
//fprintf(stderr, "Got %d inputs\n", job->numinputs);
}
if (job->output == NULL) {
if (job->skipcompiler) {
job->output = "./a.out.C";
} else if (job->skipassembler) {
job->output = "./a.out.S";
} else if (job->skiplinker) {
job->output = "./a.out.o";
} else {
job->output = "./a.out";
}
}
if (job->tmpdir == NULL) {
job->tmpdir = ".";
}
if (specifiedlinkage) {
if ((job->riscv || job->useyasm || job->usefasm || job->usezasm) && !job->linkstatic) {
fprintf(stderr, "WARNING: Dynamic linking using RISC-V or FASM/YASM targets is work-in-progress.\n");
}
} else {
if ((job->riscv || job->useyasm || job->usefasm)) {
fprintf(stderr, "NOTE: Using static linking as default on RISC-V or FASM/YASM targets.\n");
job->linkstatic = 1;
}
}
/* Set any environment variables for current backend.
* TODO: Evenutally move the more-stable options into regular command-line arguments.
*/
if (job->riscv) {
brutal_setenv("CCB_FAMILY", "risc-v", 1, job->echocmd, job->fakecmd);
}
if (job->usefasm) {
brutal_setenv("CCB_ASMFMT", "fasm", 1, job->echocmd, job->fakecmd);
}
if (job->nasmsyntax) {
brutal_setenv("CCB_ASMFMT", "nasm", 1, job->echocmd, job->fakecmd);
}
if (needsmodinit) {
if (job->modinitfile == NULL) {
job->modinitfile = brutal_tmp(job, "modinit", ".c");
}
if (job->modinitfunc == NULL) {
job->modinitfunc = "__modinit";
}
if (job->numinputs < BRUTAL_MAX_FILES) {
job->inputs[job->numinputs] = job->modinitfile;
job->numinputs = job->numinputs + 1;
} else {
return brutal_usage(argc, argv, argi, "Too many input files");
}
}
//fprintf(stderr, "Ready to run...\n");
brutal_xdefine(job, "__BRUTAL");
brutal_xdefine(job, "_ZCC");
if (job->gc) {
brutal_xdefine(job, "__BRUTAL_FEATURE_GC");
}
if (job->mm) {
brutal_xdefine(job, "__BRUTAL_FEATURE_MM");
}
if (job->thread) {
brutal_xdefine(job, "__BRUTAL_FEATURE_THREAD");
}
if (job->riscv) {
brutal_xdefine(job, "__BRUTAL_CPU_RV64");
brutal_xdefine(job, "__riscv");
} else {
brutal_xdefine(job, "__BRUTAL_CPU_X64");
brutal_xdefine(job, "__x86_64__");
}
brutal_xdefine(job, "__BRUTAL_OS_LINUX"); // TODO: Better/more target flags
return brutal_run(job);
}