authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2018-07-17 18:36:47-04:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2018-07-18 17:43:36-04:00
log3e4a3fa5b7faadaae0a57088baa392e2bb52fe38
tree1d48d805c26c3551a88b865582e76cf25bc8ef23
parentfd3a41dadc92e7b69b409af5f747004996465032

self-hosted: find libc on linux


5 files changed, 326 insertions(+), 61 deletions(-)

src-self-hosted/compilation.zig+25-10
......@@ -28,6 +28,7 @@ const Span = errmsg.Span;
2828const codegen = @import("codegen.zig");
2929const Package = @import("package.zig").Package;
3030const link = @import("link.zig").link;
31const LibCInstallation = @import("libc_installation.zig").LibCInstallation;
3132
3233/// Data that is local to the event loop.
3334pub const EventLoopLocal = struct {
......@@ -37,6 +38,8 @@ pub const EventLoopLocal = struct {
3738 /// TODO pool these so that it doesn't have to lock
3839 prng: event.Locked(std.rand.DefaultPrng),
3940
41 native_libc: event.Future(LibCInstallation),
42
4043 var lazy_init_targets = std.lazyInit(void);
4144
4245 fn init(loop: *event.Loop) !EventLoopLocal {
......@@ -52,6 +55,7 @@ pub const EventLoopLocal = struct {
5255 .loop = loop,
5356 .llvm_handle_pool = std.atomic.Stack(llvm.ContextRef).init(),
5457 .prng = event.Locked(std.rand.DefaultPrng).init(loop, std.rand.DefaultPrng.init(seed)),
58 .native_libc = event.Future(LibCInstallation).init(loop),
5559 };
5660 }
5761
......@@ -78,6 +82,13 @@ pub const EventLoopLocal = struct {
7882
7983 return LlvmHandle{ .node = node };
8084 }
85
86 pub async fn getNativeLibC(self: *EventLoopLocal) !*LibCInstallation {
87 if (await (async self.native_libc.start() catch unreachable)) |ptr| return ptr;
88 try await (async self.native_libc.data.findNative(self.loop) catch unreachable);
89 self.native_libc.resolve();
90 return &self.native_libc.data;
91 }
8192};
8293
8394pub const LlvmHandle = struct {
......@@ -109,11 +120,6 @@ pub const Compilation = struct {
109120
110121 linker_script: ?[]const u8,
111122 cache_dir: []const u8,
112 libc_lib_dir: ?[]const u8,
113 libc_static_lib_dir: ?[]const u8,
114 libc_include_dir: ?[]const u8,
115 msvc_lib_dir: ?[]const u8,
116 kernel32_lib_dir: ?[]const u8,
117123 dynamic_linker: ?[]const u8,
118124 out_h_path: ?[]const u8,
119125
......@@ -318,11 +324,6 @@ pub const Compilation = struct {
318324 .verbose_link = false,
319325
320326 .linker_script = null,
321 .libc_lib_dir = null,
322 .libc_static_lib_dir = null,
323 .libc_include_dir = null,
324 .msvc_lib_dir = null,
325 .kernel32_lib_dir = null,
326327 .dynamic_linker = null,
327328 .out_h_path = null,
328329 .is_test = false,
......@@ -762,10 +763,24 @@ pub const Compilation = struct {
762763 try self.link_libs_list.append(link_lib);
763764 if (is_libc) {
764765 self.libc_link_lib = link_lib;
766
767 // get a head start on looking for the native libc
768 if (self.target == Target.Native) {
769 try async<self.loop.allocator> self.startFindingNativeLibC();
770 }
765771 }
766772 return link_lib;
767773 }
768774
775 /// cancels itself so no need to await or cancel the promise.
776 async fn startFindingNativeLibC(self: *Compilation) void {
777 // we don't care if it fails, we're just trying to kick off the future resolution
778 _ = (await (async self.loop.call(EventLoopLocal.getNativeLibC, self.event_loop_local) catch unreachable)) catch {};
779 suspend |p| {
780 cancel p;
781 }
782 }
783
769784 /// General Purpose Allocator. Must free when done.
770785 fn gpa(self: Compilation) *mem.Allocator {
771786 return self.loop.allocator;
src-self-hosted/libc_installation.zig created+234
......@@ -0,0 +1,234 @@
1const std = @import("std");
2const builtin = @import("builtin");
3const event = std.event;
4
5pub const LibCInstallation = struct {
6 /// The directory that contains `stdlib.h`.
7 /// On Linux, can be found with: `cc -E -Wp,-v -xc /dev/null`
8 include_dir: []const u8,
9
10 /// The directory that contains `crt1.o`.
11 /// On Linux, can be found with `cc -print-file-name=crt1.o`.
12 /// Not needed when targeting MacOS.
13 lib_dir: ?[]const u8,
14
15 /// The directory that contains `crtbegin.o`.
16 /// On Linux, can be found with `cc -print-file-name=crt1.o`.
17 /// Not needed when targeting MacOS or Windows.
18 static_lib_dir: ?[]const u8,
19
20 /// The directory that contains `vcruntime.lib`.
21 /// Only needed when targeting Windows.
22 msvc_lib_dir: ?[]const u8,
23
24 /// The directory that contains `kernel32.lib`.
25 /// Only needed when targeting Windows.
26 kernel32_lib_dir: ?[]const u8,
27
28 pub const Error = error{
29 OutOfMemory,
30 FileSystem,
31 UnableToSpawnCCompiler,
32 CCompilerExitCode,
33 CCompilerCrashed,
34 CCompilerCannotFindHeaders,
35 CCompilerCannotFindCRuntime,
36 LibCStdLibHeaderNotFound,
37 };
38
39 /// Finds the default, native libc.
40 pub async fn findNative(self: *LibCInstallation, loop: *event.Loop) !void {
41 self.* = LibCInstallation{
42 .lib_dir = null,
43 .include_dir = ([*]const u8)(undefined)[0..0],
44 .static_lib_dir = null,
45 .msvc_lib_dir = null,
46 .kernel32_lib_dir = null,
47 };
48 var group = event.Group(Error!void).init(loop);
49 switch (builtin.os) {
50 builtin.Os.windows => {
51 try group.call(findNativeIncludeDirWindows, self, loop);
52 try group.call(findNativeLibDirWindows, self, loop);
53 try group.call(findNativeMsvcLibDir, self, loop);
54 try group.call(findNativeKernel32LibDir, self, loop);
55 },
56 builtin.Os.linux => {
57 try group.call(findNativeIncludeDirLinux, self, loop);
58 try group.call(findNativeLibDirLinux, self, loop);
59 try group.call(findNativeStaticLibDir, self, loop);
60 },
61 builtin.Os.macosx => {
62 try group.call(findNativeIncludeDirMacOS, self, loop);
63 },
64 else => @compileError("unimplemented: find libc for this OS"),
65 }
66 return await (async group.wait() catch unreachable);
67 }
68
69 async fn findNativeIncludeDirLinux(self: *LibCInstallation, loop: *event.Loop) !void {
70 const cc_exe = std.os.getEnvPosix("CC") orelse "cc";
71 const argv = []const []const u8{
72 cc_exe,
73 "-E",
74 "-Wp,-v",
75 "-xc",
76 "/dev/null",
77 };
78 // TODO make this use event loop
79 const errorable_result = std.os.ChildProcess.exec(loop.allocator, argv, null, null, 1024 * 1024);
80 const exec_result = if (std.debug.runtime_safety) blk: {
81 break :blk errorable_result catch unreachable;
82 } else blk: {
83 break :blk errorable_result catch |err| switch (err) {
84 error.OutOfMemory => return error.OutOfMemory,
85 else => return error.UnableToSpawnCCompiler,
86 };
87 };
88 defer {
89 loop.allocator.free(exec_result.stdout);
90 loop.allocator.free(exec_result.stderr);
91 }
92
93 switch (exec_result.term) {
94 std.os.ChildProcess.Term.Exited => |code| {
95 if (code != 0) return error.CCompilerExitCode;
96 },
97 else => {
98 return error.CCompilerCrashed;
99 },
100 }
101
102 var it = std.mem.split(exec_result.stderr, "\n\r");
103 var search_paths = std.ArrayList([]const u8).init(loop.allocator);
104 defer search_paths.deinit();
105 while (it.next()) |line| {
106 if (line.len != 0 and line[0] == ' ') {
107 try search_paths.append(line);
108 }
109 }
110 if (search_paths.len == 0) {
111 return error.CCompilerCannotFindHeaders;
112 }
113
114 // search in reverse order
115 var path_i: usize = 0;
116 while (path_i < search_paths.len) : (path_i += 1) {
117 const search_path_untrimmed = search_paths.at(search_paths.len - path_i - 1);
118 const search_path = std.mem.trimLeft(u8, search_path_untrimmed, " ");
119 const stdlib_path = try std.os.path.join(loop.allocator, search_path, "stdlib.h");
120 defer loop.allocator.free(stdlib_path);
121
122 if (std.os.File.access(loop.allocator, stdlib_path)) |_| {
123 self.include_dir = try std.mem.dupe(loop.allocator, u8, search_path);
124 return;
125 } else |err| switch (err) {
126 error.NotFound, error.PermissionDenied => continue,
127 error.OutOfMemory => return error.OutOfMemory,
128 else => return error.FileSystem,
129 }
130 }
131
132 return error.LibCStdLibHeaderNotFound;
133 }
134
135 async fn findNativeIncludeDirWindows(self: *LibCInstallation, loop: *event.Loop) !void {
136 // TODO
137 //ZigWindowsSDK *sdk = get_windows_sdk(g);
138 //g->libc_include_dir = buf_alloc();
139 //if (os_get_win32_ucrt_include_path(sdk, g->libc_include_dir)) {
140 // fprintf(stderr, "Unable to determine libc include path. --libc-include-dir");
141 // exit(1);
142 //}
143 @panic("TODO");
144 }
145
146 async fn findNativeIncludeDirMacOS(self: *LibCInstallation, loop: *event.Loop) !void {
147 self.include_dir = try std.mem.dupe(loop.allocator, u8, "/usr/include");
148 }
149
150 async fn findNativeLibDirWindows(self: *LibCInstallation, loop: *event.Loop) Error!void {
151 // TODO
152 //ZigWindowsSDK *sdk = get_windows_sdk(g);
153
154 //if (g->msvc_lib_dir == nullptr) {
155 // Buf* vc_lib_dir = buf_alloc();
156 // if (os_get_win32_vcruntime_path(vc_lib_dir, g->zig_target.arch.arch)) {
157 // fprintf(stderr, "Unable to determine vcruntime path. --msvc-lib-dir");
158 // exit(1);
159 // }
160 // g->msvc_lib_dir = vc_lib_dir;
161 //}
162
163 //if (g->libc_lib_dir == nullptr) {
164 // Buf* ucrt_lib_path = buf_alloc();
165 // if (os_get_win32_ucrt_lib_path(sdk, ucrt_lib_path, g->zig_target.arch.arch)) {
166 // fprintf(stderr, "Unable to determine ucrt path. --libc-lib-dir");
167 // exit(1);
168 // }
169 // g->libc_lib_dir = ucrt_lib_path;
170 //}
171
172 //if (g->kernel32_lib_dir == nullptr) {
173 // Buf* kern_lib_path = buf_alloc();
174 // if (os_get_win32_kern32_path(sdk, kern_lib_path, g->zig_target.arch.arch)) {
175 // fprintf(stderr, "Unable to determine kernel32 path. --kernel32-lib-dir");
176 // exit(1);
177 // }
178 // g->kernel32_lib_dir = kern_lib_path;
179 //}
180 @panic("TODO");
181 }
182
183 async fn findNativeLibDirLinux(self: *LibCInstallation, loop: *event.Loop) Error!void {
184 self.lib_dir = try await (async ccPrintFileNameDir(loop, "crt1.o") catch unreachable);
185 }
186
187 async fn findNativeStaticLibDir(self: *LibCInstallation, loop: *event.Loop) Error!void {
188 self.static_lib_dir = try await (async ccPrintFileNameDir(loop, "crtbegin.o") catch unreachable);
189 }
190
191 async fn findNativeMsvcLibDir(self: *LibCInstallation, loop: *event.Loop) Error!void {
192 @panic("TODO");
193 }
194
195 async fn findNativeKernel32LibDir(self: *LibCInstallation, loop: *event.Loop) Error!void {
196 @panic("TODO");
197 }
198};
199
200/// caller owns returned memory
201async fn ccPrintFileNameDir(loop: *event.Loop, o_file: []const u8) ![]u8 {
202 const cc_exe = std.os.getEnvPosix("CC") orelse "cc";
203 const arg1 = try std.fmt.allocPrint(loop.allocator, "-print-file-name={}", o_file);
204 defer loop.allocator.free(arg1);
205 const argv = []const []const u8{ cc_exe, arg1 };
206
207 // TODO evented I/O
208 const errorable_result = std.os.ChildProcess.exec(loop.allocator, argv, null, null, 1024 * 1024);
209 const exec_result = if (std.debug.runtime_safety) blk: {
210 break :blk errorable_result catch unreachable;
211 } else blk: {
212 break :blk errorable_result catch |err| switch (err) {
213 error.OutOfMemory => return error.OutOfMemory,
214 else => return error.UnableToSpawnCCompiler,
215 };
216 };
217 defer {
218 loop.allocator.free(exec_result.stdout);
219 loop.allocator.free(exec_result.stderr);
220 }
221 switch (exec_result.term) {
222 std.os.ChildProcess.Term.Exited => |code| {
223 if (code != 0) return error.CCompilerExitCode;
224 },
225 else => {
226 return error.CCompilerCrashed;
227 },
228 }
229 var it = std.mem.split(exec_result.stdout, "\n\r");
230 const line = it.next() orelse return error.CCompilerCannotFindCRuntime;
231 const dirname = std.os.path.dirname(line) orelse return error.CCompilerCannotFindCRuntime;
232
233 return std.mem.dupe(loop.allocator, u8, dirname);
234}
src-self-hosted/main.zig+40-25
......@@ -31,6 +31,7 @@ const usage =
3131 \\ build-exe [source] Create executable from source or object files
3232 \\ build-lib [source] Create library from source or object files
3333 \\ build-obj [source] Create object from source or assembly
34 \\ find-libc Show native libc installation paths
3435 \\ fmt [source] Parse file and render in canonical zig format
3536 \\ targets List available compilation targets
3637 \\ version Print version number and exit
......@@ -81,6 +82,10 @@ pub fn main() !void {
8182 .name = "build-obj",
8283 .exec = cmdBuildObj,
8384 },
85 Command{
86 .name = "find-libc",
87 .exec = cmdFindLibc,
88 },
8489 Command{
8590 .name = "fmt",
8691 .exec = cmdFmt,
......@@ -134,7 +139,6 @@ const usage_build_generic =
134139 \\ --cache-dir [path] Override the cache directory
135140 \\ --emit [filetype] Emit a specific file format as compilation output
136141 \\ --enable-timing-info Print timing diagnostics
137 \\ --libc-include-dir [path] Directory where libc stdlib.h resides
138142 \\ --name [name] Override output name
139143 \\ --output [file] Override destination path
140144 \\ --output-h [file] Override generated header file path
......@@ -165,10 +169,6 @@ const usage_build_generic =
165169 \\ --ar-path [path] Set the path to ar
166170 \\ --dynamic-linker [path] Set the path to ld.so
167171 \\ --each-lib-rpath Add rpath for each used dynamic library
168 \\ --libc-lib-dir [path] Directory where libc crt1.o resides
169 \\ --libc-static-lib-dir [path] Directory where libc crtbegin.o resides
170 \\ --msvc-lib-dir [path] (windows) directory where vcruntime.lib resides
171 \\ --kernel32-lib-dir [path] (windows) directory where kernel32.lib resides
172172 \\ --library [lib] Link against lib
173173 \\ --forbid-library [lib] Make it an error to link against lib
174174 \\ --library-path [dir] Add a directory to the library search path
......@@ -210,7 +210,6 @@ const args_build_generic = []Flag{
210210 "llvm-ir",
211211 }),
212212 Flag.Bool("--enable-timing-info"),
213 Flag.Arg1("--libc-include-dir"),
214213 Flag.Arg1("--name"),
215214 Flag.Arg1("--output"),
216215 Flag.Arg1("--output-h"),
......@@ -236,10 +235,6 @@ const args_build_generic = []Flag{
236235 Flag.Arg1("--ar-path"),
237236 Flag.Arg1("--dynamic-linker"),
238237 Flag.Bool("--each-lib-rpath"),
239 Flag.Arg1("--libc-lib-dir"),
240 Flag.Arg1("--libc-static-lib-dir"),
241 Flag.Arg1("--msvc-lib-dir"),
242 Flag.Arg1("--kernel32-lib-dir"),
243238 Flag.ArgMergeN("--library", 1),
244239 Flag.ArgMergeN("--forbid-library", 1),
245240 Flag.ArgMergeN("--library-path", 1),
......@@ -430,21 +425,6 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Co
430425
431426 comp.strip = flags.present("strip");
432427
433 if (flags.single("libc-lib-dir")) |libc_lib_dir| {
434 comp.libc_lib_dir = libc_lib_dir;
435 }
436 if (flags.single("libc-static-lib-dir")) |libc_static_lib_dir| {
437 comp.libc_static_lib_dir = libc_static_lib_dir;
438 }
439 if (flags.single("libc-include-dir")) |libc_include_dir| {
440 comp.libc_include_dir = libc_include_dir;
441 }
442 if (flags.single("msvc-lib-dir")) |msvc_lib_dir| {
443 comp.msvc_lib_dir = msvc_lib_dir;
444 }
445 if (flags.single("kernel32-lib-dir")) |kernel32_lib_dir| {
446 comp.kernel32_lib_dir = kernel32_lib_dir;
447 }
448428 if (flags.single("dynamic-linker")) |dynamic_linker| {
449429 comp.dynamic_linker = dynamic_linker;
450430 }
......@@ -579,6 +559,41 @@ const Fmt = struct {
579559 }
580560};
581561
562fn cmdFindLibc(allocator: *Allocator, args: []const []const u8) !void {
563 var loop: event.Loop = undefined;
564 try loop.initMultiThreaded(allocator);
565 defer loop.deinit();
566
567 var event_loop_local = try EventLoopLocal.init(&loop);
568 defer event_loop_local.deinit();
569
570 const handle = try async<loop.allocator> findLibCAsync(&event_loop_local);
571 defer cancel handle;
572
573 loop.run();
574}
575
576async fn findLibCAsync(event_loop_local: *EventLoopLocal) void {
577 const libc = (await (async event_loop_local.getNativeLibC() catch unreachable)) catch |err| {
578 stderr.print("unable to find libc: {}\n", @errorName(err)) catch os.exit(1);
579 os.exit(1);
580 };
581 stderr.print(
582 \\include_dir={}
583 \\lib_dir={}
584 \\static_lib_dir={}
585 \\msvc_lib_dir={}
586 \\kernel32_lib_dir={}
587 \\
588 ,
589 libc.include_dir,
590 libc.lib_dir,
591 libc.static_lib_dir orelse "",
592 libc.msvc_lib_dir orelse "",
593 libc.kernel32_lib_dir orelse "",
594 ) catch os.exit(1);
595}
596
582597fn cmdFmt(allocator: *Allocator, args: []const []const u8) !void {
583598 var flags = try Args.parse(allocator, args_fmt_spec, args);
584599 defer flags.deinit();
std/fmt/index.zig+6-2
......@@ -785,11 +785,15 @@ pub fn bufPrint(buf: []u8, comptime fmt: []const u8, args: ...) ![]u8 {
785785 return buf[0 .. buf.len - context.remaining.len];
786786}
787787
788pub fn allocPrint(allocator: *mem.Allocator, comptime fmt: []const u8, args: ...) ![]u8 {
788pub const AllocPrintError = error{OutOfMemory};
789
790pub fn allocPrint(allocator: *mem.Allocator, comptime fmt: []const u8, args: ...) AllocPrintError![]u8 {
789791 var size: usize = 0;
790792 format(&size, error{}, countSize, fmt, args) catch |err| switch (err) {};
791793 const buf = try allocator.alloc(u8, size);
792 return bufPrint(buf, fmt, args);
794 return bufPrint(buf, fmt, args) catch |err| switch (err) {
795 error.BufferTooSmall => unreachable, // we just counted the size above
796 };
793797}
794798
795799fn countSize(size: *usize, bytes: []const u8) (error{}!void) {
std/os/file.zig+21-24
......@@ -109,43 +109,40 @@ pub const File = struct {
109109 Unexpected,
110110 };
111111
112 pub fn access(allocator: *mem.Allocator, path: []const u8, file_mode: os.FileMode) AccessError!bool {
112 pub fn access(allocator: *mem.Allocator, path: []const u8) AccessError!void {
113113 const path_with_null = try std.cstr.addNullByte(allocator, path);
114114 defer allocator.free(path_with_null);
115115
116116 if (is_posix) {
117 // mode is ignored and is always F_OK for now
118117 const result = posix.access(path_with_null.ptr, posix.F_OK);
119118 const err = posix.getErrno(result);
120 if (err > 0) {
121 return switch (err) {
122 posix.EACCES => error.PermissionDenied,
123 posix.EROFS => error.PermissionDenied,
124 posix.ELOOP => error.PermissionDenied,
125 posix.ETXTBSY => error.PermissionDenied,
126 posix.ENOTDIR => error.NotFound,
127 posix.ENOENT => error.NotFound,
119 switch (err) {
120 0 => return,
121 posix.EACCES => return error.PermissionDenied,
122 posix.EROFS => return error.PermissionDenied,
123 posix.ELOOP => return error.PermissionDenied,
124 posix.ETXTBSY => return error.PermissionDenied,
125 posix.ENOTDIR => return error.NotFound,
126 posix.ENOENT => return error.NotFound,
128127
129 posix.ENAMETOOLONG => error.NameTooLong,
130 posix.EINVAL => error.BadMode,
131 posix.EFAULT => error.BadPathName,
132 posix.EIO => error.Io,
133 posix.ENOMEM => error.SystemResources,
134 else => os.unexpectedErrorPosix(err),
135 };
128 posix.ENAMETOOLONG => return error.NameTooLong,
129 posix.EINVAL => unreachable,
130 posix.EFAULT => return error.BadPathName,
131 posix.EIO => return error.Io,
132 posix.ENOMEM => return error.SystemResources,
133 else => return os.unexpectedErrorPosix(err),
136134 }
137 return true;
138135 } else if (is_windows) {
139136 if (os.windows.GetFileAttributesA(path_with_null.ptr) != os.windows.INVALID_FILE_ATTRIBUTES) {
140 return true;
137 return;
141138 }
142139
143140 const err = windows.GetLastError();
144 return switch (err) {
145 windows.ERROR.FILE_NOT_FOUND => error.NotFound,
146 windows.ERROR.ACCESS_DENIED => error.PermissionDenied,
147 else => os.unexpectedErrorWindows(err),
148 };
141 switch (err) {
142 windows.ERROR.FILE_NOT_FOUND => return error.NotFound,
143 windows.ERROR.ACCESS_DENIED => return error.PermissionDenied,
144 else => return os.unexpectedErrorWindows(err),
145 }
149146 } else {
150147 @compileError("TODO implement access for this OS");
151148 }