Commit 7efa2cd81c

Andrew Kelley <superjoe30@gmail.com>
2017-03-13 18:11:55
add --each-lib-rpath option and corresponding config option
This adds an rpath entry for each used dynamic library directory. This is necessary on some systems such as NixOS.
1 parent d10bbd2
src/all_types.hpp
@@ -1413,6 +1413,7 @@ struct CodeGen {
     uint32_t test_fn_count;
 
     bool check_unused;
+    bool each_lib_rpath;
 
     ZigList<AstNode *> error_decls;
     bool generate_error_name_table;
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;
 }
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);
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.
src/link.cpp
@@ -17,6 +17,7 @@ struct LinkJob {
     ZigList<const char *> args;
     bool link_in_crt;
     Buf out_file_o;
+    HashMap<Buf *, bool, buf_hash, buf_eql_buf> 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);
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<const char *> 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);
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<const char *> &args,
         Termination *term, Buf *out_stderr, Buf *out_stdout)
src/os.hpp
@@ -10,6 +10,7 @@
 
 #include "list.hpp"
 #include "buffer.hpp"
+#include "error.hpp"
 
 #include <stdio.h>
 
@@ -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
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)