aboutsummaryrefslogtreecommitdiff
path: root/build.c
diff options
context:
space:
mode:
Diffstat (limited to 'build.c')
-rw-r--r--build.c400
1 files changed, 400 insertions, 0 deletions
diff --git a/build.c b/build.c
new file mode 100644
index 0000000..ec6f0c3
--- /dev/null
+++ b/build.c
@@ -0,0 +1,400 @@
+// The moonshine library build and installation script.
+
+// ----- This part is a bit of boilerplate to establish basic functionality and get to work. -----
+
+#define syscall1(num) \
+ ({ \
+ long result; \
+ __asm__ volatile("syscall" : "=a"(result) : "a"(num) : "rcx", "r11", "memory", "cc"); \
+ result; \
+ })
+
+#define syscall2(num, arg1) \
+ ({ \
+ long result; \
+ __asm__ volatile("syscall" : "=a"(result) : "a"(num), "D"(arg1) : "rcx", "r11", "memory", "cc"); \
+ result; \
+ })
+
+#define syscall4(num, arg1, arg2, arg3) \
+ ({ \
+ long result; \
+ __asm__ volatile("syscall" : "=a"(result) : "a"(num), "D"(arg1), "S"(arg2), "d"(arg3) : "rcx", "r11", "memory", "cc"); \
+ result; \
+ })
+
+#define syscall5(num, arg1, arg2, arg3, arg4) \
+ ({ \
+ long result; \
+ register long __arg4 __asm__("r10") = (long) (arg4); \
+ __asm__ volatile("syscall" \
+ : "=a"(result) \
+ : "a"(num), "D"(arg1), "S"(arg2), "d"(arg3), "r"(__arg4) \
+ : "rcx", "r11", "memory", "cc"); \
+ result; \
+ })
+
+#define stdin 0
+#define stdout 1
+#define stderr 2
+
+#define BUFFER_SIZE 4096
+
+char stdout_buffer[ BUFFER_SIZE ];
+int stdout_buffer_index = 0;
+
+int flush(int fd) {
+ if (stdout_buffer_index == 0) return 0;
+
+ int total_written = 0;
+ while (total_written < stdout_buffer_index) {
+ int result = syscall4(1, fd, stdout_buffer + total_written, stdout_buffer_index - total_written);
+ if (result < 0) return -1;
+ total_written += result;
+ }
+ stdout_buffer_index = 0;
+ return total_written;
+}
+
+int write(const char *ptr, const int size, const int fd) {
+ if (size <= 0) return 0;
+
+ if (fd == 1) {
+ for (int i = 0; i < size; i++) {
+ if (ptr[ i ] == 0) return flush(fd);
+ stdout_buffer[ stdout_buffer_index++ ] = ptr[ i ];
+ if (stdout_buffer_index == BUFFER_SIZE || ptr[ i ] == '\n') {
+ int result = flush(fd);
+ if (result < 0) return -1;
+ }
+ }
+ return size;
+ } else {
+ int total_written = 0;
+ while (total_written < size) {
+ int result = syscall4(1, fd, (void *) (ptr + total_written), size - total_written);
+ if (result < 0) { return -1; }
+ total_written += result;
+ }
+ return total_written;
+ }
+}
+
+#define staputs(txt) \
+ ({ \
+ const char __txt[] = "" txt; \
+ write(__txt, sizeof(__txt), stdout); \
+ })
+#define puts(txt) \
+ ({ \
+ const char *__txt = txt; \
+ write(__txt, strlen(__txt), stdout); \
+ })
+
+__attribute__((diagnose_as_builtin(__builtin_strlen, 1))) unsigned long strlen(const char *str) {
+ unsigned long len = 0;
+ while (str[ len ] != '\0') len++;
+ return len;
+}
+
+void exit(const int exit_code) {
+ const unsigned long syscall_number = 60;
+
+ flush(stdout);
+ (void) syscall2(syscall_number, exit_code);
+
+ __builtin_unreachable();
+}
+
+int fork() {
+ const unsigned long syscall_number = 57;
+ const int pid = syscall1(syscall_number);
+
+ return pid;
+}
+
+char **environ;
+
+[[noreturn]] __attribute__((used)) void __moonshine_builder_start(int argc, char **argv, char **envp) {
+ int exit_code;
+
+ environ = envp;
+
+ __asm__ __volatile__("mov %1, %%rdi \n"
+ "mov %2, %%rsi \n"
+ "call main \n"
+ "mov %%eax, %0 \n"
+ : "=r"(exit_code)
+ : "r"((long) argc), "r"(argv)
+ : "rdi", "rsi", "rax", "memory");
+
+ exit(exit_code);
+ __builtin_unreachable();
+}
+
+__attribute__((force_align_arg_pointer)) __attribute__((naked)) void _start(void) {
+ __asm__ volatile("mov (%rsp), %rdi \n"
+ "lea 8(%rsp), %rsi \n"
+ "mov %rdi, %rax \n"
+ "lea (,%rax,8), %rcx \n"
+ "add $16, %rcx \n"
+ "lea (%rsp, %rcx), %rdx \n"
+ "call __moonshine_builder_start \n"
+ "mov $60, %rax \n"
+ "xor %rdi, %rdi \n"
+ "syscall \n"
+ "hlt \n");
+}
+
+#define NULL ((void *) 0)
+
+int execl(const char *path, const char *arg0, const char *arg1, const char *arg2, char **envp) {
+ char *argv[] = { (char *) arg0, (char *) arg1, (char *) arg2, (char *) 0 };
+ int ret = (int) syscall4(59, path, argv, envp);
+ return ret;
+}
+
+int waitpid(int pid, int *status, int options) { return syscall5(61, pid, status, options, 0); }
+
+int system(const char *command) {
+ int pid;
+ int status;
+
+ if (command == NULL) { return 1; }
+
+ pid = fork();
+ if (pid == -1) { return -1; }
+ if (pid == 0) {
+ execl("/bin/sh", "sh", "-c", command, environ);
+ exit(127);
+ }
+ waitpid(pid, &status, 0);
+ return status;
+}
+
+int run(const char *command) {
+ staputs("> ");
+ puts(command);
+ staputs("\n");
+ return system(command);
+}
+
+__attribute__((diagnose_as_builtin(__builtin_memcpy, 1, 2, 3))) void *
+memcpy(void *_dest, const void *_src, const unsigned long n) {
+ char *dest = _dest;
+ const char *src = _src;
+
+ for (unsigned long i = 0; i < n; i++) dest[ i ] = src[ i ];
+ return dest;
+}
+
+__attribute__((diagnose_as_builtin(__builtin_strcmp, 1, 2))) int strcmp(const char *left, const char *right) {
+ while (*left && (*left == *right) && left++ && right++);
+ return (unsigned char) (*left) - (unsigned char) (*right);
+}
+
+__attribute__((diagnose_as_builtin(__builtin_strncmp, 1, 2, 3))) int
+strncmp(const char *left, const char *right, unsigned long n) {
+ while (n && *left && (*left == *right)) {
+ left++;
+ right++;
+ n--;
+ }
+ return (n == 0) ? 0 : ((char) (*left) - (char) (*right));
+}
+
+#define putchar(c) \
+ ({ \
+ char f = c; \
+ write(&f, 1, stdout); \
+ })
+
+void puts_number(const long number, const unsigned char is_signed) {
+ char temp[ 20 ];
+ int len = 0;
+ long n = number;
+
+ if (is_signed && number < 0) {
+ putchar('-');
+ n = -n;
+ }
+
+ do {
+ temp[ len++ ] = '0' + (n % 10);
+ n /= 10;
+ } while (n > 0);
+
+ for (int i = len - 1; i >= 0; i--) putchar(temp[ i ]);
+}
+
+unsigned char eq(const char *l, const char *r) { return strcmp(l, r) == 0; }
+
+// ----- The macro processor starts here. -----
+
+#define type(...) \
+ ({ \
+ staputs(__VA_ARGS__); \
+ putchar('\n'); \
+ })
+
+void macro_define_part(char *name, int index, int count) {
+ staputs("#define EXPAND_");
+ puts(name);
+ staputs("_");
+ puts_number(index, 0);
+ staputs("(func, extra, x, ...) func(extra, ");
+ puts_number(count - index + 1, 0);
+ staputs(", x)");
+ if(index == 1) {
+ putchar('\n');
+ return;
+ }
+ staputs(" cat(dump_or_continue_0, __VA_OPT__(1))(EXPAND_");
+ puts(name);
+ staputs("_");
+ puts_number(index - 1, 0);
+ type("(func, extra, __VA_ARGS__))");
+}
+
+void macro_define_expander(char *name, int count) {
+ staputs("#define EXPAND_");
+ puts(name);
+ staputs("(func, extra, ...) EXPAND_");
+ puts(name);
+ staputs("_");
+ puts_number(count, 0);
+ type("(func, extra, __VA_ARGS__)");
+
+ for (int i = 0; i < count; i++) { macro_define_part(name, count - i, count); }
+}
+
+void macro_processor() {
+ type("#ifndef moonshine_m4_header\n"
+ "#define moonshine_m4_header\n"
+ "\n"
+ "// Autogenerated by the moonshine builder.\n"
+ "// If you wanna make changes, good luck :P\n");
+
+ type("#define p_cat(a, b) a##b\n"
+ "#define cat(a, b) p_cat(a, b)\n"
+ "#define cat3(a, b, c) cat(a, cat(b, c))\n"
+ "#define cat4(a, b, c, d) cat(cat(a, b), cat(c, d))\n");
+
+ type("#define dump_or_continue_0(...)\n"
+ "#define dump_or_continue_01(...) __VA_ARGS__\n");
+
+ macro_define_expander("general", 1024);
+ macro_define_expander("general2", 1024);
+
+ type("\n#endif");
+}
+
+// ----- The real builder code starts here. -----
+
+#define FLAGS "-Wno-incompatible-library-redeclaration -nostartfiles -nostdlib"
+#define SIZE_FLAGS \
+ "-Wl,--undefined=main -flto -Oz -ffunction-sections -fdata-sections -Wl,--gc-sections -Wl,--strip-all " \
+ "-finline-hint-functions -fno-builtin -Wl,--print-gc-sections -fno-unwind-tables -fomit-frame-pointer " \
+ "-fno-asynchronous-unwind-tables -fno-ident -fvisibility=hidden -march=native -Wl,--build-id=none"
+
+unsigned char code_ran = 0;
+
+void run_dep_code() {
+ if (code_ran) return;
+ code_ran = 1;
+ run("clang main.c -o out.bin " FLAGS " -O3");
+}
+
+unsigned char macro_ran = 0;
+
+void run_dep_macro(char *name) {
+ if (macro_ran) return;
+ macro_ran = 1;
+
+ int self_len = strlen(name);
+ char addon[] = " __internal_macro_processor__ > moonshine.expander.h";
+ char self[ self_len + sizeof(addon) + 1 ];
+
+ memcpy(self, name, self_len);
+ memcpy(self + self_len, addon, sizeof(addon));
+ self[ sizeof(self) - 1 ] = '\0';
+
+ run("rm -f moonshine.expander.h");
+ system(self);
+}
+
+unsigned char install_ran = 0;
+
+void run_dep_install() {
+ if (install_ran) return;
+ install_ran = 1;
+
+ type("Make sure your CPATH environment variable is set to recognize ~/include/ as a valid header directory.");
+ type("Otherwise, clang will not recognize moonshine as a valid library.");
+
+ run("cp moonshine.h ~/include/moonshine.h");
+ run("cp moonshine.expander.h ~/include/moonshine.expander.h");
+}
+
+int main(int argc, char **argv) {
+ if (argc <= 1) {
+ staputs("Usage:\n ");
+ puts(argv[ 0 ]);
+ staputs(
+ " [ clean | macro | code | run | profile | debug | install ]+ | <all>\n\n"
+ "This is a bootstrapped lightweight make alternative designed specifically to work with the moonshine library.\n"
+ "Clean residue files with:\n$ ");
+ puts(argv[ 0 ]);
+ staputs(" clean\n"
+ "Run the tests with:\n$ ");
+ puts(argv[ 0 ]);
+ staputs(" run\n"
+ "Install the header files for use within your projects:\n$ ");
+ puts(argv[ 0 ]);
+ staputs(" install\n"
+ "Perform preprocessing and install moonshine:\n$ ");
+ puts(argv[ 0 ]);
+ staputs(" all\n");
+ }
+
+ int tracker = 0;
+ while (++tracker < argc) {
+ char *argument = argv[ tracker ];
+
+ if (eq(argument, "clean")) {
+ type("-- clean");
+ run("rm -f out.*");
+ run("rm -f *.expander.h");
+ run("rm -f perf.data*");
+ } else if (eq(argument, "macro")) {
+ type("-- macro");
+ run_dep_macro(argv[ 0 ]);
+ } else if (eq(argument, "code")) {
+ type("-- code");
+ run_dep_code();
+ } else if (eq(argument, "run")) {
+ type("-- run");
+ run_dep_code();
+ run("./out.bin");
+ } else if (eq(argument, "profile")) {
+ type("-- profile");
+ run_dep_code();
+ run("perf record ./out.bin");
+ run("perf report > ./out.profile.txt");
+ } else if (eq(argument, "debug")) {
+ type("-- debug");
+ run("clang main.c -o out.bin $(FLAGS) -g");
+ run("lldb ./out.bin -o run");
+ } else if (eq(argument, "install")) {
+ type("-- install");
+ run_dep_install();
+ } else if (eq(argument, "all")) {
+ type("-- all");
+ run_dep_macro(argv[ 0 ]);
+ run_dep_install();
+ } else if (eq(argument, "__internal_macro_processor__")) {
+ macro_processor();
+ exit(0);
+ }
+ }
+}