914 lines
29 KiB
C
914 lines
29 KiB
C
|
#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);
|
||
|
}
|