diff --git a/CMakeLists.txt b/CMakeLists.txt index b65b234fc5..ca78d6b281 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -18,6 +18,7 @@ set(ZIG_LIBC_LIB_DIR "" CACHE STRING "Default native target libc directory where set(ZIG_LIBC_STATIC_LIB_DIR "" CACHE STRING "Default native target libc directory where crtbeginT.o can be found") set(ZIG_LIBC_INCLUDE_DIR "/usr/include" CACHE STRING "Default native target libc include directory") set(ZIG_DYNAMIC_LINKER "" CACHE STRING "Override dynamic linker for native target") +set(ZIG_EACH_LIB_RPATH off CACHE BOOL "Add each dynamic library to rpath for native target") option(ZIG_TEST_COVERAGE "Build Zig with test coverage instrumentation" OFF) diff --git a/src/all_types.hpp b/src/all_types.hpp index 2315b28c46..8fe633460c 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -1413,6 +1413,7 @@ struct CodeGen { uint32_t test_fn_count; bool check_unused; + bool each_lib_rpath; ZigList error_decls; bool generate_error_name_table; diff --git a/src/codegen.cpp b/src/codegen.cpp index 3fbf0ff686..fd2d8dd3a9 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -90,6 +90,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->darwin_linker_version = buf_create_from_str(""); + g->each_lib_rpath = false; } else { // native compilation, we can rely on the configuration stuff g->is_native_target = true; @@ -100,6 +101,9 @@ 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->darwin_linker_version = buf_create_from_str(ZIG_HOST_LINK_VERSION); +#ifdef ZIG_EACH_LIB_RPATH + g->each_lib_rpath = true; +#endif if (g->zig_target.os == ZigLLVM_Darwin || g->zig_target.os == ZigLLVM_MacOSX || @@ -138,6 +142,10 @@ void codegen_set_check_unused(CodeGen *g, bool check_unused) { g->check_unused = check_unused; } +void codegen_set_each_lib_rpath(CodeGen *g, bool each_lib_rpath) { + g->each_lib_rpath = each_lib_rpath; +} + void codegen_set_errmsg_color(CodeGen *g, ErrColor err_color) { g->err_color = err_color; } diff --git a/src/codegen.hpp b/src/codegen.hpp index 21e8406540..dc2fb99ed8 100644 --- a/src/codegen.hpp +++ b/src/codegen.hpp @@ -20,6 +20,7 @@ void codegen_set_clang_argv(CodeGen *codegen, const char **args, size_t len); void codegen_set_is_release(CodeGen *codegen, bool is_release); void codegen_set_is_test(CodeGen *codegen, bool is_test); void codegen_set_check_unused(CodeGen *codegen, bool check_unused); +void codegen_set_each_lib_rpath(CodeGen *codegen, bool each_lib_rpath); void codegen_set_is_static(CodeGen *codegen, bool is_static); void codegen_set_strip(CodeGen *codegen, bool strip); diff --git a/src/config.h.in b/src/config.h.in index 32c53add8f..1018ec4e78 100644 --- a/src/config.h.in +++ b/src/config.h.in @@ -21,6 +21,7 @@ #define ZIG_DYNAMIC_LINKER "@ZIG_DYNAMIC_LINKER@" #define ZIG_HOST_LINK_VERSION "@ZIG_HOST_LINK_VERSION@" +#cmakedefine ZIG_EACH_LIB_RPATH #cmakedefine ZIG_LLVM_OLD_CXX_ABI // Only used for running tests before installing. diff --git a/src/link.cpp b/src/link.cpp index 9d1cb0574d..3447110a2c 100644 --- a/src/link.cpp +++ b/src/link.cpp @@ -17,6 +17,7 @@ struct LinkJob { ZigList args; bool link_in_crt; Buf out_file_o; + HashMap rpath_table; }; static const char *get_libc_file(CodeGen *g, const char *file) { @@ -148,6 +149,16 @@ static const char *getLDMOption(const ZigTarget *t) { } } +static void add_rpath(LinkJob *lj, Buf *rpath) { + if (lj->rpath_table.maybe_get(rpath) != nullptr) + return; + + lj->args.append("-rpath"); + lj->args.append(buf_ptr(rpath)); + + lj->rpath_table.put(rpath, true); +} + static void construct_linker_job_elf(LinkJob *lj) { CodeGen *g = lj->codegen; @@ -205,8 +216,24 @@ static void construct_linker_job_elf(LinkJob *lj) { for (size_t i = 0; i < g->rpath_list.length; i += 1) { Buf *rpath = g->rpath_list.at(i); - lj->args.append("-rpath"); - lj->args.append(buf_ptr(rpath)); + add_rpath(lj, rpath); + } + if (g->each_lib_rpath) { + for (size_t i = 0; i < g->lib_dirs.length; i += 1) { + const char *lib_dir = g->lib_dirs.at(i); + for (size_t i = 0; i < g->link_libs.length; i += 1) { + Buf *link_lib = g->link_libs.at(i); + bool does_exist; + Buf *test_path = buf_sprintf("%s/lib%s.so", lib_dir, buf_ptr(link_lib)); + if (os_file_exists(test_path, &does_exist) != ErrorNone) { + zig_panic("link: unable to check if file exists: %s", buf_ptr(test_path)); + } + if (does_exist) { + add_rpath(lj, buf_create_from_str(lib_dir)); + break; + } + } + } } for (size_t i = 0; i < g->lib_dirs.length; i += 1) { @@ -708,6 +735,7 @@ static void construct_linker_job(LinkJob *lj) { void codegen_link(CodeGen *g, const char *out_file) { LinkJob lj = {0}; + lj.rpath_table.init(4); lj.codegen = g; if (out_file) { buf_init_from_str(&lj.out_file, out_file); diff --git a/src/main.cpp b/src/main.cpp index bee0c1e4ce..87d06ee7fe 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -58,7 +58,8 @@ static int usage(const char *arg0) { " -framework [name] (darwin only) link against framework\n" " --check-unused perform semantic analysis on unused declarations\n" " --linker-script [path] use a custom linker script\n" - " -rpath [path] add a directory to the runtime library search path\n" + " -rpath [path] add directory to the runtime library search path\n" + " --each-lib-rpath add rpath for each used dynamic library\n" , arg0); return EXIT_FAILURE; } @@ -143,6 +144,7 @@ int main(int argc, char **argv) { bool check_unused = false; const char *linker_script = nullptr; ZigList rpath_list = {0}; + bool each_lib_rpath = false; for (int i = 1; i < argc; i += 1) { char *arg = argv[i]; @@ -166,6 +168,8 @@ int main(int argc, char **argv) { rdynamic = true; } else if (strcmp(arg, "--check-unused") == 0) { check_unused = true; + } else if (strcmp(arg, "--each-lib-rpath") == 0) { + each_lib_rpath = true; } else if (arg[1] == 'L' && arg[2] != 0) { // alias for --library-path lib_dirs.append(&arg[2]); @@ -351,6 +355,8 @@ int main(int argc, char **argv) { codegen_set_is_test(g, cmd == CmdTest); codegen_set_linker_script(g, linker_script); codegen_set_check_unused(g, check_unused); + if (each_lib_rpath) + codegen_set_each_lib_rpath(g, each_lib_rpath); codegen_set_clang_argv(g, clang_argv.items, clang_argv.length); codegen_set_strip(g, strip); diff --git a/src/os.cpp b/src/os.cpp index 272a55c336..391a9df7b4 100644 --- a/src/os.cpp +++ b/src/os.cpp @@ -218,6 +218,15 @@ int os_fetch_file(FILE *f, Buf *out_buf) { zig_unreachable(); } +int os_file_exists(Buf *full_path, bool *result) { +#if defined(ZIG_OS_POSIX) + *result = access(buf_ptr(full_path), F_OK) != -1; + return 0; +#else + zig_panic("TODO implement os_file_exists for non-posix"); +#endif +} + #if defined(ZIG_OS_POSIX) static int os_exec_process_posix(const char *exe, ZigList &args, Termination *term, Buf *out_stderr, Buf *out_stdout) diff --git a/src/os.hpp b/src/os.hpp index 70456aac18..1b2649a4ef 100644 --- a/src/os.hpp +++ b/src/os.hpp @@ -10,6 +10,7 @@ #include "list.hpp" #include "buffer.hpp" +#include "error.hpp" #include @@ -40,7 +41,6 @@ bool os_path_is_absolute(Buf *path); void os_write_file(Buf *full_path, Buf *contents); - int os_fetch_file(FILE *file, Buf *out_contents); int os_fetch_file_path(Buf *full_path, Buf *out_contents); @@ -51,4 +51,6 @@ bool os_stderr_tty(void); int os_buf_to_tmp_file(Buf *contents, Buf *suffix, Buf *out_tmp_path); int os_delete_file(Buf *path); +int os_file_exists(Buf *full_path, bool *result); + #endif