From ebdc6b594ddc0762ed9e41b5f36e6da5e03c19e0 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 27 Aug 2017 17:16:42 -0400 Subject: [PATCH] all tests passing in MacOS depends on LLD 5.0.0 with 3 patches See #273 --- ci/travis_osx_script | 2 +- example/mix_o_files/test.c | 2 +- src/link.cpp | 32 ++++++++++++++++++-- std/build.zig | 42 +++++++++++++++++++++++--- std/c/index.zig | 1 + std/os/darwin.zig | 6 ++++ std/os/linux.zig | 2 ++ std/os/path.zig | 61 ++++++++++++++++++++++++++++++++------ 8 files changed, 131 insertions(+), 17 deletions(-) diff --git a/ci/travis_osx_script b/ci/travis_osx_script index d29a52db9..362083612 100755 --- a/ci/travis_osx_script +++ b/ci/travis_osx_script @@ -12,4 +12,4 @@ cd build cmake .. -DCMAKE_PREFIX_PATH=$PREFIX_DIR -DCMAKE_INSTALL_PREFIX=$(pwd) -DZIG_LIBC_LIB_DIR=$(dirname $($CC -print-file-name=crt1.o)) -DZIG_LIBC_INCLUDE_DIR=$(echo -n | $CC -E -x c - -v 2>&1 | grep -B1 "End of search list." | head -n1 | cut -c 2- | sed "s/ .*//") -DZIG_LIBC_STATIC_LIB_DIR=$(dirname $($CC -print-file-name=crtbegin.o)) make VERBOSE=1 make install -./zig build --build-file ../build.zig test-behavior test-std test-compiler-rt test-compare-output test-compile-errors test-asm-link test-debug-safety test-parseh +./zig build --build-file ../build.zig test diff --git a/example/mix_o_files/test.c b/example/mix_o_files/test.c index c71bac684..8a4758acc 100644 --- a/example/mix_o_files/test.c +++ b/example/mix_o_files/test.c @@ -8,7 +8,7 @@ int main(int argc, char **argv) { const char *encoded = "YWxsIHlvdXIgYmFzZSBhcmUgYmVsb25nIHRvIHVz"; char buf[200]; - size_t len = decode_base_64(buf, 200, encoded, strlen(encoded)); + size_t len = decode_base_64((uint8_t *)buf, 200, (uint8_t *)encoded, strlen(encoded)); buf[len] = 0; assert(strcmp(buf, "all your base are belong to us") == 0); diff --git a/src/link.cpp b/src/link.cpp index 2f8ac2826..da9cff8ff 100644 --- a/src/link.cpp +++ b/src/link.cpp @@ -634,7 +634,29 @@ static void construct_linker_job_macho(LinkJob *lj) { } if (is_lib) { - zig_panic("TODO linker args on darwin for making a library"); + if (!g->is_static) { + lj->args.append("-dylib"); + + Buf *compat_vers = buf_sprintf("%" ZIG_PRI_usize ".0.0", g->version_major); + lj->args.append("-compatibility_version"); + lj->args.append(buf_ptr(compat_vers)); + + Buf *cur_vers = buf_sprintf("%" ZIG_PRI_usize ".%" ZIG_PRI_usize ".%" ZIG_PRI_usize, + g->version_major, g->version_minor, g->version_patch); + lj->args.append("-current_version"); + lj->args.append(buf_ptr(cur_vers)); + + // TODO getting an error when running an executable when doing this rpath thing + //Buf *dylib_install_name = buf_sprintf("@rpath/lib%s.%" ZIG_PRI_usize ".dylib", + // buf_ptr(g->root_out_name), g->version_major); + //lj->args.append("-install_name"); + //lj->args.append(buf_ptr(dylib_install_name)); + + if (buf_len(&lj->out_file) == 0) { + buf_appendf(&lj->out_file, "lib%s.%" ZIG_PRI_usize ".%" ZIG_PRI_usize ".%" ZIG_PRI_usize ".dylib", + buf_ptr(g->root_out_name), g->version_major, g->version_minor, g->version_patch); + } + } } lj->args.append("-arch"); @@ -667,8 +689,14 @@ static void construct_linker_job_macho(LinkJob *lj) { lj->args.append("-o"); lj->args.append(buf_ptr(&lj->out_file)); + for (size_t i = 0; i < g->rpath_list.length; i += 1) { + Buf *rpath = g->rpath_list.at(i); + add_rpath(lj, rpath); + } + add_rpath(lj, &lj->out_file); + if (shared) { - zig_panic("TODO"); + lj->args.append("-headerpad_max_install_names"); } else if (g->is_static) { lj->args.append("-lcrt0.o"); } else { diff --git a/std/build.zig b/std/build.zig index 7a40e97f5..072a400b3 100644 --- a/std/build.zig +++ b/std/build.zig @@ -784,10 +784,27 @@ pub const LibExeObjStep = struct { if (self.static) { self.out_filename = self.builder.fmt("lib{}.a", self.name); } else { - self.out_filename = self.builder.fmt("lib{}.so.{d}.{d}.{d}", - self.name, self.version.major, self.version.minor, self.version.patch); - self.major_only_filename = self.builder.fmt("lib{}.so.{d}", self.name, self.version.major); - self.name_only_filename = self.builder.fmt("lib{}.so", self.name); + const target_os = switch (self.target) { + Target.Native => builtin.os, + Target.Cross => |t| t.os, + }; + switch (target_os) { + builtin.Os.darwin, builtin.Os.ios, builtin.Os.macosx => { + self.out_filename = self.builder.fmt("lib{}.dylib.{d}.{d}.{d}", + self.name, self.version.major, self.version.minor, self.version.patch); + self.major_only_filename = self.builder.fmt("lib{}.dylib.{d}", self.name, self.version.major); + self.name_only_filename = self.builder.fmt("lib{}.dylib", self.name); + }, + builtin.Os.windows => { + self.out_filename = self.builder.fmt("lib{}.dll", self.name); + }, + else => { + self.out_filename = self.builder.fmt("lib{}.so.{d}.{d}.{d}", + self.name, self.version.major, self.version.minor, self.version.patch); + self.major_only_filename = self.builder.fmt("lib{}.so.{d}", self.name, self.version.major); + self.name_only_filename = self.builder.fmt("lib{}.so", self.name); + }, + } } }, } @@ -1124,6 +1141,7 @@ pub const CLibExeObjStep = struct { kind: Kind, build_mode: builtin.Mode, strip: bool, + need_flat_namespace_hack: bool, const Kind = enum { Exe, @@ -1178,6 +1196,7 @@ pub const CLibExeObjStep = struct { .object_src = undefined, .build_mode = builtin.Mode.Debug, .strip = false, + .need_flat_namespace_hack = false, }; clib.computeOutFileNames(); return clib; @@ -1223,6 +1242,7 @@ pub const CLibExeObjStep = struct { %%self.full_path_libs.append(lib.getOutputPath()); // TODO should be some kind of isolated directory that only has this header in it %%self.include_dirs.append(self.builder.cache_root); + self.need_flat_namespace_hack = true; } pub fn linkSystemLibrary(self: &CLibExeObjStep, name: []const u8) { @@ -1448,6 +1468,20 @@ pub const CLibExeObjStep = struct { %%cc_args.append("-rdynamic"); + const target_os = switch (self.target) { + Target.Native => builtin.os, + Target.Cross => |t| t.os, + }; + switch (target_os) { + builtin.Os.darwin, builtin.Os.ios, builtin.Os.macosx => { + if (self.need_flat_namespace_hack) { + %%cc_args.append("-Wl,-flat_namespace"); + } + %%cc_args.append("-Wl,-search_paths_first"); + }, + else => {} + } + for (self.full_path_libs.toSliceConst()) |full_path_lib| { %%cc_args.append(builder.pathFromRoot(full_path_lib)); } diff --git a/std/c/index.zig b/std/c/index.zig index 1aab8177b..820324ff3 100644 --- a/std/c/index.zig +++ b/std/c/index.zig @@ -37,3 +37,4 @@ pub extern "c" fn execve(path: &const u8, argv: &const ?&const u8, pub extern "c" fn dup(fd: c_int) -> c_int; pub extern "c" fn dup2(old_fd: c_int, new_fd: c_int) -> c_int; pub extern "c" fn readlink(noalias path: &const u8, noalias buf: &u8, bufsize: usize) -> isize; +pub extern "c" fn realpath(noalias file_name: &const u8, noalias resolved_name: &u8) -> ?&u8; diff --git a/std/os/darwin.zig b/std/os/darwin.zig index b33755c43..fcd356156 100644 --- a/std/os/darwin.zig +++ b/std/os/darwin.zig @@ -3,6 +3,8 @@ const assert = @import("../debug.zig").assert; pub use @import("darwin_errno.zig"); +pub const PATH_MAX = 1024; + pub const STDIN_FILENO = 0; pub const STDOUT_FILENO = 1; pub const STDERR_FILENO = 2; @@ -203,6 +205,10 @@ pub fn readlink(noalias path: &const u8, noalias buf_ptr: &u8, buf_len: usize) - errnoWrap(c.readlink(path, buf_ptr, buf_len)) } +pub fn realpath(noalias filename: &const u8, noalias resolved_name: &u8) -> usize { + if (c.realpath(filename, resolved_name) == null) @bitCast(usize, -isize(*c._errno())) else 0 +} + /// Takes the return value from a syscall and formats it back in the way /// that the kernel represents it to libc. Errno was a mistake, let's make /// it go away forever. diff --git a/std/os/linux.zig b/std/os/linux.zig index f6d0de1ed..903548ef0 100644 --- a/std/os/linux.zig +++ b/std/os/linux.zig @@ -6,6 +6,8 @@ const arch = switch (builtin.arch) { }; pub use @import("linux_errno.zig"); +pub const PATH_MAX = 4096; + pub const STDIN_FILENO = 0; pub const STDOUT_FILENO = 1; pub const STDERR_FILENO = 2; diff --git a/std/os/path.zig b/std/os/path.zig index e743a2fb9..cfed8d7b2 100644 --- a/std/os/path.zig +++ b/std/os/path.zig @@ -8,6 +8,8 @@ const Allocator = mem.Allocator; const os = @import("index.zig"); const math = @import("../math.zig"); const posix = os.posix; +const c = @import("../c/index.zig"); +const cstr = @import("../cstr.zig"); pub const sep = switch (builtin.os) { Os.windows => '\\', @@ -279,19 +281,60 @@ fn testRelative(from: []const u8, to: []const u8, expected_output: []const u8) { assert(mem.eql(u8, result, expected_output)); } +error AccessDenied; +error FileNotFound; +error NotSupported; +error NotDir; +error NameTooLong; +error SymLinkLoop; +error InputOutput; +error Unexpected; /// Return the canonicalized absolute pathname. /// Expands all symbolic links and resolves references to `.`, `..`, and /// extra `/` characters in ::pathname. /// Caller must deallocate result. pub fn real(allocator: &Allocator, pathname: []const u8) -> %[]u8 { - if (builtin.os == builtin.Os.windows) { - @compileError("TODO implement os.path.real for windows"); + switch (builtin.os) { + Os.windows => @compileError("TODO implement os.path.real for windows"), + Os.darwin, Os.macosx, Os.ios => { + // TODO instead of calling the libc function here, port the implementation + // to Zig, and then remove the NameTooLong error possibility. + const pathname_buf = %return allocator.alloc(u8, pathname.len + 1); + defer allocator.free(pathname_buf); + + const result_buf = %return allocator.alloc(u8, posix.PATH_MAX); + %defer allocator.free(result_buf); + + mem.copy(u8, pathname_buf, pathname); + pathname_buf[pathname.len] = 0; + + const err = posix.getErrno(posix.realpath(pathname_buf.ptr, result_buf.ptr)); + if (err > 0) { + return switch (err) { + posix.EINVAL => unreachable, + posix.EBADF => unreachable, + posix.EFAULT => unreachable, + posix.EACCES => error.AccessDenied, + posix.ENOENT => error.FileNotFound, + posix.ENOTSUP => error.NotSupported, + posix.ENOTDIR => error.NotDir, + posix.ENAMETOOLONG => error.NameTooLong, + posix.ELOOP => error.SymLinkLoop, + posix.EIO => error.InputOutput, + else => error.Unexpected, + }; + } + return cstr.toSlice(result_buf.ptr); + }, + Os.linux => { + const fd = %return os.posixOpen(pathname, posix.O_PATH|posix.O_NONBLOCK|posix.O_CLOEXEC, 0, allocator); + defer os.posixClose(fd); + + var buf: ["/proc/self/fd/-2147483648".len]u8 = undefined; + const proc_path = fmt.bufPrint(buf[0..], "/proc/self/fd/{}", fd); + + return os.readLink(allocator, proc_path); + }, + else => @compileError("TODO implement os.path.real for " ++ @enumTagName(builtin.os)), } - const fd = %return os.posixOpen(pathname, posix.O_PATH|posix.O_NONBLOCK|posix.O_CLOEXEC, 0, allocator); - defer os.posixClose(fd); - - var buf: ["/proc/self/fd/-2147483648".len]u8 = undefined; - const proc_path = fmt.bufPrint(buf[0..], "/proc/self/fd/{}", fd); - - return os.readLink(allocator, proc_path); }