diff --git a/src/compiler/linker.c b/src/compiler/linker.c index 907d31ec1..eaba10ad0 100644 --- a/src/compiler/linker.c +++ b/src/compiler/linker.c @@ -1,5 +1,7 @@ #include "compiler_internal.h" +#include // for LLVM_VERSION_STRING + extern bool llvm_link_elf(const char **args, int arg_count, const char** error_string); extern bool llvm_link_macho(const char **args, int arg_count, const char** error_string); extern bool llvm_link_coff(const char **args, int arg_count, const char** error_string); @@ -14,6 +16,51 @@ static void add_files(const char ***args, const char **files_to_link, unsigned f } } +static const char *join_strings(const char **args, unsigned count) +{ + char *res = ""; + for (unsigned i = 0; i < count; ++i) + { + res = strcat_arena(res, args[i]); + } + return res; +} + +static void prepare_msys2_linker_flags(const char ***args, const char **files_to_link, unsigned file_count) +{ + const char *root = getenv("MSYSTEM_PREFIX"); +#define add_arg(opt) vec_add(*args, opt) + add_arg("-m"); + add_arg("i386pep"); + add_arg("-Bdynamic"); + add_arg(join_strings((const char *[]){root, "\\x86_64-w64-mingw32\\lib\\crt2.o"}, 2)); + add_arg(join_strings((const char *[]){root, "\\x86_64-w64-mingw32\\lib\\crtbegin.o"}, 2)); + add_arg(join_strings((const char *[]){"-L", root, "\\x86_64-w64-mingw32\\lib"}, 3)); + add_arg(join_strings((const char *[]){"-L", root, "\\lib"}, 3)); + add_arg(join_strings((const char *[]){"-L", root, "\\x86_64-w64-mingw32\\sys-root\\mingw\\lib"}, 3)); + add_arg(join_strings((const char *[]){"-L", root, "\\lib\\clang\\", LLVM_VERSION_STRING, "\\lib\\windows"}, 5)); + add_files(args, files_to_link, file_count); + add_arg("-lmingw32"); + add_arg(join_strings((const char *[]){root, "\\lib\\clang\\", LLVM_VERSION_STRING, "\\lib\\windows\\libclang_rt.builtins-x86_64.a"}, 4)); + add_arg("-lunwind"); + add_arg("-lmoldname"); + add_arg("-lmingwex"); + add_arg("-lmsvcrt"); + add_arg("-ladvapi32"); + add_arg("-lshell32"); + add_arg("-luser32"); + add_arg("-lkernel32"); + add_arg("-lmingw32"); + add_arg(join_strings((const char *[]){root, "\\lib\\clang\\", LLVM_VERSION_STRING, "\\lib\\windows\\libclang_rt.builtins-x86_64.a"}, 4)); + add_arg("-lunwind"); + add_arg("-lmoldname"); + add_arg("-lmingwex"); + add_arg("-lmsvcrt"); + add_arg("-lkernel32"); + add_arg(join_strings((const char *[]){root, "\\x86_64-w64-mingw32\\lib\\crtend.o"}, 2)); +#undef add_arg +} + static bool link_exe(const char *output_file, const char **files_to_link, unsigned file_count) { const char **args = NULL; @@ -24,7 +71,20 @@ static bool link_exe(const char *output_file, const char **files_to_link, unsign switch (platform_target.os) { case OS_TYPE_WIN32: - return false; + // TODO: properly detect if llvm-lld is available + // TODO: check if running inside MSYS2, it could be done via getting MSYSTEM environment variable + // https://stackoverflow.com/questions/65527286/how-to-check-if-my-program-is-running-on-mingwor-msys-shell-or-on-cmd + if (!platform_target.x64.is_mingw64) return false; + if (NULL == getenv("MSYSTEM")) return false; + if (!strcmp(getenv("MSYSTEM"), "CLANG64") || !strcmp(getenv("MSYSTEM"), "MINGW64")) + { + prepare_msys2_linker_flags(&args, files_to_link, file_count); + } + else + { + return false; + } + break; case OS_TYPE_MACOSX: add_files(&args, files_to_link, file_count); vec_add(args, "-lSystem"); @@ -110,7 +170,7 @@ static bool link_exe(const char *output_file, const char **files_to_link, unsign switch (platform_target.object_format) { case OBJ_FORMAT_COFF: - success = llvm_link_coff(args, (int)vec_size(args), &error); + success = (platform_target.x64.is_mingw64 ? llvm_link_mingw : llvm_link_coff)(args, (int)vec_size(args), &error); break; case OBJ_FORMAT_ELF: success = llvm_link_elf(args, (int)vec_size(args), &error); diff --git a/src/compiler/target.c b/src/compiler/target.c index 080e4e1ff..955a96c9e 100644 --- a/src/compiler/target.c +++ b/src/compiler/target.c @@ -571,7 +571,7 @@ static OsType os_from_llvm_string(StringSlice os_string) static VendorType vendor_from_llvm_string(StringSlice slice) { -#define STRCASE(_str, _vendor) if (slicestrcmp(slice, _str) == 0) return _vendor; +#define STRCASE(_str, _vendor) if (slicestrcmp(slice, _str)) return _vendor; STRCASE("apple", VENDOR_APPLE) STRCASE("pc", VENDOR_PC) STRCASE("scei", VENDOR_SCEI) diff --git a/src/utils/file_utils.c b/src/utils/file_utils.c index ee93990ab..45bd19bc7 100644 --- a/src/utils/file_utils.c +++ b/src/utils/file_utils.c @@ -13,6 +13,10 @@ #include #include "whereami.h" +#if PLATFORM_WINDOWS +#include +#endif + const char* expand_path(const char* path) { @@ -186,7 +190,14 @@ void file_add_wildcard_files(const char ***files, const char *path, bool recursi // Doesn't end with .c3 if (strncmp(&ent->d_name[namelen - 3], ".c3", 3) != 0) { - if (ent->d_type == DT_DIR && ent->d_name[0] != '.' && recursive) + bool is_directory; +#if PLATFORM_WINDOWS + struct stat st; + is_directory = stat(ent->d_name, &st) == 0 && S_ISDIR(st.st_mode); +#else + is_directory = ent->d_type == DT_DIR; // is it POSIX-compliant? As +#endif + if (is_directory && ent->d_name[0] != '.' && recursive) { char *new_path = strndup(ent->d_name, namelen); file_add_wildcard_files(files, new_path, recursive); @@ -201,4 +212,20 @@ void file_add_wildcard_files(const char ***files, const char *path, bool recursi vec_add(*files, name); } closedir(dir); -} \ No newline at end of file +} + +#if PLATFORM_WINDOWS + +char *realpath(const char *path, char *const resolved_path) +{ + char *result = NULL == resolved_path ? calloc(PATH_MAX + 1, 1) : resolved_path; + if (NULL == result) return NULL; + if (!GetFullPathNameA(path, MAX_PATH, result, NULL)) + { + if (NULL == resolved_path) free(result); + return NULL; + } + return result; +} + +#endif diff --git a/src/utils/lib.h b/src/utils/lib.h index cf8960cc3..da0c39113 100644 --- a/src/utils/lib.h +++ b/src/utils/lib.h @@ -6,6 +6,10 @@ #include "common.h" +#if PLATFORM_WINDOWS +#include "direct.h" +#endif + typedef struct Task_ { void (*task)(void *arg); @@ -434,3 +438,18 @@ static inline StringSlice strtoslice(const char *data) typeof(_a) __a__ = (_a); \ typeof(_b) __b__ = (_b); \ __a__ < __b__ ? __a__ : __b__; }) + +// Windows-specific code + + +#if PLATFORM_WINDOWS + +int asprintf(char **strp, const char *fmt, ...); +int vasprintf(char **strp, const char *fmt, va_list ap); +char *strndup(const char *s, size_t len); + +char *realpath(const char *path, char *resolved_path); + +#define mkdir(name, unused) _mkdir(name) + +#endif diff --git a/src/utils/malloc.c b/src/utils/malloc.c index e17988f38..f57e60293 100644 --- a/src/utils/malloc.c +++ b/src/utils/malloc.c @@ -31,7 +31,7 @@ void *malloc_arena(size_t mem) void print_arena_status(void) { printf("-- ARENA INFO -- \n"); - printf(" * Memory used: %ld Kb\n", arena.allocated / 1024); + printf(" * Memory used: %zu Kb\n", arena.allocated / 1024); printf(" * Allocations: %d\n", allocations_done); } diff --git a/src/utils/stringutils.c b/src/utils/stringutils.c index 71c60f938..19d7bf099 100644 --- a/src/utils/stringutils.c +++ b/src/utils/stringutils.c @@ -59,4 +59,44 @@ char *strcat_arena(const char *a, const char *b) memcpy(buffer + a_len, b, b_len); buffer[a_len + b_len] = '\0'; return buffer; -} \ No newline at end of file +} + +#if PLATFORM_WINDOWS + +int asprintf(char **strp, const char *fmt, ...) +{ + va_list args; + va_start(args, fmt); + int res = vasprintf(strp, fmt, args); + va_end(args); + return res; +} + +int vasnprintf(char **strp, const char *fmt, va_list args) +{ + va_list args_copy; + va_copy(args_copy, args); + int res = vsprintf(NULL, fmt, args); + if (res < 0) goto END; + char *buf = calloc(res + 1, 1); + if (NULL == buf) goto END; + sprintf(buf, fmt, args_copy); // this can't fail, right? + *strp = buf; + END: + va_end(args_copy); + return res; +} + +char *strndup(const char *s, size_t n) +{ + n = strnlen(s, n); + char *t = calloc(n + 1, 1); + if (NULL != t) + { + memcpy(t, s, n); + t[n] = '\0'; + } + return t; +} + +#endif diff --git a/src/utils/vmem.c b/src/utils/vmem.c index 076b3a0ba..dc8c548ea 100644 --- a/src/utils/vmem.c +++ b/src/utils/vmem.c @@ -42,7 +42,7 @@ static inline void* mmap_allocate(Vmem *vmem, size_t to_allocate) size_t allocated_after = to_allocate + vmem->allocated; #if PLATFORM_WINDOWS size_t blocks_committed = vmem->committed / COMMIT_PAGE_SIZE; - size_t end_block = (allocated_after) / COMMIT_PAGE_SIZE; + size_t end_block = (allocated_after + COMMIT_PAGE_SIZE - 1) / COMMIT_PAGE_SIZE; // round up size_t blocks_to_allocate = end_block - blocks_committed; if (blocks_to_allocate > 0) { diff --git a/wrapper/src/wrapper.cpp b/wrapper/src/wrapper.cpp index 84497f1a3..fcfadea4a 100644 --- a/wrapper/src/wrapper.cpp +++ b/wrapper/src/wrapper.cpp @@ -65,6 +65,9 @@ static bool llvm_link(ObjFormat format, const char **args, int arg_count, const case COFF: if (lld::coff::link(arg_vector, false, output, output_err)) return true; break; + case MINGW: + if (lld::mingw::link(arg_vector, false, output, output_err)) return true; + break; default: exit(-1); }