diff --git a/CMakeLists.txt b/CMakeLists.txt index fdd23c4016..206e5e2da2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -144,6 +144,27 @@ set(ZIG_STD_SRC "${CMAKE_SOURCE_DIR}/std/math.zig" ) + +set(ZIG_HOST_LINK_VERSION) +if (APPLE) + set(LD_V_OUTPUT) + execute_process( + COMMAND sh -c "${CMAKE_LINKER} -v 2>&1 | head -1" + RESULT_VARIABLE HAD_ERROR + OUTPUT_VARIABLE LD_V_OUTPUT + ) + if (NOT HAD_ERROR) + if ("${LD_V_OUTPUT}" MATCHES ".*ld64-([0-9.]+).*") + string(REGEX REPLACE ".*ld64-([0-9.]+).*" "\\1" ZIG_HOST_LINK_VERSION ${LD_V_OUTPUT}) + elseif ("${LD_V_OUTPUT}" MATCHES "[^0-9]*([0-9.]+).*") + string(REGEX REPLACE "[^0-9]*([0-9.]+).*" "\\1" ZIG_HOST_LINK_VERSION ${LD_V_OUTPUT}) + endif() + else() + message(FATAL_ERROR "${CMAKE_LINKER} failed with status ${HAD_ERROR}") + endif() +endif() + + set(C_HEADERS_DEST "lib/zig/include") set(ZIG_STD_DEST "lib/zig/std") set(CONFIGURE_OUT_FILE "${CMAKE_BINARY_DIR}/config.h") diff --git a/src/all_types.hpp b/src/all_types.hpp index 07218a40d6..172e553b48 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -1130,6 +1130,10 @@ struct CodeGen { bool windows_subsystem_windows; bool windows_subsystem_console; bool windows_linker_unicode; + Buf *darwin_linker_version; + Buf *mmacosx_version_min; + Buf *mios_version_min; + bool linker_rdynamic; // The function definitions this module includes. There must be a corresponding // fn_protos entry. diff --git a/src/codegen.cpp b/src/codegen.cpp index 530fb33f68..2d89d4aa5d 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -22,6 +22,31 @@ #include +static void init_darwin_native(CodeGen *g) { + char *osx_target = getenv("MACOSX_DEPLOYMENT_TARGET"); + char *ios_target = getenv("IPHONEOS_DEPLOYMENT_TARGET"); + + // Allow conflicts among OSX and iOS, but choose the default platform. + if (osx_target && ios_target) { + if (g->zig_target.arch.arch == ZigLLVM_arm || + g->zig_target.arch.arch == ZigLLVM_aarch64 || + g->zig_target.arch.arch == ZigLLVM_thumb) + { + osx_target = nullptr; + } else { + ios_target = nullptr; + } + } + + if (osx_target) { + g->mmacosx_version_min = buf_create_from_str(osx_target); + } else if (ios_target) { + g->mios_version_min = buf_create_from_str(ios_target); + } else { + zig_panic("unable to determine -mmacosx-version-min or -mios-version-min"); + } +} + CodeGen *codegen_create(Buf *root_source_dir, const ZigTarget *target) { CodeGen *g = allocate(1); g->import_table.init(32); @@ -46,6 +71,7 @@ CodeGen *codegen_create(Buf *root_source_dir, const ZigTarget *target) { g->libc_static_lib_dir = buf_create_from_str(""); g->libc_include_dir = buf_create_from_str(""); g->linker_path = buf_create_from_str(""); + g->darwin_linker_version = buf_create_from_str(""); } else { // native compilation, we can rely on the configuration stuff g->is_native_target = true; @@ -56,6 +82,15 @@ CodeGen *codegen_create(Buf *root_source_dir, const ZigTarget *target) { g->libc_static_lib_dir = buf_create_from_str(ZIG_LIBC_STATIC_LIB_DIR); g->libc_include_dir = buf_create_from_str(ZIG_LIBC_INCLUDE_DIR); g->linker_path = buf_create_from_str(ZIG_LD_PATH); + g->darwin_linker_version = buf_create_from_str(ZIG_HOST_LINK_VERSION); + + if (g->zig_target.os == ZigLLVM_Darwin || + g->zig_target.os == ZigLLVM_MacOSX || + g->zig_target.os == ZigLLVM_IOS) + { + init_darwin_native(g); + } + } return g; @@ -131,6 +166,22 @@ void codegen_set_windows_unicode(CodeGen *g, bool municode) { g->windows_linker_unicode = municode; } +void codegen_set_mlinker_version(CodeGen *g, Buf *darwin_linker_version) { + g->darwin_linker_version = darwin_linker_version; +} + +void codegen_set_mmacosx_version_min(CodeGen *g, Buf *mmacosx_version_min) { + g->mmacosx_version_min = mmacosx_version_min; +} + +void codegen_set_mios_version_min(CodeGen *g, Buf *mios_version_min) { + g->mios_version_min = mios_version_min; +} + +void codegen_set_rdynamic(CodeGen *g, bool rdynamic) { + g->linker_rdynamic = rdynamic; +} + static LLVMValueRef gen_expr(CodeGen *g, AstNode *expr_node); static LLVMValueRef gen_lvalue(CodeGen *g, AstNode *expr_node, AstNode *node, TypeTableEntry **out_type_entry); static LLVMValueRef gen_field_access_expr(CodeGen *g, AstNode *node, bool is_lvalue); diff --git a/src/codegen.hpp b/src/codegen.hpp index 06fe7dd8ae..d802deeed3 100644 --- a/src/codegen.hpp +++ b/src/codegen.hpp @@ -34,6 +34,10 @@ void codegen_set_linker_path(CodeGen *g, Buf *linker_path); void codegen_set_windows_subsystem(CodeGen *g, bool mwindows, bool mconsole); void codegen_set_windows_unicode(CodeGen *g, bool municode); void codegen_add_lib_dir(CodeGen *codegen, const char *dir); +void codegen_set_mlinker_version(CodeGen *g, Buf *darwin_linker_version); +void codegen_set_rdynamic(CodeGen *g, bool rdynamic); +void codegen_set_mmacosx_version_min(CodeGen *g, Buf *mmacosx_version_min); +void codegen_set_mios_version_min(CodeGen *g, Buf *mios_version_min); void codegen_add_root_code(CodeGen *g, Buf *source_dir, Buf *source_basename, Buf *source_code); diff --git a/src/config.h.in b/src/config.h.in index 77e563a99d..1985a53e2b 100644 --- a/src/config.h.in +++ b/src/config.h.in @@ -13,6 +13,7 @@ #define ZIG_LIBC_STATIC_LIB_DIR "@ZIG_LIBC_STATIC_LIB_DIR@" #define ZIG_LD_PATH "@ZIG_LD_PATH@" #define ZIG_DYNAMIC_LINKER "@ZIG_DYNAMIC_LINKER@" +#define ZIG_HOST_LINK_VERSION "@ZIG_HOST_LINK_VERSION@" #cmakedefine ZIG_LLVM_OLD_CXX_ABI diff --git a/src/link.cpp b/src/link.cpp index 14fd7cd040..34e5859b39 100644 --- a/src/link.cpp +++ b/src/link.cpp @@ -50,6 +50,9 @@ static Buf *build_o(CodeGen *parent_gen, const char *oname) { codegen_set_verbose(child_gen, parent_gen->verbose); codegen_set_errmsg_color(child_gen, parent_gen->err_color); + codegen_set_mmacosx_version_min(child_gen, parent_gen->mmacosx_version_min); + codegen_set_mios_version_min(child_gen, parent_gen->mios_version_min); + Buf *full_path = buf_alloc(); os_path_join(std_dir_path, source_basename, full_path); Buf source_code = BUF_INIT; @@ -391,38 +394,251 @@ static void construct_linker_job_mingw(LinkJob *lj) { } } + +// Parse (([0-9]+)(.([0-9]+)(.([0-9]+)?))?)? and return the +// grouped values as integers. Numbers which are not provided are set to 0. +// return true if the entire string was parsed (9.2), or all groups were +// parsed (10.3.5extrastuff). +static bool darwin_get_release_version(const char *str, int *major, int *minor, int *micro, bool *had_extra) { + *had_extra = false; + + *major = 0; + *minor = 0; + *micro = 0; + + if (*str == '\0') + return false; + + char *end; + *major = strtol(str, &end, 10); + if (*str != '\0' && *end == '\0') + return true; + if (*end != '.') + return false; + + str = end + 1; + *minor = strtol(str, &end, 10); + if (*str != '\0' && *end == '\0') + return true; + if (*end != '.') + return false; + + str = end + 1; + *micro = strtol(str, &end, 10); + if (*str != '\0' && *end == '\0') + return true; + if (str == end) + return false; + *had_extra = true; + return true; +} + +enum DarwinPlatformKind { + MacOS, + IPhoneOS, + IPhoneOSSimulator, +}; + +struct DarwinPlatform { + DarwinPlatformKind kind; + int major; + int minor; + int micro; +}; + +static void get_darwin_platform(LinkJob *lj, DarwinPlatform *platform) { + CodeGen *g = lj->codegen; + + if (g->mmacosx_version_min) { + platform->kind = MacOS; + } else if (g->mios_version_min) { + platform->kind = IPhoneOS; + } else { + zig_panic("unable to infer -macosx-version-min or -mios-version-min"); + } + + bool had_extra; + if (platform->kind == MacOS) { + if (!darwin_get_release_version(buf_ptr(g->mmacosx_version_min), + &platform->major, &platform->minor, &platform->micro, &had_extra) || + had_extra || platform->major != 10 || platform->minor >= 100 || platform->micro >= 100) + { + zig_panic("invalid -mmacosx-version-min"); + } + } else if (platform->kind == IPhoneOS) { + if (!darwin_get_release_version(buf_ptr(g->mios_version_min), + &platform->major, &platform->minor, &platform->micro, &had_extra) || + had_extra || platform->major >= 10 || platform->minor >= 100 || platform->micro >= 100) + { + zig_panic("invalid -mios-version-min"); + } + } else { + zig_unreachable(); + } + + if (platform->kind == IPhoneOS && + (g->zig_target.arch.arch == ZigLLVM_x86 || + g->zig_target.arch.arch == ZigLLVM_x86_64)) + { + platform->kind = IPhoneOSSimulator; + } +} + +static bool darwin_version_lt(DarwinPlatform *platform, int major, int minor) { + if (platform->major < major) { + return true; + } else if (platform->major > major) { + return false; + } + if (platform->minor < minor) { + return true; + } + return false; +} + +static void construct_linker_job_darwin(LinkJob *lj) { + CodeGen *g = lj->codegen; + + int ver_major; + int ver_minor; + int ver_micro; + bool had_extra; + + if (!darwin_get_release_version(buf_ptr(g->darwin_linker_version), &ver_major, &ver_minor, &ver_micro, + &had_extra) || had_extra) + { + zig_panic("invalid linker version number"); + } + + // Newer linkers support -demangle. Pass it if supported and not disabled by + // the user. + if (ver_major >= 100) { + lj->args.append("-demangle"); + } + + if (g->linker_rdynamic && ver_major >= 137) { + lj->args.append("-export_dynamic"); + } + + bool is_lib = g->out_type == OutTypeLib; + bool shared = !g->is_static && is_lib; + if (g->is_static) { + lj->args.append("-static"); + } else { + lj->args.append("-dynamic"); + } + + if (is_lib) { + zig_panic("TODO linker args on darwin for making a library"); + } + + DarwinPlatform platform; + get_darwin_platform(lj, &platform); + switch (platform.kind) { + case MacOS: + lj->args.append("-macosx_version_min"); + break; + case IPhoneOS: + lj->args.append("-iphoneos_version_min"); + break; + case IPhoneOSSimulator: + lj->args.append("-ios_simulator_version_min"); + break; + } + lj->args.append(buf_ptr(buf_sprintf("%d.%d.%d", platform.major, platform.minor, platform.micro))); + + + if (g->out_type == OutTypeExe) { + if (g->is_static) { + lj->args.append("-no_pie"); + } else { + lj->args.append("-pie"); + } + } + + lj->args.append("-o"); + lj->args.append(buf_ptr(&lj->out_file)); + + if (lj->link_in_crt) { + if (shared) { + zig_panic("TODO"); + } else if (g->is_static) { + lj->args.append("-lcrt0.o"); + } else { + switch (platform.kind) { + case MacOS: + if (darwin_version_lt(&platform, 10, 5)) { + lj->args.append("-lcrt1.o"); + } else if (darwin_version_lt(&platform, 10, 6)) { + lj->args.append("-lcrt1.10.5.o"); + } else if (darwin_version_lt(&platform, 10, 8)) { + lj->args.append("-lcrt1.10.6.o"); + } + break; + case IPhoneOS: + if (g->zig_target.arch.arch == ZigLLVM_aarch64) { + // iOS does not need any crt1 files for arm64 + } else if (darwin_version_lt(&platform, 3, 1)) { + lj->args.append("-lcrt1.o"); + } else if (darwin_version_lt(&platform, 6, 0)) { + lj->args.append("-lcrt1.3.1.o"); + } + break; + case IPhoneOSSimulator: + // no crt1.o needed + break; + } + } + } + + for (int i = 0; i < g->lib_dirs.length; i += 1) { + const char *lib_dir = g->lib_dirs.at(i); + lj->args.append("-L"); + lj->args.append(lib_dir); + } + + lj->args.append((const char *)buf_ptr(&lj->out_file_o)); + + for (int i = 0; i < g->link_libs.length; i += 1) { + Buf *link_lib = g->link_libs.at(i); + Buf *arg = buf_sprintf("-l%s", buf_ptr(link_lib)); + lj->args.append(buf_ptr(arg)); + } + +} + static void construct_linker_job(LinkJob *lj) { switch (lj->codegen->zig_target.os) { case ZigLLVM_UnknownOS: - zig_unreachable(); + zig_unreachable(); case ZigLLVM_Linux: - if (lj->codegen->zig_target.arch.arch == ZigLLVM_hexagon) { + if (lj->codegen->zig_target.arch.arch == ZigLLVM_hexagon) { zig_panic("TODO construct hexagon_TC linker job"); - } else { + } else { return construct_linker_job_linux(lj); - } + } case ZigLLVM_CloudABI: - zig_panic("TODO construct CloudABI linker job"); + zig_panic("TODO construct CloudABI linker job"); case ZigLLVM_Darwin: case ZigLLVM_MacOSX: case ZigLLVM_IOS: - zig_panic("TODO construct DarwinClang linker job"); + return construct_linker_job_darwin(lj); case ZigLLVM_DragonFly: - zig_panic("TODO construct DragonFly linker job"); + zig_panic("TODO construct DragonFly linker job"); case ZigLLVM_OpenBSD: - zig_panic("TODO construct OpenBSD linker job"); + zig_panic("TODO construct OpenBSD linker job"); case ZigLLVM_Bitrig: - zig_panic("TODO construct Bitrig linker job"); + zig_panic("TODO construct Bitrig linker job"); case ZigLLVM_NetBSD: - zig_panic("TODO construct NetBSD linker job"); + zig_panic("TODO construct NetBSD linker job"); case ZigLLVM_FreeBSD: - zig_panic("TODO construct FreeBSD linker job"); + zig_panic("TODO construct FreeBSD linker job"); case ZigLLVM_Minix: - zig_panic("TODO construct Minix linker job"); + zig_panic("TODO construct Minix linker job"); case ZigLLVM_NaCl: - zig_panic("TODO construct NaCl_TC linker job"); + zig_panic("TODO construct NaCl_TC linker job"); case ZigLLVM_Solaris: - zig_panic("TODO construct Solaris linker job"); + zig_panic("TODO construct Solaris linker job"); case ZigLLVM_Win32: switch (lj->codegen->zig_target.environ) { default: @@ -440,27 +656,27 @@ static void construct_linker_job(LinkJob *lj) { case ZigLLVM_MSVC: case ZigLLVM_UnknownEnvironment: zig_panic("TODO construct MSVC linker job"); - } + } case ZigLLVM_CUDA: - zig_panic("TODO construct Cuda linker job"); + zig_panic("TODO construct Cuda linker job"); default: - // Of these targets, Hexagon is the only one that might have - // an OS of Linux, in which case it got handled above already. - if (lj->codegen->zig_target.arch.arch == ZigLLVM_tce) { + // Of these targets, Hexagon is the only one that might have + // an OS of Linux, in which case it got handled above already. + if (lj->codegen->zig_target.arch.arch == ZigLLVM_tce) { zig_panic("TODO construct TCE linker job"); - } else if (lj->codegen->zig_target.arch.arch == ZigLLVM_hexagon) { + } else if (lj->codegen->zig_target.arch.arch == ZigLLVM_hexagon) { zig_panic("TODO construct Hexagon_TC linker job"); - } else if (lj->codegen->zig_target.arch.arch == ZigLLVM_xcore) { + } else if (lj->codegen->zig_target.arch.arch == ZigLLVM_xcore) { zig_panic("TODO construct XCore linker job"); - } else if (lj->codegen->zig_target.arch.arch == ZigLLVM_shave) { + } else if (lj->codegen->zig_target.arch.arch == ZigLLVM_shave) { zig_panic("TODO construct SHAVE linker job"); - } else if (lj->codegen->zig_target.oformat == ZigLLVM_ELF) { + } else if (lj->codegen->zig_target.oformat == ZigLLVM_ELF) { zig_panic("TODO construct Generic_ELF linker job"); - } else if (lj->codegen->zig_target.oformat == ZigLLVM_MachO) { + } else if (lj->codegen->zig_target.oformat == ZigLLVM_MachO) { zig_panic("TODO construct MachO linker job"); - } else { + } else { zig_panic("TODO construct Generic_GCC linker job"); - } + } } } @@ -535,7 +751,6 @@ void codegen_link(CodeGen *g, const char *out_file) { } lj.link_in_crt = (g->link_libc && g->out_type == OutTypeExe); - // invoke `ld` ensure_we_have_linker_path(g); construct_linker_job(&lj); diff --git a/src/main.cpp b/src/main.cpp index 44ac144dd0..88167b0cd7 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -18,10 +18,10 @@ static int usage(const char *arg0) { fprintf(stderr, "Usage: %s [command] [options]\n" "Commands:\n" - " build create executable, object, or library from target\n" - " test create and run a test build\n" + " build [source] create executable, object, or library from source\n" + " test [source] create and run a test build\n" + " parseh [source] convert a c header file to zig extern declarations\n" " version print version number and exit\n" - " parseh convert a c header file to zig extern declarations\n" " targets list available compilation targets\n" "Options:\n" " --release build with optimizations on and debug protection off\n" @@ -33,7 +33,7 @@ static int usage(const char *arg0) { " --verbose turn on compiler debug output\n" " --color [auto|off|on] enable or disable colored error messages\n" " --libc-lib-dir [path] directory where libc crt1.o resides\n" - " --libc-static-lib-dir [path] directory where libc crtbeginT.o resides\n" + " --libc-static-lib-dir [path] directory where libc crtbegin.o resides\n" " --libc-include-dir [path] directory where libc stdlib.h resides\n" " --dynamic-linker [path] set the path to ld.so\n" " --ld-path [path] set the path to the linker\n" @@ -46,6 +46,10 @@ static int usage(const char *arg0) { " -mwindows (windows only) --subsystem windows to the linker\n" " -mconsole (windows only) --subsystem console to the linker\n" " -municode (windows only) link with unicode\n" + " -mlinker-version [ver] (darwin only) override linker version\n" + " -rdynamic add all symbols to the dynamic symbol table\n" + " -mmacosx-version-min [ver] (darwin only) set Mac OS X deployment target\n" + " -mios-version-min [ver] (darwin only) set iOS deployment target\n" , arg0); return EXIT_FAILURE; } @@ -119,6 +123,10 @@ int main(int argc, char **argv) { bool mwindows = false; bool mconsole = false; bool municode = false; + const char *mlinker_version = nullptr; + bool rdynamic = false; + const char *mmacosx_version_min = nullptr; + const char *mios_version_min = nullptr; for (int i = 1; i < argc; i += 1) { char *arg = argv[i]; @@ -138,6 +146,8 @@ int main(int argc, char **argv) { mconsole = true; } else if (strcmp(arg, "-municode") == 0) { municode = true; + } else if (strcmp(arg, "-rdynamic") == 0) { + rdynamic = true; } else if (i + 1 >= argc) { return usage(arg0); } else { @@ -192,6 +202,12 @@ int main(int argc, char **argv) { target_os = argv[i]; } else if (strcmp(arg, "--target-environ") == 0) { target_environ = argv[i]; + } else if (strcmp(arg, "-mlinker-version") == 0) { + mlinker_version = argv[i]; + } else if (strcmp(arg, "-mmacosx-version-min") == 0) { + mmacosx_version_min = argv[i]; + } else if (strcmp(arg, "-mios-version-min") == 0) { + mios_version_min = argv[i]; } else { fprintf(stderr, "Invalid argument: %s\n", arg); return usage(arg0); @@ -327,6 +343,20 @@ int main(int argc, char **argv) { codegen_set_windows_subsystem(g, mwindows, mconsole); codegen_set_windows_unicode(g, municode); + codegen_set_rdynamic(g, rdynamic); + if (mlinker_version) { + codegen_set_mlinker_version(g, buf_create_from_str(mlinker_version)); + } + if (mmacosx_version_min && mios_version_min) { + fprintf(stderr, "-mmacosx-version-min and -mios-version-min options not allowed together\n"); + return EXIT_FAILURE; + } + if (mmacosx_version_min) { + codegen_set_mmacosx_version_min(g, buf_create_from_str(mmacosx_version_min)); + } + if (mios_version_min) { + codegen_set_mios_version_min(g, buf_create_from_str(mios_version_min)); + } if (cmd == CmdBuild) { codegen_add_root_code(g, &root_source_dir, &root_source_name, &root_source_code);