authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-02-16 13:25:30-05:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-02-16 13:25:30-05:00
log4b02a39aa93b0043f05de0d90443051c019643ab
tree12632d2e43bc6e13911d8e88d1f91e84b11106f6
parent5e37fc0746a75ed319fc57ae62d8cc966382c592
signaturelock-open Commit is signed but in an unrecognized format.

self-hosted libc detection

* libc_installation.cpp is deleted. src-self-hosted/libc_installation.zig is now used for both stage1 and stage2 compilers. * (breaking) move `std.fs.File.access` to `std.fs.Dir.access`. The API now encourages use with an open directory handle. * Add `std.os.faccessat` and related functions. * Deprecate the "C" suffix naming convention for null-terminated parameters. "C" should be used when it is related to libc. However null-terminated parameters often have to do with the native system ABI rather than libc. "Z" suffix is the new convention. For example, `std.os.openC` is deprecated in favor of `std.os.openZ`. * Add `std.mem.dupeZ` for using an allocator to copy memory and add a null terminator. * Remove dead struct field `std.ChildProcess.llnode`. * Introduce `std.event.Batch`. This API allows expressing concurrency without forcing code to be async. It requires no Allocator and does not introduce any failure conditions. However it is not thread-safe. * There is now an ongoing experiment to transition away from `std.event.Group` in favor of `std.event.Batch`. * `std.os.execvpeC` calls `getenvZ` rather than `getenv`. This is slightly more efficient on most systems, and works around a limitation of `getenv` lack of integration with libc. * (breaking) `std.os.AccessError` gains `FileBusy`, `SymLinkLoop`, and `ReadOnlyFileSystem`. Previously these error codes were all reported as `PermissionDenied`. * Add `std.Target.isDragonFlyBSD`. * stage2: access to the windows_sdk functions is done with a manually maintained .zig binding file instead of `@cImport`. * Update src-self-hosted/libc_installation.zig with all the improvements that stage1 has seen to src/libc_installation.cpp until now. In addition, it now takes advantage of Batch so that evented I/O mode takes advantage of concurrency, but it still works in blocking I/O mode, which is how it is used in stage1.

31 files changed, 931 insertions(+), 1019 deletions(-)

CMakeLists.txt-1
......@@ -457,7 +457,6 @@ set(ZIG_SOURCES
457457 "${CMAKE_SOURCE_DIR}/src/heap.cpp"
458458 "${CMAKE_SOURCE_DIR}/src/ir.cpp"
459459 "${CMAKE_SOURCE_DIR}/src/ir_print.cpp"
460 "${CMAKE_SOURCE_DIR}/src/libc_installation.cpp"
461460 "${CMAKE_SOURCE_DIR}/src/link.cpp"
462461 "${CMAKE_SOURCE_DIR}/src/mem.cpp"
463462 "${CMAKE_SOURCE_DIR}/src/os.cpp"
build.zig+1-1
......@@ -175,7 +175,7 @@ fn dependOnLib(b: *Builder, lib_exe_obj: var, dep: LibraryDep) void {
175175}
176176
177177fn fileExists(filename: []const u8) !bool {
178 fs.File.access(filename) catch |err| switch (err) {
178 fs.cwd().access(filename, .{}) catch |err| switch (err) {
179179 error.FileNotFound => return false,
180180 else => return err,
181181 };
lib/std/c.zig+1
......@@ -96,6 +96,7 @@ pub extern "c" fn getcwd(buf: [*]u8, size: usize) ?[*]u8;
9696pub extern "c" fn waitpid(pid: c_int, stat_loc: *c_uint, options: c_uint) c_int;
9797pub extern "c" fn fork() c_int;
9898pub extern "c" fn access(path: [*:0]const u8, mode: c_uint) c_int;
99pub extern "c" fn faccessat(dirfd: fd_t, path: [*:0]const u8, mode: c_uint, flags: c_uint) c_int;
99100pub extern "c" fn pipe(fds: *[2]fd_t) c_int;
100101pub extern "c" fn pipe2(fds: *[2]fd_t, flags: u32) c_int;
101102pub extern "c" fn mkdir(path: [*:0]const u8, mode: c_uint) c_int;
lib/std/child_process.zig-3
......@@ -48,7 +48,6 @@ pub const ChildProcess = struct {
4848 cwd: ?[]const u8,
4949
5050 err_pipe: if (builtin.os == .windows) void else [2]os.fd_t,
51 llnode: if (builtin.os == .windows) void else TailQueue(*ChildProcess).Node,
5251
5352 pub const SpawnError = error{
5453 OutOfMemory,
......@@ -90,7 +89,6 @@ pub const ChildProcess = struct {
9089 .handle = undefined,
9190 .thread_handle = undefined,
9291 .err_pipe = undefined,
93 .llnode = undefined,
9492 .term = null,
9593 .env_map = null,
9694 .cwd = null,
......@@ -453,7 +451,6 @@ pub const ChildProcess = struct {
453451
454452 self.pid = pid;
455453 self.err_pipe = err_pipe;
456 self.llnode = TailQueue(*ChildProcess).Node.init(self);
457454 self.term = null;
458455
459456 if (self.stdin_behavior == StdIo.Pipe) {
lib/std/event.zig+2
......@@ -1,6 +1,7 @@
11pub const Channel = @import("event/channel.zig").Channel;
22pub const Future = @import("event/future.zig").Future;
33pub const Group = @import("event/group.zig").Group;
4pub const Batch = @import("event/batch.zig").Batch;
45pub const Lock = @import("event/lock.zig").Lock;
56pub const Locked = @import("event/locked.zig").Locked;
67pub const RwLock = @import("event/rwlock.zig").RwLock;
......@@ -11,6 +12,7 @@ test "import event tests" {
1112 _ = @import("event/channel.zig");
1213 _ = @import("event/future.zig");
1314 _ = @import("event/group.zig");
15 _ = @import("event/batch.zig");
1416 _ = @import("event/lock.zig");
1517 _ = @import("event/locked.zig");
1618 _ = @import("event/rwlock.zig");
lib/std/event/batch.zig created+139
......@@ -0,0 +1,139 @@
1const std = @import("../std.zig");
2const testing = std.testing;
3
4/// Performs multiple async functions in parallel, without heap allocation.
5/// Async function frames are managed externally to this abstraction, and
6/// passed in via the `add` function. Once all the jobs are added, call `wait`.
7/// This API is *not* thread-safe. The object must be accessed from one thread at
8/// a time, however, it need not be the same thread.
9pub fn Batch(
10 /// The return value for each job.
11 /// If a job slot was re-used due to maxed out concurrency, then its result
12 /// value will be overwritten. The values can be accessed with the `results` field.
13 comptime Result: type,
14 /// How many jobs to run in parallel.
15 comptime max_jobs: comptime_int,
16 /// Controls whether the `add` and `wait` functions will be async functions.
17 comptime async_behavior: enum {
18 /// Observe the value of `std.io.is_async` to decide whether `add`
19 /// and `wait` will be async functions. Asserts that the jobs do not suspend when
20 /// `std.io.mode == .blocking`. This is a generally safe assumption, and the
21 /// usual recommended option for this parameter.
22 auto_async,
23
24 /// Always uses the `noasync` keyword when using `await` on the jobs,
25 /// making `add` and `wait` non-async functions. Asserts that the jobs do not suspend.
26 never_async,
27
28 /// `add` and `wait` use regular `await` keyword, making them async functions.
29 always_async,
30 },
31) type {
32 return struct {
33 jobs: [max_jobs]Job,
34 next_job_index: usize,
35 collected_result: CollectedResult,
36
37 const Job = struct {
38 frame: ?anyframe->Result,
39 result: Result,
40 };
41
42 const Self = @This();
43
44 const CollectedResult = switch (@typeInfo(Result)) {
45 .ErrorUnion => Result,
46 else => void,
47 };
48
49 const async_ok = switch (async_behavior) {
50 .auto_async => std.io.is_async,
51 .never_async => false,
52 .always_async => true,
53 };
54
55 pub fn init() Self {
56 return Self{
57 .jobs = [1]Job{
58 .{
59 .frame = null,
60 .result = undefined,
61 },
62 } ** max_jobs,
63 .next_job_index = 0,
64 .collected_result = {},
65 };
66 }
67
68 /// Add a frame to the Batch. If all jobs are in-flight, then this function
69 /// waits until one completes.
70 /// This function is *not* thread-safe. It must be called from one thread at
71 /// a time, however, it need not be the same thread.
72 /// TODO: "select" language feature to use the next available slot, rather than
73 /// awaiting the next index.
74 pub fn add(self: *Self, frame: anyframe->Result) void {
75 const job = &self.jobs[self.next_job_index];
76 self.next_job_index = (self.next_job_index + 1) % max_jobs;
77 if (job.frame) |existing| {
78 job.result = if (async_ok) await existing else noasync await existing;
79 if (CollectedResult != void) {
80 job.result catch |err| {
81 self.collected_result = err;
82 };
83 }
84 }
85 job.frame = frame;
86 }
87
88 /// Wait for all the jobs to complete.
89 /// Safe to call any number of times.
90 /// If `Result` is an error union, this function returns the last error that occurred, if any.
91 /// Unlike the `results` field, the return value of `wait` will report any error that occurred;
92 /// hitting max parallelism will not compromise the result.
93 /// This function is *not* thread-safe. It must be called from one thread at
94 /// a time, however, it need not be the same thread.
95 pub fn wait(self: *Self) CollectedResult {
96 for (self.jobs) |*job| if (job.frame) |f| {
97 job.result = if (async_ok) await f else noasync await f;
98 if (CollectedResult != void) {
99 job.result catch |err| {
100 self.collected_result = err;
101 };
102 }
103 job.frame = null;
104 };
105 return self.collected_result;
106 }
107 };
108}
109
110test "std.event.Batch" {
111 var count: usize = 0;
112 var batch = Batch(void, 2).init();
113 batch.add(&async sleepALittle(&count));
114 batch.add(&async increaseByTen(&count));
115 batch.wait();
116 testing.expect(count == 11);
117
118 var another = Batch(anyerror!void, 2).init();
119 another.add(&async somethingElse());
120 another.add(&async doSomethingThatFails());
121 testing.expectError(error.ItBroke, another.wait());
122}
123
124fn sleepALittle(count: *usize) void {
125 std.time.sleep(1 * std.time.millisecond);
126 _ = @atomicRmw(usize, count, .Add, 1, .SeqCst);
127}
128
129fn increaseByTen(count: *usize) void {
130 var i: usize = 0;
131 while (i < 10) : (i += 1) {
132 _ = @atomicRmw(usize, count, .Add, 1, .SeqCst);
133 }
134}
135
136fn doSomethingThatFails() anyerror!void {}
137fn somethingElse() anyerror!void {
138 return error.ItBroke;
139}
lib/std/event/group.zig+5
......@@ -5,6 +5,11 @@ const testing = std.testing;
55const Allocator = std.mem.Allocator;
66
77/// ReturnType must be `void` or `E!void`
8/// TODO This API was created back with the old design of async/await, when calling any
9/// async function required an allocator. There is an ongoing experiment to transition
10/// all uses of this API to the simpler and more resource-aware `std.event.Batch` API.
11/// If the transition goes well, all usages of `Group` will be gone, and this API
12/// will be deleted.
813pub fn Group(comptime ReturnType: type) type {
914 return struct {
1015 frame_stack: Stack,
lib/std/fs.zig+32
......@@ -1323,6 +1323,38 @@ pub const Dir = struct {
13231323 defer file.close();
13241324 try file.write(data);
13251325 }
1326
1327 pub const AccessError = os.AccessError;
1328
1329 /// Test accessing `path`.
1330 /// `path` is UTF8-encoded.
1331 /// Be careful of Time-Of-Check-Time-Of-Use race conditions when using this function.
1332 /// For example, instead of testing if a file exists and then opening it, just
1333 /// open it and handle the error for file not found.
1334 pub fn access(self: Dir, sub_path: []const u8, flags: File.OpenFlags) AccessError!void {
1335 const path_c = try os.toPosixPath(sub_path);
1336 return self.accessZ(&path_c, flags);
1337 }
1338
1339 /// Same as `access` except the path parameter is null-terminated.
1340 pub fn accessZ(self: Dir, sub_path: [*:0]const u8, flags: File.OpenFlags) AccessError!void {
1341 const os_mode = if (flags.write and flags.read)
1342 @as(u32, os.R_OK | os.W_OK)
1343 else if (flags.write)
1344 @as(u32, os.W_OK)
1345 else
1346 @as(u32, os.F_OK);
1347 const result = if (need_async_thread)
1348 std.event.Loop.instance.?.faccessatZ(self.fd, sub_path, os_mode)
1349 else
1350 os.faccessatZ(self.fd, sub_path, os_mode, 0);
1351 return result;
1352 }
1353
1354 /// Same as `access` except the parameter is null-terminated UTF16LE-encoded.
1355 pub fn accessW(self: Dir, sub_path: [*:0]const u16, flags: File.OpenFlags) AccessError!void {
1356 return os.faccessatW(self.fd, sub_path, 0, 0);
1357 }
13261358};
13271359
13281360/// Returns an handle to the current working directory that is open for traversal.
lib/std/fs/file.zig-25
......@@ -60,31 +60,6 @@ pub const File = struct {
6060 mode: Mode = default_mode,
6161 };
6262
63 /// Test for the existence of `path`.
64 /// `path` is UTF8-encoded.
65 /// In general it is recommended to avoid this function. For example,
66 /// instead of testing if a file exists and then opening it, just
67 /// open it and handle the error for file not found.
68 /// TODO: deprecate this and move it to `std.fs.Dir`.
69 /// TODO: integrate with async I/O
70 pub fn access(path: []const u8) !void {
71 return os.access(path, os.F_OK);
72 }
73
74 /// Same as `access` except the parameter is null-terminated.
75 /// TODO: deprecate this and move it to `std.fs.Dir`.
76 /// TODO: integrate with async I/O
77 pub fn accessC(path: [*:0]const u8) !void {
78 return os.accessC(path, os.F_OK);
79 }
80
81 /// Same as `access` except the parameter is null-terminated UTF16LE-encoded.
82 /// TODO: deprecate this and move it to `std.fs.Dir`.
83 /// TODO: integrate with async I/O
84 pub fn accessW(path: [*:0]const u16) !void {
85 return os.accessW(path, os.F_OK);
86 }
87
8863 /// Upon success, the stream is in an uninitialized state. To continue using it,
8964 /// you must use the open() function.
9065 pub fn close(self: File) void {
lib/std/mem.zig+9-1
......@@ -387,13 +387,21 @@ pub fn allEqual(comptime T: type, slice: []const T, scalar: T) bool {
387387 return true;
388388}
389389
390/// Copies ::m to newly allocated memory. Caller is responsible to free it.
390/// Copies `m` to newly allocated memory. Caller owns the memory.
391391pub fn dupe(allocator: *Allocator, comptime T: type, m: []const T) ![]T {
392392 const new_buf = try allocator.alloc(T, m.len);
393393 copy(T, new_buf, m);
394394 return new_buf;
395395}
396396
397/// Copies `m` to newly allocated memory, with a null-terminated element. Caller owns the memory.
398pub fn dupeZ(allocator: *Allocator, comptime T: type, m: []const T) ![:0]T {
399 const new_buf = try allocator.alloc(T, m.len + 1);
400 copy(T, new_buf, m);
401 new_buf[m.len] = 0;
402 return new_buf[0..m.len :0];
403}
404
397405/// Remove values from the beginning of a slice.
398406pub fn trimLeft(comptime T: type, slice: []const T, values_to_strip: []const T) []const T {
399407 var begin: usize = 0;
lib/std/os.zig+57-8
......@@ -950,7 +950,7 @@ pub fn execvpeC(file: [*:0]const u8, child_argv: [*:null]const ?[*:0]const u8, e
950950 const file_slice = mem.toSliceConst(u8, file);
951951 if (mem.indexOfScalar(u8, file_slice, '/') != null) return execveC(file, child_argv, envp);
952952
953 const PATH = getenv("PATH") orelse "/usr/local/bin:/bin/:/usr/bin";
953 const PATH = getenvZ("PATH") orelse "/usr/local/bin:/bin/:/usr/bin";
954954 var path_buf: [MAX_PATH_BYTES]u8 = undefined;
955955 var it = mem.tokenize(PATH, ":");
956956 var seen_eacces = false;
......@@ -1038,7 +1038,7 @@ pub fn freeNullDelimitedEnvMap(allocator: *mem.Allocator, envp_buf: []?[*:0]u8)
10381038}
10391039
10401040/// Get an environment variable.
1041/// See also `getenvC`.
1041/// See also `getenvZ`.
10421042/// TODO make this go through libc when we have it
10431043pub fn getenv(key: []const u8) ?[]const u8 {
10441044 for (environ) |ptr| {
......@@ -1056,9 +1056,12 @@ pub fn getenv(key: []const u8) ?[]const u8 {
10561056 return null;
10571057}
10581058
1059/// Deprecated in favor of `getenvZ`.
1060pub const getenvC = getenvZ;
1061
10591062/// Get an environment variable with a null-terminated name.
10601063/// See also `getenv`.
1061pub fn getenvC(key: [*:0]const u8) ?[]const u8 {
1064pub fn getenvZ(key: [*:0]const u8) ?[]const u8 {
10621065 if (builtin.link_libc) {
10631066 const value = system.getenv(key) orelse return null;
10641067 return mem.toSliceConst(u8, value);
......@@ -2452,6 +2455,9 @@ pub const AccessError = error{
24522455 InputOutput,
24532456 SystemResources,
24542457 BadPathName,
2458 FileBusy,
2459 SymLinkLoop,
2460 ReadOnlyFileSystem,
24552461
24562462 /// On Windows, file paths must be valid Unicode.
24572463 InvalidUtf8,
......@@ -2469,8 +2475,11 @@ pub fn access(path: []const u8, mode: u32) AccessError!void {
24692475 return accessC(&path_c, mode);
24702476}
24712477
2478/// Deprecated in favor of `accessZ`.
2479pub const accessC = accessZ;
2480
24722481/// Same as `access` except `path` is null-terminated.
2473pub fn accessC(path: [*:0]const u8, mode: u32) AccessError!void {
2482pub fn accessZ(path: [*:0]const u8, mode: u32) AccessError!void {
24742483 if (builtin.os == .windows) {
24752484 const path_w = try windows.cStrToPrefixedFileW(path);
24762485 _ = try windows.GetFileAttributesW(&path_w);
......@@ -2479,12 +2488,11 @@ pub fn accessC(path: [*:0]const u8, mode: u32) AccessError!void {
24792488 switch (errno(system.access(path, mode))) {
24802489 0 => return,
24812490 EACCES => return error.PermissionDenied,
2482 EROFS => return error.PermissionDenied,
2483 ELOOP => return error.PermissionDenied,
2484 ETXTBSY => return error.PermissionDenied,
2491 EROFS => return error.ReadOnlyFileSystem,
2492 ELOOP => return error.SymLinkLoop,
2493 ETXTBSY => return error.FileBusy,
24852494 ENOTDIR => return error.FileNotFound,
24862495 ENOENT => return error.FileNotFound,
2487
24882496 ENAMETOOLONG => return error.NameTooLong,
24892497 EINVAL => unreachable,
24902498 EFAULT => unreachable,
......@@ -2510,6 +2518,47 @@ pub fn accessW(path: [*:0]const u16, mode: u32) windows.GetFileAttributesError!v
25102518 }
25112519}
25122520
2521/// Check user's permissions for a file, based on an open directory handle.
2522/// TODO currently this ignores `mode` and `flags` on Windows.
2523pub fn faccessat(dirfd: fd_t, path: []const u8, mode: u32, flags: u32) AccessError!void {
2524 if (builtin.os == .windows) {
2525 const path_w = try windows.sliceToPrefixedFileW(path);
2526 return faccessatW(dirfd, &path_w, mode, flags);
2527 }
2528 const path_c = try toPosixPath(path);
2529 return faccessatZ(dirfd, &path_c, mode, flags);
2530}
2531
2532/// Same as `faccessat` except the path parameter is null-terminated.
2533pub fn faccessatZ(dirfd: fd_t, path: [*:0]const u8, mode: u32, flags: u32) AccessError!void {
2534 if (builtin.os == .windows) {
2535 const path_w = try windows.cStrToPrefixedFileW(path);
2536 return faccessatW(dirfd, &path_w, mode, flags);
2537 }
2538 switch (errno(system.faccessat(dirfd, path, mode, flags))) {
2539 0 => return,
2540 EACCES => return error.PermissionDenied,
2541 EROFS => return error.ReadOnlyFileSystem,
2542 ELOOP => return error.SymLinkLoop,
2543 ETXTBSY => return error.FileBusy,
2544 ENOTDIR => return error.FileNotFound,
2545 ENOENT => return error.FileNotFound,
2546 ENAMETOOLONG => return error.NameTooLong,
2547 EINVAL => unreachable,
2548 EFAULT => unreachable,
2549 EIO => return error.InputOutput,
2550 ENOMEM => return error.SystemResources,
2551 else => |err| return unexpectedErrno(err),
2552 }
2553}
2554
2555/// Same as `faccessat` except asserts the target is Windows and the path parameter
2556/// is null-terminated WTF-16 encoded.
2557/// TODO currently this ignores `mode` and `flags`
2558pub fn faccessatW(dirfd: fd_t, path: [*:0]const u16, mode: u32, flags: u32) AccessError!void {
2559 @compileError("TODO implement faccessatW on Windows");
2560}
2561
25132562pub const PipeError = error{
25142563 SystemFdQuotaExceeded,
25152564 ProcessFdQuotaExceeded,
lib/std/os/test.zig+1-1
......@@ -29,7 +29,7 @@ test "makePath, put some files in it, deleteTree" {
2929
3030test "access file" {
3131 try fs.makePath(a, "os_test_tmp");
32 if (File.access("os_test_tmp" ++ fs.path.sep_str ++ "file.txt")) |ok| {
32 if (fs.cwd().access("os_test_tmp" ++ fs.path.sep_str ++ "file.txt", .{})) |ok| {
3333 @panic("expected error");
3434 } else |err| {
3535 expect(err == error.FileNotFound);
lib/std/target.zig+7
......@@ -1037,6 +1037,13 @@ pub const Target = union(enum) {
10371037 };
10381038 }
10391039
1040 pub fn isDragonFlyBSD(self: Target) bool {
1041 return switch (self.getOs()) {
1042 .dragonfly => true,
1043 else => false,
1044 };
1045 }
1046
10401047 pub fn isUefi(self: Target) bool {
10411048 return switch (self.getOs()) {
10421049 .uefi => true,
lib/std/time.zig+1
......@@ -8,6 +8,7 @@ const math = std.math;
88pub const epoch = @import("time/epoch.zig");
99
1010/// Spurious wakeups are possible and no precision of timing is guaranteed.
11/// TODO integrate with evented I/O
1112pub fn sleep(nanoseconds: u64) void {
1213 if (builtin.os == .windows) {
1314 const ns_per_ms = ns_per_s / ms_per_s;
src-self-hosted/c.zig-1
......@@ -4,5 +4,4 @@ pub usingnamespace @cImport({
44 @cInclude("inttypes.h");
55 @cInclude("config.h");
66 @cInclude("zig_llvm.h");
7 @cInclude("windows_sdk.h");
87});
src-self-hosted/libc_installation.zig+300-271
......@@ -1,20 +1,29 @@
11const std = @import("std");
22const builtin = @import("builtin");
3const event = std.event;
43const util = @import("util.zig");
54const Target = std.Target;
6const c = @import("c.zig");
75const fs = std.fs;
86const Allocator = std.mem.Allocator;
7const Batch = std.event.Batch;
8
9const is_darwin = Target.current.isDarwin();
10const is_windows = Target.current.isWindows();
11const is_freebsd = Target.current.isFreeBSD();
12const is_netbsd = Target.current.isNetBSD();
13const is_linux = Target.current.isLinux();
14const is_dragonfly = Target.current.isDragonFlyBSD();
15const is_gnu = Target.current.isGnu();
16
17usingnamespace @import("windows_sdk.zig");
918
1019/// See the render function implementation for documentation of the fields.
1120pub const LibCInstallation = struct {
12 include_dir: []const u8,
13 lib_dir: ?[]const u8,
14 static_lib_dir: ?[]const u8,
15 msvc_lib_dir: ?[]const u8,
16 kernel32_lib_dir: ?[]const u8,
17 dynamic_linker_path: ?[]const u8,
21 include_dir: ?[:0]const u8 = null,
22 sys_include_dir: ?[:0]const u8 = null,
23 crt_dir: ?[:0]const u8 = null,
24 static_crt_dir: ?[:0]const u8 = null,
25 msvc_lib_dir: ?[:0]const u8 = null,
26 kernel32_lib_dir: ?[:0]const u8 = null,
1827
1928 pub const FindError = error{
2029 OutOfMemory,
......@@ -30,28 +39,20 @@ pub const LibCInstallation = struct {
3039 };
3140
3241 pub fn parse(
33 self: *LibCInstallation,
3442 allocator: *Allocator,
3543 libc_file: []const u8,
3644 stderr: *std.io.OutStream(fs.File.WriteError),
37 ) !void {
38 self.initEmpty();
39
40 const keys = [_][]const u8{
41 "include_dir",
42 "lib_dir",
43 "static_lib_dir",
44 "msvc_lib_dir",
45 "kernel32_lib_dir",
46 "dynamic_linker_path",
47 };
45 ) !LibCInstallation {
46 var self: LibCInstallation = .{};
47
48 const fields = std.meta.fields(LibCInstallation);
4849 const FoundKey = struct {
4950 found: bool,
50 allocated: ?[]u8,
51 allocated: ?[:0]u8,
5152 };
52 var found_keys = [1]FoundKey{FoundKey{ .found = false, .allocated = null }} ** keys.len;
53 var found_keys = [1]FoundKey{FoundKey{ .found = false, .allocated = null }} ** fields.len;
5354 errdefer {
54 self.initEmpty();
55 self = .{};
5556 for (found_keys) |found_key| {
5657 if (found_key.allocated) |s| allocator.free(s);
5758 }
......@@ -69,152 +70,188 @@ pub const LibCInstallation = struct {
6970 return error.ParseError;
7071 };
7172 const value = line_it.rest();
72 inline for (keys) |key, i| {
73 if (std.mem.eql(u8, name, key)) {
73 inline for (fields) |field, i| {
74 if (std.mem.eql(u8, name, field.name)) {
7475 found_keys[i].found = true;
75 switch (@typeInfo(@TypeOf(@field(self, key)))) {
76 .Optional => {
77 if (value.len == 0) {
78 @field(self, key) = null;
79 } else {
80 found_keys[i].allocated = try std.mem.dupe(allocator, u8, value);
81 @field(self, key) = found_keys[i].allocated;
82 }
83 },
84 else => {
85 if (value.len == 0) {
86 try stderr.print("field cannot be empty: {}\n", .{key});
87 return error.ParseError;
88 }
89 const dupe = try std.mem.dupe(allocator, u8, value);
90 found_keys[i].allocated = dupe;
91 @field(self, key) = dupe;
92 },
76 if (value.len == 0) {
77 @field(self, field.name) = null;
78 } else {
79 found_keys[i].allocated = try std.mem.dupeZ(allocator, u8, value);
80 @field(self, field.name) = found_keys[i].allocated;
9381 }
9482 break;
9583 }
9684 }
9785 }
98 for (found_keys) |found_key, i| {
99 if (!found_key.found) {
100 try stderr.print("missing field: {}\n", .{keys[i]});
86 inline for (fields) |field, i| {
87 if (!found_keys[i].found) {
88 try stderr.print("missing field: {}\n", .{field.name});
10189 return error.ParseError;
10290 }
10391 }
92 if (self.include_dir == null) {
93 try stderr.print("include_dir may not be empty\n", .{});
94 return error.ParseError;
95 }
96 if (self.sys_include_dir == null) {
97 try stderr.print("sys_include_dir may not be empty\n", .{});
98 return error.ParseError;
99 }
100 if (self.crt_dir == null and is_darwin) {
101 try stderr.print("crt_dir may not be empty for {}\n", .{@tagName(Target.current.getOs())});
102 return error.ParseError;
103 }
104 if (self.static_crt_dir == null and is_windows and is_gnu) {
105 try stderr.print("static_crt_dir may not be empty for {}-{}\n", .{
106 @tagName(Target.current.getOs()),
107 @tagName(Target.current.getAbi()),
108 });
109 return error.ParseError;
110 }
111 if (self.msvc_lib_dir == null and is_windows and !is_gnu) {
112 try stderr.print("msvc_lib_dir may not be empty for {}-{}\n", .{
113 @tagName(Target.current.getOs()),
114 @tagName(Target.current.getAbi()),
115 });
116 return error.ParseError;
117 }
118 if (self.kernel32_lib_dir == null and is_windows and !is_gnu) {
119 try stderr.print("kernel32_lib_dir may not be empty for {}-{}\n", .{
120 @tagName(Target.current.getOs()),
121 @tagName(Target.current.getAbi()),
122 });
123 return error.ParseError;
124 }
125
126 return self;
104127 }
105128
106 pub fn render(self: *const LibCInstallation, out: *std.io.OutStream(fs.File.WriteError)) !void {
129 pub fn render(self: LibCInstallation, out: *std.io.OutStream(fs.File.WriteError)) !void {
107130 @setEvalBranchQuota(4000);
108 const lib_dir = self.lib_dir orelse "";
109 const static_lib_dir = self.static_lib_dir orelse "";
131 const include_dir = self.include_dir orelse "";
132 const sys_include_dir = self.sys_include_dir orelse "";
133 const crt_dir = self.crt_dir orelse "";
134 const static_crt_dir = self.static_crt_dir orelse "";
110135 const msvc_lib_dir = self.msvc_lib_dir orelse "";
111136 const kernel32_lib_dir = self.kernel32_lib_dir orelse "";
112 const dynamic_linker_path = self.dynamic_linker_path orelse util.getDynamicLinkerPath(Target{ .Native = {} });
137
113138 try out.print(
114139 \\# The directory that contains `stdlib.h`.
115 \\# On Linux, can be found with: `cc -E -Wp,-v -xc /dev/null`
140 \\# On POSIX-like systems, include directories be found with: `cc -E -Wp,-v -xc /dev/null`
116141 \\include_dir={}
117142 \\
118 \\# The directory that contains `crt1.o`.
119 \\# On Linux, can be found with `cc -print-file-name=crt1.o`.
143 \\# The system-specific include directory. May be the same as `include_dir`.
144 \\# On Windows it's the directory that includes `vcruntime.h`.
145 \\# On POSIX it's the directory that includes `sys/errno.h`.
146 \\sys_include_dir={}
147 \\
148 \\# The directory that contains `crt1.o` or `crt2.o`.
149 \\# On POSIX, can be found with `cc -print-file-name=crt1.o`.
120150 \\# Not needed when targeting MacOS.
121 \\lib_dir={}
151 \\crt_dir={}
122152 \\
123153 \\# The directory that contains `crtbegin.o`.
124 \\# On Linux, can be found with `cc -print-file-name=crtbegin.o`.
125 \\# Not needed when targeting MacOS or Windows.
126 \\static_lib_dir={}
154 \\# On POSIX, can be found with `cc -print-file-name=crtbegin.o`.
155 \\# Not needed when targeting MacOS.
156 \\static_crt_dir={}
127157 \\
128158 \\# The directory that contains `vcruntime.lib`.
129 \\# Only needed when targeting Windows.
159 \\# Only needed when targeting MSVC on Windows.
130160 \\msvc_lib_dir={}
131161 \\
132162 \\# The directory that contains `kernel32.lib`.
133 \\# Only needed when targeting Windows.
163 \\# Only needed when targeting MSVC on Windows.
134164 \\kernel32_lib_dir={}
135165 \\
136 \\# The full path to the dynamic linker, on the target system.
137 \\# Only needed when targeting Linux.
138 \\dynamic_linker_path={}
139 \\
140 , .{ self.include_dir, lib_dir, static_lib_dir, msvc_lib_dir, kernel32_lib_dir, dynamic_linker_path });
166 , .{
167 include_dir,
168 sys_include_dir,
169 crt_dir,
170 static_crt_dir,
171 msvc_lib_dir,
172 kernel32_lib_dir,
173 });
141174 }
142175
143176 /// Finds the default, native libc.
144 pub fn findNative(self: *LibCInstallation, allocator: *Allocator) !void {
145 self.initEmpty();
146 var group = event.Group(FindError!void).init(allocator);
147 errdefer group.wait() catch {};
148 var windows_sdk: ?*c.ZigWindowsSDK = null;
149 errdefer if (windows_sdk) |sdk| c.zig_free_windows_sdk(@ptrCast(?[*]c.ZigWindowsSDK, sdk));
150
151 switch (builtin.os) {
152 .windows => {
153 var sdk: *c.ZigWindowsSDK = undefined;
154 switch (c.zig_find_windows_sdk(@ptrCast(?[*]?[*]c.ZigWindowsSDK, &sdk))) {
155 c.ZigFindWindowsSdkError.None => {
156 windows_sdk = sdk;
157
158 if (sdk.msvc_lib_dir_ptr != 0) {
159 self.msvc_lib_dir = try std.mem.dupe(allocator, u8, sdk.msvc_lib_dir_ptr[0..sdk.msvc_lib_dir_len]);
160 }
161 try group.call(findNativeKernel32LibDir, .{ allocator, self, sdk });
162 try group.call(findNativeIncludeDirWindows, .{ self, allocator, sdk });
163 try group.call(findNativeLibDirWindows, .{ self, allocator, sdk });
177 pub fn findNative(allocator: *Allocator) !LibCInstallation {
178 var self: LibCInstallation = .{};
179
180 if (is_windows) {
181 if (is_gnu) {
182 var batch = Batch(FindError!void, 3, .auto_async).init();
183 batch.add(&async self.findNativeIncludeDirPosix(allocator));
184 batch.add(&async self.findNativeCrtDirPosix(allocator));
185 batch.add(&async self.findNativeStaticCrtDirPosix(allocator));
186 try batch.wait();
187 } else {
188 var sdk: *ZigWindowsSDK = undefined;
189 switch (zig_find_windows_sdk(&sdk)) {
190 .None => {
191 defer zig_free_windows_sdk(sdk);
192
193 var batch = Batch(FindError!void, 5, .auto_async).init();
194 batch.add(&async self.findNativeMsvcIncludeDir(allocator, sdk));
195 batch.add(&async self.findNativeMsvcLibDir(allocator, sdk));
196 batch.add(&async self.findNativeKernel32LibDir(allocator, sdk));
197 batch.add(&async self.findNativeIncludeDirWindows(allocator, sdk));
198 batch.add(&async self.findNativeCrtDirWindows(allocator, sdk));
199 try batch.wait();
164200 },
165 c.ZigFindWindowsSdkError.OutOfMemory => return error.OutOfMemory,
166 c.ZigFindWindowsSdkError.NotFound => return error.NotFound,
167 c.ZigFindWindowsSdkError.PathTooLong => return error.NotFound,
201 .OutOfMemory => return error.OutOfMemory,
202 .NotFound => return error.NotFound,
203 .PathTooLong => return error.NotFound,
168204 }
169 },
170 .linux => {
171 try group.call(findNativeIncludeDirLinux, .{ self, allocator });
172 try group.call(findNativeLibDirLinux, .{ self, allocator });
173 try group.call(findNativeStaticLibDir, .{ self, allocator });
174 try group.call(findNativeDynamicLinker, .{ self, allocator });
175 },
176 .macosx, .freebsd, .netbsd => {
177 self.include_dir = try std.mem.dupe(allocator, u8, "/usr/include");
178 },
179 else => @compileError("unimplemented: find libc for this OS"),
205 }
206 } else {
207 var batch = Batch(FindError!void, 2, .auto_async).init();
208 batch.add(&async self.findNativeIncludeDirPosix(allocator));
209 if (is_freebsd or is_netbsd) {
210 self.crt_dir = try std.mem.dupeZ(allocator, u8, "/usr/lib");
211 } else if (is_linux or is_dragonfly) {
212 batch.add(&async self.findNativeCrtDirPosix(allocator));
213 }
214 try batch.wait();
180215 }
181 return group.wait();
216 return self;
182217 }
183218
184 async fn findNativeIncludeDirLinux(self: *LibCInstallation, allocator: *Allocator) FindError!void {
185 const cc_exe = std.os.getenv("CC") orelse "cc";
219 /// Must be the same allocator passed to `parse` or `findNative`.
220 pub fn deinit(self: *LibCInstallation, allocator: *Allocator) void {
221 const fields = std.meta.fields(LibCInstallation);
222 inline for (fields) |field| {
223 if (@field(self, field.name)) |payload| {
224 allocator.free(payload);
225 }
226 }
227 self.* = undefined;
228 }
229
230 fn findNativeIncludeDirPosix(self: *LibCInstallation, allocator: *Allocator) FindError!void {
231 const dev_null = if (is_windows) "nul" else "/dev/null";
232 const cc_exe = std.os.getenvZ("CC") orelse default_cc_exe;
186233 const argv = [_][]const u8{
187234 cc_exe,
188235 "-E",
189236 "-Wp,-v",
190237 "-xc",
191 "/dev/null",
238 dev_null,
192239 };
193 // TODO make this use event loop
194 const errorable_result = std.ChildProcess.exec(allocator, &argv, null, null, 1024 * 1024);
195 const exec_result = if (std.debug.runtime_safety) blk: {
196 break :blk errorable_result catch unreachable;
197 } else blk: {
198 break :blk errorable_result catch |err| switch (err) {
199 error.OutOfMemory => return error.OutOfMemory,
200 else => return error.UnableToSpawnCCompiler,
201 };
240 const max_bytes = 1024 * 1024;
241 const exec_res = std.ChildProcess.exec(allocator, &argv, null, null, max_bytes) catch |err| switch (err) {
242 error.OutOfMemory => return error.OutOfMemory,
243 else => return error.UnableToSpawnCCompiler,
202244 };
203245 defer {
204 allocator.free(exec_result.stdout);
205 allocator.free(exec_result.stderr);
246 allocator.free(exec_res.stdout);
247 allocator.free(exec_res.stderr);
206248 }
207
208 switch (exec_result.term) {
209 .Exited => |code| {
210 if (code != 0) return error.CCompilerExitCode;
211 },
212 else => {
213 return error.CCompilerCrashed;
214 },
249 switch (exec_res.term) {
250 .Exited => |code| if (code != 0) return error.CCompilerExitCode,
251 else => return error.CCompilerCrashed,
215252 }
216253
217 var it = std.mem.tokenize(exec_result.stderr, "\n\r");
254 var it = std.mem.tokenize(exec_res.stderr, "\n\r");
218255 var search_paths = std.ArrayList([]const u8).init(allocator);
219256 defer search_paths.deinit();
220257 while (it.next()) |line| {
......@@ -226,16 +263,44 @@ pub const LibCInstallation = struct {
226263 return error.CCompilerCannotFindHeaders;
227264 }
228265
229 // search in reverse order
266 const include_dir_example_file = "stdlib.h";
267 const sys_include_dir_example_file = if (is_windows) "sys\\types.h" else "sys/errno.h";
268
230269 var path_i: usize = 0;
231270 while (path_i < search_paths.len) : (path_i += 1) {
271 // search in reverse order
232272 const search_path_untrimmed = search_paths.at(search_paths.len - path_i - 1);
233273 const search_path = std.mem.trimLeft(u8, search_path_untrimmed, " ");
234 const stdlib_path = try fs.path.join(allocator, &[_][]const u8{ search_path, "stdlib.h" });
235 defer allocator.free(stdlib_path);
274 var search_dir = fs.cwd().openDirList(search_path) catch |err| switch (err) {
275 error.FileNotFound,
276 error.NotDir,
277 error.NoDevice,
278 => continue,
236279
237 if (try fileExists(stdlib_path)) {
238 self.include_dir = try std.mem.dupe(allocator, u8, search_path);
280 else => return error.FileSystem,
281 };
282 defer search_dir.close();
283
284 if (self.include_dir == null) {
285 if (search_dir.accessZ(include_dir_example_file, .{})) |_| {
286 self.include_dir = try std.mem.dupeZ(allocator, u8, search_path);
287 } else |err| switch (err) {
288 error.FileNotFound => {},
289 else => return error.FileSystem,
290 }
291 }
292
293 if (self.sys_include_dir == null) {
294 if (search_dir.accessZ(sys_include_dir_example_file, .{})) |_| {
295 self.sys_include_dir = try std.mem.dupeZ(allocator, u8, search_path);
296 } else |err| switch (err) {
297 error.FileNotFound => {},
298 else => return error.FileSystem,
299 }
300 }
301
302 if (self.include_dir != null and self.sys_include_dir != null) {
303 // Success.
239304 return;
240305 }
241306 }
......@@ -243,7 +308,7 @@ pub const LibCInstallation = struct {
243308 return error.LibCStdLibHeaderNotFound;
244309 }
245310
246 async fn findNativeIncludeDirWindows(self: *LibCInstallation, allocator: *Allocator, sdk: *c.ZigWindowsSDK) !void {
311 fn findNativeIncludeDirWindows(self: *LibCInstallation, allocator: *Allocator, sdk: *ZigWindowsSDK) !void {
247312 var search_buf: [2]Search = undefined;
248313 const searches = fillSearch(&search_buf, sdk);
249314
......@@ -255,179 +320,152 @@ pub const LibCInstallation = struct {
255320 const stream = &std.io.BufferOutStream.init(&result_buf).stream;
256321 try stream.print("{}\\Include\\{}\\ucrt", .{ search.path, search.version });
257322
258 const stdlib_path = try fs.path.join(
259 allocator,
260 [_][]const u8{ result_buf.toSliceConst(), "stdlib.h" },
261 );
262 defer allocator.free(stdlib_path);
323 var dir = fs.cwd().openDirList(result_buf.toSliceConst()) catch |err| switch (err) {
324 error.FileNotFound,
325 error.NotDir,
326 error.NoDevice,
327 => continue,
263328
264 if (try fileExists(stdlib_path)) {
265 self.include_dir = result_buf.toOwnedSlice();
266 return;
267 }
329 else => return error.FileSystem,
330 };
331 defer dir.close();
332
333 dir.accessZ("stdlib.h", .{}) catch |err| switch (err) {
334 error.FileNotFound => continue,
335 else => return error.FileSystem,
336 };
337
338 self.include_dir = result_buf.toOwnedSlice();
339 return;
268340 }
269341
270342 return error.LibCStdLibHeaderNotFound;
271343 }
272344
273 async fn findNativeLibDirWindows(self: *LibCInstallation, allocator: *Allocator, sdk: *c.ZigWindowsSDK) FindError!void {
345 fn findNativeCrtDirWindows(self: *LibCInstallation, allocator: *Allocator, sdk: *ZigWindowsSDK) FindError!void {
274346 var search_buf: [2]Search = undefined;
275347 const searches = fillSearch(&search_buf, sdk);
276348
277349 var result_buf = try std.Buffer.initSize(allocator, 0);
278350 defer result_buf.deinit();
279351
352 const arch_sub_dir = switch (builtin.arch) {
353 .i386 => "x86",
354 .x86_64 => "x64",
355 .arm, .armeb => "arm",
356 else => return error.UnsupportedArchitecture,
357 };
358
280359 for (searches) |search| {
281360 result_buf.shrink(0);
282361 const stream = &std.io.BufferOutStream.init(&result_buf).stream;
283 try stream.print("{}\\Lib\\{}\\ucrt\\", .{ search.path, search.version });
284 switch (builtin.arch) {
285 .i386 => try stream.write("x86"),
286 .x86_64 => try stream.write("x64"),
287 .aarch64 => try stream.write("arm"),
288 else => return error.UnsupportedArchitecture,
289 }
290 const ucrt_lib_path = try fs.path.join(
291 allocator,
292 [_][]const u8{ result_buf.toSliceConst(), "ucrt.lib" },
293 );
294 defer allocator.free(ucrt_lib_path);
295 if (try fileExists(ucrt_lib_path)) {
296 self.lib_dir = result_buf.toOwnedSlice();
297 return;
298 }
299 }
300 return error.LibCRuntimeNotFound;
301 }
362 try stream.print("{}\\Lib\\{}\\ucrt\\{}", .{ search.path, search.version, arch_sub_dir });
302363
303 async fn findNativeLibDirLinux(self: *LibCInstallation, allocator: *Allocator) FindError!void {
304 self.lib_dir = try ccPrintFileName(allocator, "crt1.o", true);
305 }
364 var dir = fs.cwd().openDirList(result_buf.toSliceConst()) catch |err| switch (err) {
365 error.FileNotFound,
366 error.NotDir,
367 error.NoDevice,
368 => continue,
306369
307 async fn findNativeStaticLibDir(self: *LibCInstallation, allocator: *Allocator) FindError!void {
308 self.static_lib_dir = try ccPrintFileName(allocator, "crtbegin.o", true);
309 }
370 else => return error.FileSystem,
371 };
372 defer dir.close();
310373
311 async fn findNativeDynamicLinker(self: *LibCInstallation, allocator: *Allocator) FindError!void {
312 var dyn_tests = [_]DynTest{
313 DynTest{
314 .name = "ld-linux-x86-64.so.2",
315 .result = null,
316 },
317 DynTest{
318 .name = "ld-musl-x86_64.so.1",
319 .result = null,
320 },
321 };
322 var group = event.Group(FindError!void).init(allocator);
323 errdefer group.wait() catch {};
324 for (dyn_tests) |*dyn_test| {
325 try group.call(testNativeDynamicLinker, .{ self, allocator, dyn_test });
326 }
327 try group.wait();
328 for (dyn_tests) |*dyn_test| {
329 if (dyn_test.result) |result| {
330 self.dynamic_linker_path = result;
331 return;
332 }
374 dir.accessZ("ucrt.lib", .{}) catch |err| switch (err) {
375 error.FileNotFound => continue,
376 else => return error.FileSystem,
377 };
378
379 self.crt_dir = result_buf.toOwnedSlice();
380 return;
333381 }
382 return error.LibCRuntimeNotFound;
334383 }
335384
336 const DynTest = struct {
337 name: []const u8,
338 result: ?[]const u8,
339 };
385 fn findNativeCrtDirPosix(self: *LibCInstallation, allocator: *Allocator) FindError!void {
386 self.crt_dir = try ccPrintFileName(allocator, "crt1.o", .only_dir);
387 }
340388
341 async fn testNativeDynamicLinker(self: *LibCInstallation, allocator: *Allocator, dyn_test: *DynTest) FindError!void {
342 if (ccPrintFileName(allocator, dyn_test.name, false)) |result| {
343 dyn_test.result = result;
344 return;
345 } else |err| switch (err) {
346 error.LibCRuntimeNotFound => return,
347 else => return err,
348 }
389 fn findNativeStaticCrtDirPosix(self: *LibCInstallation, allocator: *Allocator) FindError!void {
390 self.static_crt_dir = try ccPrintFileName(allocator, "crtbegin.o", .only_dir);
349391 }
350392
351 async fn findNativeKernel32LibDir(self: *LibCInstallation, allocator: *Allocator, sdk: *c.ZigWindowsSDK) FindError!void {
393 fn findNativeKernel32LibDir(self: *LibCInstallation, allocator: *Allocator, sdk: *ZigWindowsSDK) FindError!void {
352394 var search_buf: [2]Search = undefined;
353395 const searches = fillSearch(&search_buf, sdk);
354396
355397 var result_buf = try std.Buffer.initSize(allocator, 0);
356398 defer result_buf.deinit();
357399
400 const arch_sub_dir = switch (builtin.arch) {
401 .i386 => "x86",
402 .x86_64 => "x64",
403 .arm, .armeb => "arm",
404 else => return error.UnsupportedArchitecture,
405 };
406
358407 for (searches) |search| {
359408 result_buf.shrink(0);
360409 const stream = &std.io.BufferOutStream.init(&result_buf).stream;
361 try stream.print("{}\\Lib\\{}\\um\\", .{ search.path, search.version });
362 switch (builtin.arch) {
363 .i386 => try stream.write("x86\\"),
364 .x86_64 => try stream.write("x64\\"),
365 .aarch64 => try stream.write("arm\\"),
366 else => return error.UnsupportedArchitecture,
367 }
368 const kernel32_path = try fs.path.join(
369 allocator,
370 [_][]const u8{ result_buf.toSliceConst(), "kernel32.lib" },
371 );
372 defer allocator.free(kernel32_path);
373 if (try fileExists(kernel32_path)) {
374 self.kernel32_lib_dir = result_buf.toOwnedSlice();
375 return;
376 }
410 try stream.print("{}\\Lib\\{}\\um\\{}", .{ search.path, search.version, arch_sub_dir });
411
412 var dir = fs.cwd().openDirList(result_buf.toSliceConst()) catch |err| switch (err) {
413 error.FileNotFound,
414 error.NotDir,
415 error.NoDevice,
416 => continue,
417
418 else => return error.FileSystem,
419 };
420 defer dir.close();
421
422 dir.accessZ("kernel32.lib", .{}) catch |err| switch (err) {
423 error.FileNotFound => continue,
424 else => return error.FileSystem,
425 };
426
427 self.kernel32_lib_dir = result_buf.toOwnedSlice();
428 return;
377429 }
378430 return error.LibCKernel32LibNotFound;
379431 }
380
381 fn initEmpty(self: *LibCInstallation) void {
382 self.* = LibCInstallation{
383 .include_dir = @as([*]const u8, undefined)[0..0],
384 .lib_dir = null,
385 .static_lib_dir = null,
386 .msvc_lib_dir = null,
387 .kernel32_lib_dir = null,
388 .dynamic_linker_path = null,
389 };
390 }
391432};
392433
434const default_cc_exe = if (is_windows) "cc.exe" else "cc";
435
393436/// caller owns returned memory
394fn ccPrintFileName(allocator: *Allocator, o_file: []const u8, want_dirname: bool) ![]u8 {
395 const cc_exe = std.os.getenv("CC") orelse "cc";
437pub fn ccPrintFileName(
438 allocator: *Allocator,
439 o_file: []const u8,
440 want_dirname: enum { full_path, only_dir },
441) ![:0]u8 {
442 const cc_exe = std.os.getenvZ("CC") orelse default_cc_exe;
396443 const arg1 = try std.fmt.allocPrint(allocator, "-print-file-name={}", .{o_file});
397444 defer allocator.free(arg1);
398445 const argv = [_][]const u8{ cc_exe, arg1 };
399446
400 // TODO This simulates evented I/O for the child process exec
401 event.Loop.startCpuBoundOperation();
402 const errorable_result = std.ChildProcess.exec(allocator, &argv, null, null, 1024 * 1024);
403 const exec_result = if (std.debug.runtime_safety) blk: {
404 break :blk errorable_result catch unreachable;
405 } else blk: {
406 break :blk errorable_result catch |err| switch (err) {
407 error.OutOfMemory => return error.OutOfMemory,
408 else => return error.UnableToSpawnCCompiler,
409 };
447 const max_bytes = 1024 * 1024;
448 const exec_res = std.ChildProcess.exec(allocator, &argv, null, null, max_bytes) catch |err| switch (err) {
449 error.OutOfMemory => return error.OutOfMemory,
450 else => return error.UnableToSpawnCCompiler,
410451 };
411452 defer {
412 allocator.free(exec_result.stdout);
413 allocator.free(exec_result.stderr);
453 allocator.free(exec_res.stdout);
454 allocator.free(exec_res.stderr);
414455 }
415 switch (exec_result.term) {
416 .Exited => |code| {
417 if (code != 0) return error.CCompilerExitCode;
418 },
419 else => {
420 return error.CCompilerCrashed;
421 },
456 switch (exec_res.term) {
457 .Exited => |code| if (code != 0) return error.CCompilerExitCode,
458 else => return error.CCompilerCrashed,
422459 }
423 var it = std.mem.tokenize(exec_result.stdout, "\n\r");
424 const line = it.next() orelse return error.LibCRuntimeNotFound;
425 const dirname = fs.path.dirname(line) orelse return error.LibCRuntimeNotFound;
426460
427 if (want_dirname) {
428 return std.mem.dupe(allocator, u8, dirname);
429 } else {
430 return std.mem.dupe(allocator, u8, line);
461 var it = std.mem.tokenize(exec_res.stdout, "\n\r");
462 const line = it.next() orelse return error.LibCRuntimeNotFound;
463 switch (want_dirname) {
464 .full_path => return std.mem.dupeZ(allocator, u8, line),
465 .only_dir => {
466 const dirname = fs.path.dirname(line) orelse return error.LibCRuntimeNotFound;
467 return std.mem.dupeZ(allocator, u8, dirname);
468 },
431469 }
432470}
433471
......@@ -436,34 +474,25 @@ const Search = struct {
436474 version: []const u8,
437475};
438476
439fn fillSearch(search_buf: *[2]Search, sdk: *c.ZigWindowsSDK) []Search {
477fn fillSearch(search_buf: *[2]Search, sdk: *ZigWindowsSDK) []Search {
440478 var search_end: usize = 0;
441 if (sdk.path10_ptr != 0) {
442 if (sdk.version10_ptr != 0) {
479 if (sdk.path10_ptr) |path10_ptr| {
480 if (sdk.version10_ptr) |version10_ptr| {
443481 search_buf[search_end] = Search{
444 .path = sdk.path10_ptr[0..sdk.path10_len],
445 .version = sdk.version10_ptr[0..sdk.version10_len],
482 .path = path10_ptr[0..sdk.path10_len],
483 .version = version10_ptr[0..sdk.version10_len],
446484 };
447485 search_end += 1;
448486 }
449487 }
450 if (sdk.path81_ptr != 0) {
451 if (sdk.version81_ptr != 0) {
488 if (sdk.path81_ptr) |path81_ptr| {
489 if (sdk.version81_ptr) |version81_ptr| {
452490 search_buf[search_end] = Search{
453 .path = sdk.path81_ptr[0..sdk.path81_len],
454 .version = sdk.version81_ptr[0..sdk.version81_len],
491 .path = path81_ptr[0..sdk.path81_len],
492 .version = version81_ptr[0..sdk.version81_len],
455493 };
456494 search_end += 1;
457495 }
458496 }
459497 return search_buf[0..search_end];
460498}
461
462fn fileExists(path: []const u8) !bool {
463 if (fs.File.access(path)) |_| {
464 return true;
465 } else |err| switch (err) {
466 error.FileNotFound => return false,
467 else => return error.FileSystem,
468 }
469}
src-self-hosted/stage1.zig+214-15
......@@ -14,6 +14,7 @@ const self_hosted_main = @import("main.zig");
1414const errmsg = @import("errmsg.zig");
1515const DepTokenizer = @import("dep_tokenizer.zig").Tokenizer;
1616const assert = std.debug.assert;
17const LibCInstallation = @import("libc_installation.zig").LibCInstallation;
1718
1819var stderr_file: fs.File = undefined;
1920var stderr: *io.OutStream(fs.File.WriteError) = undefined;
......@@ -93,6 +94,20 @@ const Error = extern enum {
9394 InvalidLlvmCpuFeaturesFormat,
9495 UnknownApplicationBinaryInterface,
9596 ASTUnitFailure,
97 BadPathName,
98 SymLinkLoop,
99 ProcessFdQuotaExceeded,
100 SystemFdQuotaExceeded,
101 NoDevice,
102 DeviceBusy,
103 UnableToSpawnCCompiler,
104 CCompilerExitCode,
105 CCompilerCrashed,
106 CCompilerCannotFindHeaders,
107 LibCRuntimeNotFound,
108 LibCStdLibHeaderNotFound,
109 LibCKernel32LibNotFound,
110 UnsupportedArchitecture,
96111};
97112
98113const FILE = std.c.FILE;
......@@ -113,12 +128,12 @@ export fn stage2_translate_c(
113128 error.SemanticAnalyzeFail => {
114129 out_errors_ptr.* = errors.ptr;
115130 out_errors_len.* = errors.len;
116 return Error.CCompileErrors;
131 return .CCompileErrors;
117132 },
118 error.ASTUnitFailure => return Error.ASTUnitFailure,
119 error.OutOfMemory => return Error.OutOfMemory,
133 error.ASTUnitFailure => return .ASTUnitFailure,
134 error.OutOfMemory => return .OutOfMemory,
120135 };
121 return Error.None;
136 return .None;
122137}
123138
124139export fn stage2_free_clang_errors(errors_ptr: [*]translate_c.ClangErrMsg, errors_len: usize) void {
......@@ -129,18 +144,18 @@ export fn stage2_render_ast(tree: *ast.Tree, output_file: *FILE) Error {
129144 const c_out_stream = &std.io.COutStream.init(output_file).stream;
130145 _ = std.zig.render(std.heap.c_allocator, c_out_stream, tree) catch |e| switch (e) {
131146 error.WouldBlock => unreachable, // stage1 opens stuff in exclusively blocking mode
132 error.SystemResources => return Error.SystemResources,
133 error.OperationAborted => return Error.OperationAborted,
134 error.BrokenPipe => return Error.BrokenPipe,
135 error.DiskQuota => return Error.DiskQuota,
136 error.FileTooBig => return Error.FileTooBig,
137 error.NoSpaceLeft => return Error.NoSpaceLeft,
138 error.AccessDenied => return Error.AccessDenied,
139 error.OutOfMemory => return Error.OutOfMemory,
140 error.Unexpected => return Error.Unexpected,
141 error.InputOutput => return Error.FileSystem,
147 error.SystemResources => return .SystemResources,
148 error.OperationAborted => return .OperationAborted,
149 error.BrokenPipe => return .BrokenPipe,
150 error.DiskQuota => return .DiskQuota,
151 error.FileTooBig => return .FileTooBig,
152 error.NoSpaceLeft => return .NoSpaceLeft,
153 error.AccessDenied => return .AccessDenied,
154 error.OutOfMemory => return .OutOfMemory,
155 error.Unexpected => return .Unexpected,
156 error.InputOutput => return .FileSystem,
142157 };
143 return Error.None;
158 return .None;
144159}
145160
146161// TODO: just use the actual self-hosted zig fmt. Until https://github.com/ziglang/zig/issues/2377,
......@@ -832,3 +847,187 @@ export fn stage2_cpu_features_get_llvm_cpu(cpu_features: *const Stage2CpuFeature
832847export fn stage2_cpu_features_get_llvm_features(cpu_features: *const Stage2CpuFeatures) ?[*:0]const u8 {
833848 return cpu_features.llvm_features_str;
834849}
850
851// ABI warning
852const Stage2LibCInstallation = extern struct {
853 include_dir: [*:0]const u8,
854 include_dir_len: usize,
855 sys_include_dir: [*:0]const u8,
856 sys_include_dir_len: usize,
857 crt_dir: [*:0]const u8,
858 crt_dir_len: usize,
859 static_crt_dir: [*:0]const u8,
860 static_crt_dir_len: usize,
861 msvc_lib_dir: [*:0]const u8,
862 msvc_lib_dir_len: usize,
863 kernel32_lib_dir: [*:0]const u8,
864 kernel32_lib_dir_len: usize,
865
866 fn initFromStage2(self: *Stage2LibCInstallation, libc: LibCInstallation) void {
867 if (libc.include_dir) |s| {
868 self.include_dir = s.ptr;
869 self.include_dir_len = s.len;
870 } else {
871 self.include_dir = "";
872 self.include_dir_len = 0;
873 }
874 if (libc.sys_include_dir) |s| {
875 self.sys_include_dir = s.ptr;
876 self.sys_include_dir_len = s.len;
877 } else {
878 self.sys_include_dir = "";
879 self.sys_include_dir_len = 0;
880 }
881 if (libc.crt_dir) |s| {
882 self.crt_dir = s.ptr;
883 self.crt_dir_len = s.len;
884 } else {
885 self.crt_dir = "";
886 self.crt_dir_len = 0;
887 }
888 if (libc.static_crt_dir) |s| {
889 self.static_crt_dir = s.ptr;
890 self.static_crt_dir_len = s.len;
891 } else {
892 self.static_crt_dir = "";
893 self.static_crt_dir_len = 0;
894 }
895 if (libc.msvc_lib_dir) |s| {
896 self.msvc_lib_dir = s.ptr;
897 self.msvc_lib_dir_len = s.len;
898 } else {
899 self.msvc_lib_dir = "";
900 self.msvc_lib_dir_len = 0;
901 }
902 if (libc.kernel32_lib_dir) |s| {
903 self.kernel32_lib_dir = s.ptr;
904 self.kernel32_lib_dir_len = s.len;
905 } else {
906 self.kernel32_lib_dir = "";
907 self.kernel32_lib_dir_len = 0;
908 }
909 }
910
911 fn toStage2(self: Stage2LibCInstallation) LibCInstallation {
912 var libc: LibCInstallation = .{};
913 if (self.include_dir_len != 0) {
914 libc.include_dir = self.include_dir[0..self.include_dir_len :0];
915 }
916 if (self.sys_include_dir_len != 0) {
917 libc.sys_include_dir = self.sys_include_dir[0..self.sys_include_dir_len :0];
918 }
919 if (self.crt_dir_len != 0) {
920 libc.crt_dir = self.crt_dir[0..self.crt_dir_len :0];
921 }
922 if (self.static_crt_dir_len != 0) {
923 libc.static_crt_dir = self.static_crt_dir[0..self.static_crt_dir_len :0];
924 }
925 if (self.msvc_lib_dir_len != 0) {
926 libc.msvc_lib_dir = self.msvc_lib_dir[0..self.msvc_lib_dir_len :0];
927 }
928 if (self.kernel32_lib_dir_len != 0) {
929 libc.kernel32_lib_dir = self.kernel32_lib_dir[0..self.kernel32_lib_dir_len :0];
930 }
931 return libc;
932 }
933};
934
935// ABI warning
936export fn stage2_libc_parse(stage1_libc: *Stage2LibCInstallation, libc_file_z: [*:0]const u8) Error {
937 stderr_file = std.io.getStdErr();
938 stderr = &stderr_file.outStream().stream;
939 const libc_file = mem.toSliceConst(u8, libc_file_z);
940 var libc = LibCInstallation.parse(std.heap.c_allocator, libc_file, stderr) catch |err| switch (err) {
941 error.ParseError => return .SemanticAnalyzeFail,
942 error.DiskQuota => return .DiskQuota,
943 error.FileTooBig => return .FileTooBig,
944 error.InputOutput => return .FileSystem,
945 error.NoSpaceLeft => return .NoSpaceLeft,
946 error.AccessDenied => return .AccessDenied,
947 error.BrokenPipe => return .BrokenPipe,
948 error.SystemResources => return .SystemResources,
949 error.OperationAborted => return .OperationAborted,
950 error.WouldBlock => unreachable,
951 error.Unexpected => return .Unexpected,
952 error.EndOfStream => return .EndOfFile,
953 error.IsDir => return .IsDir,
954 error.ConnectionResetByPeer => unreachable,
955 error.OutOfMemory => return .OutOfMemory,
956 error.Unseekable => unreachable,
957 error.SharingViolation => return .SharingViolation,
958 error.PathAlreadyExists => unreachable,
959 error.FileNotFound => return .FileNotFound,
960 error.PipeBusy => return .PipeBusy,
961 error.NameTooLong => return .PathTooLong,
962 error.InvalidUtf8 => return .BadPathName,
963 error.BadPathName => return .BadPathName,
964 error.SymLinkLoop => return .SymLinkLoop,
965 error.ProcessFdQuotaExceeded => return .ProcessFdQuotaExceeded,
966 error.SystemFdQuotaExceeded => return .SystemFdQuotaExceeded,
967 error.NoDevice => return .NoDevice,
968 error.NotDir => return .NotDir,
969 error.DeviceBusy => return .DeviceBusy,
970 };
971 stage1_libc.initFromStage2(libc);
972 return .None;
973}
974
975// ABI warning
976export fn stage2_libc_find_native(stage1_libc: *Stage2LibCInstallation) Error {
977 var libc = LibCInstallation.findNative(std.heap.c_allocator) catch |err| switch (err) {
978 error.OutOfMemory => return .OutOfMemory,
979 error.FileSystem => return .FileSystem,
980 error.UnableToSpawnCCompiler => return .UnableToSpawnCCompiler,
981 error.CCompilerExitCode => return .CCompilerExitCode,
982 error.CCompilerCrashed => return .CCompilerCrashed,
983 error.CCompilerCannotFindHeaders => return .CCompilerCannotFindHeaders,
984 error.LibCRuntimeNotFound => return .LibCRuntimeNotFound,
985 error.LibCStdLibHeaderNotFound => return .LibCStdLibHeaderNotFound,
986 error.LibCKernel32LibNotFound => return .LibCKernel32LibNotFound,
987 error.UnsupportedArchitecture => return .UnsupportedArchitecture,
988 };
989 stage1_libc.initFromStage2(libc);
990 return .None;
991}
992
993// ABI warning
994export fn stage2_libc_render(stage1_libc: *Stage2LibCInstallation, output_file: *FILE) Error {
995 var libc = stage1_libc.toStage2();
996 const c_out_stream = &std.io.COutStream.init(output_file).stream;
997 libc.render(c_out_stream) catch |err| switch (err) {
998 error.WouldBlock => unreachable, // stage1 opens stuff in exclusively blocking mode
999 error.SystemResources => return .SystemResources,
1000 error.OperationAborted => return .OperationAborted,
1001 error.BrokenPipe => return .BrokenPipe,
1002 error.DiskQuota => return .DiskQuota,
1003 error.FileTooBig => return .FileTooBig,
1004 error.NoSpaceLeft => return .NoSpaceLeft,
1005 error.AccessDenied => return .AccessDenied,
1006 error.Unexpected => return .Unexpected,
1007 error.InputOutput => return .FileSystem,
1008 };
1009 return .None;
1010}
1011
1012// ABI warning
1013export fn stage2_libc_cc_print_file_name(
1014 out_ptr: *[*:0]u8,
1015 out_len: *usize,
1016 o_file: [*:0]const u8,
1017 want_dirname: bool,
1018) Error {
1019 const result = @import("libc_installation.zig").ccPrintFileName(
1020 std.heap.c_allocator,
1021 mem.toSliceConst(u8, o_file),
1022 if (want_dirname) .only_dir else .full_path,
1023 ) catch |err| switch (err) {
1024 error.OutOfMemory => return .OutOfMemory,
1025 error.LibCRuntimeNotFound => return .FileNotFound,
1026 error.CCompilerExitCode => return .CCompilerExitCode,
1027 error.CCompilerCrashed => return .CCompilerCrashed,
1028 error.UnableToSpawnCCompiler => return .UnableToSpawnCCompiler,
1029 };
1030 out_ptr.* = result.ptr;
1031 out_len.* = result.len;
1032 return .None;
1033}
src-self-hosted/windows_sdk.zig created+22
......@@ -0,0 +1,22 @@
1// C API bindings for src/windows_sdk.h
2
3pub const ZigWindowsSDK = extern struct {
4 path10_ptr: ?[*]const u8,
5 path10_len: usize,
6 version10_ptr: ?[*]const u8,
7 version10_len: usize,
8 path81_ptr: ?[*]const u8,
9 path81_len: usize,
10 version81_ptr: ?[*]const u8,
11 version81_len: usize,
12 msvc_lib_dir_ptr: ?[*]const u8,
13 msvc_lib_dir_len: usize,
14};
15pub const ZigFindWindowsSdkError = extern enum {
16 None,
17 OutOfMemory,
18 NotFound,
19 PathTooLong,
20};
21pub extern fn zig_find_windows_sdk(out_sdk: **ZigWindowsSDK) ZigFindWindowsSdkError;
22pub extern fn zig_free_windows_sdk(sdk: *ZigWindowsSDK) void;
src/all_types.hpp+2-3
......@@ -18,7 +18,6 @@
1818#include "bigfloat.hpp"
1919#include "target.hpp"
2020#include "tokenizer.hpp"
21#include "libc_installation.hpp"
2221
2322struct AstNode;
2423struct ZigFn;
......@@ -2139,7 +2138,7 @@ struct CodeGen {
21392138 // As an input parameter, mutually exclusive with enable_cache. But it gets
21402139 // populated in codegen_build_and_link.
21412140 Buf *output_dir;
2142 Buf **libc_include_dir_list;
2141 const char **libc_include_dir_list;
21432142 size_t libc_include_dir_len;
21442143
21452144 Buf *zig_c_headers_dir; // Cannot be overridden; derived from zig_lib_dir.
......@@ -2220,7 +2219,7 @@ struct CodeGen {
22202219 ZigList<const char *> lib_dirs;
22212220 ZigList<const char *> framework_dirs;
22222221
2223 ZigLibCInstallation *libc;
2222 Stage2LibCInstallation *libc;
22242223
22252224 size_t version_major;
22262225 size_t version_minor;
src/codegen.cpp+34-30
......@@ -8982,17 +8982,20 @@ static void detect_dynamic_linker(CodeGen *g) {
89828982#if defined(ZIG_OS_LINUX)
89838983 {
89848984 Error err;
8985 Buf *result = buf_alloc();
89868985 for (size_t i = 0; possible_ld_names[i] != NULL; i += 1) {
89878986 const char *lib_name = possible_ld_names[i];
8988 if ((err = zig_libc_cc_print_file_name(lib_name, result, false, true))) {
8987 char *result_ptr;
8988 size_t result_len;
8989 if ((err = stage2_libc_cc_print_file_name(&result_ptr, &result_len, lib_name, false))) {
89898990 if (err != ErrorCCompilerCannotFindFile && err != ErrorNoCCompilerInstalled) {
89908991 fprintf(stderr, "Unable to detect native dynamic linker: %s\n", err_str(err));
89918992 exit(1);
89928993 }
89938994 continue;
89948995 }
8995 g->dynamic_linker_path = result;
8996 g->dynamic_linker_path = buf_create_from_mem(result_ptr, result_len);
8997 // Skips heap::c_allocator because the memory is allocated by stage2 library.
8998 free(result_ptr);
89968999 return;
89979000 }
89989001 }
......@@ -9028,16 +9031,16 @@ static void detect_libc(CodeGen *g) {
90289031 buf_ptr(g->zig_lib_dir), target_os_name(g->zig_target->os));
90299032
90309033 g->libc_include_dir_len = 4;
9031 g->libc_include_dir_list = heap::c_allocator.allocate<Buf*>(g->libc_include_dir_len);
9032 g->libc_include_dir_list[0] = arch_include_dir;
9033 g->libc_include_dir_list[1] = generic_include_dir;
9034 g->libc_include_dir_list[2] = arch_os_include_dir;
9035 g->libc_include_dir_list[3] = generic_os_include_dir;
9034 g->libc_include_dir_list = heap::c_allocator.allocate<const char*>(g->libc_include_dir_len);
9035 g->libc_include_dir_list[0] = buf_ptr(arch_include_dir);
9036 g->libc_include_dir_list[1] = buf_ptr(generic_include_dir);
9037 g->libc_include_dir_list[2] = buf_ptr(arch_os_include_dir);
9038 g->libc_include_dir_list[3] = buf_ptr(generic_os_include_dir);
90369039 return;
90379040 }
90389041
90399042 if (g->zig_target->is_native) {
9040 g->libc = heap::c_allocator.create<ZigLibCInstallation>();
9043 g->libc = heap::c_allocator.create<Stage2LibCInstallation>();
90419044
90429045 // search for native_libc.txt in following dirs:
90439046 // - LOCAL_CACHE_DIR
......@@ -9082,8 +9085,8 @@ static void detect_libc(CodeGen *g) {
90829085 if (libc_txt == nullptr)
90839086 libc_txt = &global_libc_txt;
90849087
9085 if ((err = zig_libc_parse(g->libc, libc_txt, g->zig_target, false))) {
9086 if ((err = zig_libc_find_native(g->libc, true))) {
9088 if ((err = stage2_libc_parse(g->libc, buf_ptr(libc_txt)))) {
9089 if ((err = stage2_libc_find_native(g->libc))) {
90879090 fprintf(stderr,
90889091 "Unable to link against libc: Unable to find libc installation: %s\n"
90899092 "See `zig libc --help` for more details.\n", err_str(err));
......@@ -9103,7 +9106,7 @@ static void detect_libc(CodeGen *g) {
91039106 fprintf(stderr, "Unable to open %s: %s\n", buf_ptr(native_libc_tmp), strerror(errno));
91049107 exit(1);
91059108 }
9106 zig_libc_render(g->libc, file);
9109 stage2_libc_render(g->libc, file);
91079110 if (fclose(file) != 0) {
91089111 fprintf(stderr, "Unable to save %s: %s\n", buf_ptr(native_libc_tmp), strerror(errno));
91099112 exit(1);
......@@ -9113,27 +9116,28 @@ static void detect_libc(CodeGen *g) {
91139116 exit(1);
91149117 }
91159118 }
9116 bool want_sys_dir = !buf_eql_buf(&g->libc->include_dir, &g->libc->sys_include_dir);
9119 bool want_sys_dir = !mem_eql_mem(g->libc->include_dir, g->libc->include_dir_len,
9120 g->libc->sys_include_dir, g->libc->sys_include_dir_len);
91179121 size_t want_um_and_shared_dirs = (g->zig_target->os == OsWindows) ? 2 : 0;
91189122 size_t dir_count = 1 + want_sys_dir + want_um_and_shared_dirs;
91199123 g->libc_include_dir_len = 0;
9120 g->libc_include_dir_list = heap::c_allocator.allocate<Buf*>(dir_count);
9124 g->libc_include_dir_list = heap::c_allocator.allocate<const char *>(dir_count);
91219125
9122 g->libc_include_dir_list[g->libc_include_dir_len] = &g->libc->include_dir;
9126 g->libc_include_dir_list[g->libc_include_dir_len] = g->libc->include_dir;
91239127 g->libc_include_dir_len += 1;
91249128
91259129 if (want_sys_dir) {
9126 g->libc_include_dir_list[g->libc_include_dir_len] = &g->libc->sys_include_dir;
9130 g->libc_include_dir_list[g->libc_include_dir_len] = g->libc->sys_include_dir;
91279131 g->libc_include_dir_len += 1;
91289132 }
91299133
91309134 if (want_um_and_shared_dirs != 0) {
9131 g->libc_include_dir_list[g->libc_include_dir_len] = buf_sprintf("%s" OS_SEP ".." OS_SEP "um",
9132 buf_ptr(&g->libc->include_dir));
9135 g->libc_include_dir_list[g->libc_include_dir_len] = buf_ptr(buf_sprintf(
9136 "%s" OS_SEP ".." OS_SEP "um", g->libc->include_dir));
91339137 g->libc_include_dir_len += 1;
91349138
9135 g->libc_include_dir_list[g->libc_include_dir_len] = buf_sprintf("%s" OS_SEP ".." OS_SEP "shared",
9136 buf_ptr(&g->libc->include_dir));
9139 g->libc_include_dir_list[g->libc_include_dir_len] = buf_ptr(buf_sprintf(
9140 "%s" OS_SEP ".." OS_SEP "shared", g->libc->include_dir));
91379141 g->libc_include_dir_len += 1;
91389142 }
91399143 assert(g->libc_include_dir_len == dir_count);
......@@ -9208,9 +9212,9 @@ void add_cc_args(CodeGen *g, ZigList<const char *> &args, const char *out_dep_pa
92089212 args.append(buf_ptr(g->zig_c_headers_dir));
92099213
92109214 for (size_t i = 0; i < g->libc_include_dir_len; i += 1) {
9211 Buf *include_dir = g->libc_include_dir_list[i];
9215 const char *include_dir = g->libc_include_dir_list[i];
92129216 args.append("-isystem");
9213 args.append(buf_ptr(include_dir));
9217 args.append(include_dir);
92149218 }
92159219
92169220 if (g->zig_target->is_native) {
......@@ -9666,7 +9670,7 @@ Error create_c_object_cache(CodeGen *g, CacheHash **out_cache_hash, bool verbose
96669670 cache_buf(cache_hash, compiler_id);
96679671 cache_int(cache_hash, g->err_color);
96689672 cache_buf(cache_hash, g->zig_c_headers_dir);
9669 cache_list_of_buf(cache_hash, g->libc_include_dir_list, g->libc_include_dir_len);
9673 cache_list_of_str(cache_hash, g->libc_include_dir_list, g->libc_include_dir_len);
96709674 cache_int(cache_hash, g->zig_target->is_native);
96719675 cache_int(cache_hash, g->zig_target->arch);
96729676 cache_int(cache_hash, g->zig_target->sub_arch);
......@@ -10482,11 +10486,11 @@ static Error check_cache(CodeGen *g, Buf *manifest_dir, Buf *digest) {
1048210486 cache_list_of_str(ch, g->lib_dirs.items, g->lib_dirs.length);
1048310487 cache_list_of_str(ch, g->framework_dirs.items, g->framework_dirs.length);
1048410488 if (g->libc) {
10485 cache_buf(ch, &g->libc->include_dir);
10486 cache_buf(ch, &g->libc->sys_include_dir);
10487 cache_buf(ch, &g->libc->crt_dir);
10488 cache_buf(ch, &g->libc->msvc_lib_dir);
10489 cache_buf(ch, &g->libc->kernel32_lib_dir);
10489 cache_str(ch, g->libc->include_dir);
10490 cache_str(ch, g->libc->sys_include_dir);
10491 cache_str(ch, g->libc->crt_dir);
10492 cache_str(ch, g->libc->msvc_lib_dir);
10493 cache_str(ch, g->libc->kernel32_lib_dir);
1049010494 }
1049110495 cache_buf_opt(ch, g->dynamic_linker_path);
1049210496 cache_buf_opt(ch, g->version_script_path);
......@@ -10765,7 +10769,7 @@ ZigPackage *codegen_create_package(CodeGen *g, const char *root_src_dir, const c
1076510769}
1076610770
1076710771CodeGen *create_child_codegen(CodeGen *parent_gen, Buf *root_src_path, OutType out_type,
10768 ZigLibCInstallation *libc, const char *name, Stage2ProgressNode *parent_progress_node)
10772 Stage2LibCInstallation *libc, const char *name, Stage2ProgressNode *parent_progress_node)
1076910773{
1077010774 Stage2ProgressNode *child_progress_node = stage2_progress_start(
1077110775 parent_progress_node ? parent_progress_node : parent_gen->sub_progress_node,
......@@ -10804,7 +10808,7 @@ CodeGen *create_child_codegen(CodeGen *parent_gen, Buf *root_src_path, OutType o
1080410808
1080510809CodeGen *codegen_create(Buf *main_pkg_path, Buf *root_src_path, const ZigTarget *target,
1080610810 OutType out_type, BuildMode build_mode, Buf *override_lib_dir,
10807 ZigLibCInstallation *libc, Buf *cache_dir, bool is_test_build, Stage2ProgressNode *progress_node)
10811 Stage2LibCInstallation *libc, Buf *cache_dir, bool is_test_build, Stage2ProgressNode *progress_node)
1080810812{
1080910813 CodeGen *g = heap::c_allocator.create<CodeGen>();
1081010814 g->pass1_arena = heap::ArenaAllocator::construct(&heap::c_allocator, &heap::c_allocator, "pass1");
src/codegen.hpp+2-3
......@@ -11,17 +11,16 @@
1111#include "parser.hpp"
1212#include "errmsg.hpp"
1313#include "target.hpp"
14#include "libc_installation.hpp"
1514#include "userland.h"
1615
1716#include <stdio.h>
1817
1918CodeGen *codegen_create(Buf *main_pkg_path, Buf *root_src_path, const ZigTarget *target,
2019 OutType out_type, BuildMode build_mode, Buf *zig_lib_dir,
21 ZigLibCInstallation *libc, Buf *cache_dir, bool is_test_build, Stage2ProgressNode *progress_node);
20 Stage2LibCInstallation *libc, Buf *cache_dir, bool is_test_build, Stage2ProgressNode *progress_node);
2221
2322CodeGen *create_child_codegen(CodeGen *parent_gen, Buf *root_src_path, OutType out_type,
24 ZigLibCInstallation *libc, const char *name, Stage2ProgressNode *progress_node);
23 Stage2LibCInstallation *libc, const char *name, Stage2ProgressNode *progress_node);
2524
2625void codegen_set_clang_argv(CodeGen *codegen, const char **args, size_t len);
2726void codegen_set_llvm_argv(CodeGen *codegen, const char **args, size_t len);
src/error.cpp+14
......@@ -65,6 +65,20 @@ const char *err_str(Error err) {
6565 case ErrorInvalidLlvmCpuFeaturesFormat: return "invalid LLVM CPU features format";
6666 case ErrorUnknownApplicationBinaryInterface: return "unknown application binary interface";
6767 case ErrorASTUnitFailure: return "compiler bug: clang encountered a compile error, but the libclang API does not expose the error. See https://github.com/ziglang/zig/issues/4455 for more details";
68 case ErrorBadPathName: return "bad path name";
69 case ErrorSymLinkLoop: return "sym link loop";
70 case ErrorProcessFdQuotaExceeded: return "process fd quota exceeded";
71 case ErrorSystemFdQuotaExceeded: return "system fd quota exceeded";
72 case ErrorNoDevice: return "no device";
73 case ErrorDeviceBusy: return "device busy";
74 case ErrorUnableToSpawnCCompiler: return "unable to spawn system C compiler";
75 case ErrorCCompilerExitCode: return "system C compiler exited with failure code";
76 case ErrorCCompilerCrashed: return "system C compiler crashed";
77 case ErrorCCompilerCannotFindHeaders: return "system C compiler cannot find libc headers";
78 case ErrorLibCRuntimeNotFound: return "libc runtime not found";
79 case ErrorLibCStdLibHeaderNotFound: return "libc std lib headers not found";
80 case ErrorLibCKernel32LibNotFound: return "kernel32 library not found";
81 case ErrorUnsupportedArchitecture: return "unsupported architecture";
6882 }
6983 return "(invalid error)";
7084}
src/libc_installation.cpp deleted-498
......@@ -1,498 +0,0 @@
1/*
2 * Copyright (c) 2019 Andrew Kelley
3 *
4 * This file is part of zig, which is MIT licensed.
5 * See http://opensource.org/licenses/MIT
6 */
7
8#include "libc_installation.hpp"
9#include "os.hpp"
10#include "windows_sdk.h"
11#include "target.hpp"
12
13static const char *zig_libc_keys[] = {
14 "include_dir",
15 "sys_include_dir",
16 "crt_dir",
17 "static_crt_dir",
18 "msvc_lib_dir",
19 "kernel32_lib_dir",
20};
21
22static const size_t zig_libc_keys_len = array_length(zig_libc_keys);
23
24static bool zig_libc_match_key(Slice<uint8_t> name, Slice<uint8_t> value, bool *found_keys,
25 size_t index, Buf *field_ptr)
26{
27 if (!memEql(name, str(zig_libc_keys[index]))) return false;
28 buf_init_from_mem(field_ptr, (const char*)value.ptr, value.len);
29 found_keys[index] = true;
30 return true;
31}
32
33static void zig_libc_init_empty(ZigLibCInstallation *libc) {
34 *libc = {};
35 buf_init_from_str(&libc->include_dir, "");
36 buf_init_from_str(&libc->sys_include_dir, "");
37 buf_init_from_str(&libc->crt_dir, "");
38 buf_init_from_str(&libc->static_crt_dir, "");
39 buf_init_from_str(&libc->msvc_lib_dir, "");
40 buf_init_from_str(&libc->kernel32_lib_dir, "");
41}
42
43Error zig_libc_parse(ZigLibCInstallation *libc, Buf *libc_file, const ZigTarget *target, bool verbose) {
44 Error err;
45 zig_libc_init_empty(libc);
46
47 bool found_keys[array_length(zig_libc_keys)] = {};
48
49 Buf *contents = buf_alloc();
50 if ((err = os_fetch_file_path(libc_file, contents))) {
51 if (err != ErrorFileNotFound && verbose) {
52 fprintf(stderr, "Unable to read '%s': %s\n", buf_ptr(libc_file), err_str(err));
53 }
54 return err;
55 }
56
57 SplitIterator it = memSplit(buf_to_slice(contents), str("\n"));
58 for (;;) {
59 Optional<Slice<uint8_t>> opt_line = SplitIterator_next(&it);
60 if (!opt_line.is_some)
61 break;
62
63 if (opt_line.value.len == 0 || opt_line.value.ptr[0] == '#')
64 continue;
65
66 SplitIterator line_it = memSplit(opt_line.value, str("="));
67 Slice<uint8_t> name;
68 if (!SplitIterator_next(&line_it).unwrap(&name)) {
69 if (verbose) {
70 fprintf(stderr, "missing equal sign after field name\n");
71 }
72 return ErrorSemanticAnalyzeFail;
73 }
74 Slice<uint8_t> value = SplitIterator_rest(&line_it);
75 bool match = false;
76 match = match || zig_libc_match_key(name, value, found_keys, 0, &libc->include_dir);
77 match = match || zig_libc_match_key(name, value, found_keys, 1, &libc->sys_include_dir);
78 match = match || zig_libc_match_key(name, value, found_keys, 2, &libc->crt_dir);
79 match = match || zig_libc_match_key(name, value, found_keys, 3, &libc->static_crt_dir);
80 match = match || zig_libc_match_key(name, value, found_keys, 4, &libc->msvc_lib_dir);
81 match = match || zig_libc_match_key(name, value, found_keys, 5, &libc->kernel32_lib_dir);
82 }
83
84 for (size_t i = 0; i < zig_libc_keys_len; i += 1) {
85 if (!found_keys[i]) {
86 if (verbose) {
87 fprintf(stderr, "missing field: %s\n", zig_libc_keys[i]);
88 }
89 return ErrorSemanticAnalyzeFail;
90 }
91 }
92
93 if (buf_len(&libc->include_dir) == 0) {
94 if (verbose) {
95 fprintf(stderr, "include_dir may not be empty\n");
96 }
97 return ErrorSemanticAnalyzeFail;
98 }
99
100 if (buf_len(&libc->sys_include_dir) == 0) {
101 if (verbose) {
102 fprintf(stderr, "sys_include_dir may not be empty\n");
103 }
104 return ErrorSemanticAnalyzeFail;
105 }
106
107 if (buf_len(&libc->crt_dir) == 0) {
108 if (!target_os_is_darwin(target->os)) {
109 if (verbose) {
110 fprintf(stderr, "crt_dir may not be empty for %s\n", target_os_name(target->os));
111 }
112 return ErrorSemanticAnalyzeFail;
113 }
114 }
115
116 if (buf_len(&libc->static_crt_dir) == 0) {
117 if (target->os == OsWindows && target_abi_is_gnu(target->abi)) {
118 if (verbose) {
119 fprintf(stderr, "static_crt_dir may not be empty for %s\n", target_os_name(target->os));
120 }
121 return ErrorSemanticAnalyzeFail;
122 }
123 }
124
125 if (buf_len(&libc->msvc_lib_dir) == 0) {
126 if (target->os == OsWindows && !target_abi_is_gnu(target->abi)) {
127 if (verbose) {
128 fprintf(stderr, "msvc_lib_dir may not be empty for %s\n", target_os_name(target->os));
129 }
130 return ErrorSemanticAnalyzeFail;
131 }
132 }
133
134 if (buf_len(&libc->kernel32_lib_dir) == 0) {
135 if (target->os == OsWindows && !target_abi_is_gnu(target->abi)) {
136 if (verbose) {
137 fprintf(stderr, "kernel32_lib_dir may not be empty for %s\n", target_os_name(target->os));
138 }
139 return ErrorSemanticAnalyzeFail;
140 }
141 }
142
143 return ErrorNone;
144}
145
146#if defined(ZIG_OS_WINDOWS)
147#define CC_EXE "cc.exe"
148#else
149#define CC_EXE "cc"
150#endif
151
152static Error zig_libc_find_native_include_dir_posix(ZigLibCInstallation *self, bool verbose) {
153 const char *cc_exe = getenv("CC");
154 cc_exe = (cc_exe == nullptr) ? CC_EXE : cc_exe;
155 ZigList<const char *> args = {};
156 args.append(cc_exe);
157 args.append("-E");
158 args.append("-Wp,-v");
159 args.append("-xc");
160 #if defined(ZIG_OS_WINDOWS)
161 args.append("nul");
162 #else
163 args.append("/dev/null");
164 #endif
165
166 Termination term;
167 Buf *out_stderr = buf_alloc();
168 Buf *out_stdout = buf_alloc();
169 Error err;
170 if ((err = os_exec_process(args, &term, out_stderr, out_stdout))) {
171 if (verbose) {
172 fprintf(stderr, "unable to determine libc include path: executing '%s': %s\n", cc_exe, err_str(err));
173 }
174 return err;
175 }
176 if (term.how != TerminationIdClean || term.code != 0) {
177 if (verbose) {
178 fprintf(stderr, "unable to determine libc include path: executing '%s' failed\n", cc_exe);
179 }
180 return ErrorCCompileErrors;
181 }
182 char *prev_newline = buf_ptr(out_stderr);
183 ZigList<const char *> search_paths = {};
184 for (;;) {
185 char *newline = strchr(prev_newline, '\n');
186 if (newline == nullptr) {
187 break;
188 }
189
190 #if defined(ZIG_OS_WINDOWS)
191 *(newline - 1) = 0;
192 #endif
193 *newline = 0;
194
195 if (prev_newline[0] == ' ') {
196 search_paths.append(prev_newline);
197 }
198 prev_newline = newline + 1;
199 }
200 if (search_paths.length == 0) {
201 if (verbose) {
202 fprintf(stderr, "unable to determine libc include path: '%s' cannot find libc headers\n", cc_exe);
203 }
204 return ErrorCCompileErrors;
205 }
206 for (size_t i = 0; i < search_paths.length; i += 1) {
207 // search in reverse order
208 const char *search_path = search_paths.items[search_paths.length - i - 1];
209 // cut off spaces
210 while (*search_path == ' ') {
211 search_path += 1;
212 }
213
214 #if defined(ZIG_OS_WINDOWS)
215 if (buf_len(&self->include_dir) == 0) {
216 Buf *stdlib_path = buf_sprintf("%s\\stdlib.h", search_path);
217 bool exists;
218 if ((err = os_file_exists(stdlib_path, &exists))) {
219 exists = false;
220 }
221 if (exists) {
222 buf_init_from_str(&self->include_dir, search_path);
223 }
224 }
225 if (buf_len(&self->sys_include_dir) == 0) {
226 Buf *stdlib_path = buf_sprintf("%s\\sys\\types.h", search_path);
227 bool exists;
228 if ((err = os_file_exists(stdlib_path, &exists))) {
229 exists = false;
230 }
231 if (exists) {
232 buf_init_from_str(&self->sys_include_dir, search_path);
233 }
234 }
235 #else
236 if (buf_len(&self->include_dir) == 0) {
237 Buf *stdlib_path = buf_sprintf("%s/stdlib.h", search_path);
238 bool exists;
239 if ((err = os_file_exists(stdlib_path, &exists))) {
240 exists = false;
241 }
242 if (exists) {
243 buf_init_from_str(&self->include_dir, search_path);
244 }
245 }
246 if (buf_len(&self->sys_include_dir) == 0) {
247 Buf *stdlib_path = buf_sprintf("%s/sys/errno.h", search_path);
248 bool exists;
249 if ((err = os_file_exists(stdlib_path, &exists))) {
250 exists = false;
251 }
252 if (exists) {
253 buf_init_from_str(&self->sys_include_dir, search_path);
254 }
255 }
256 #endif
257
258 if (buf_len(&self->include_dir) != 0 && buf_len(&self->sys_include_dir) != 0) {
259 return ErrorNone;
260 }
261 }
262 if (verbose) {
263 if (buf_len(&self->include_dir) == 0) {
264 fprintf(stderr, "unable to determine libc include path: stdlib.h not found in '%s' search paths\n", cc_exe);
265 }
266 if (buf_len(&self->sys_include_dir) == 0) {
267 #if defined(ZIG_OS_WINDOWS)
268 fprintf(stderr, "unable to determine libc include path: sys/types.h not found in '%s' search paths\n", cc_exe);
269 #else
270 fprintf(stderr, "unable to determine libc include path: sys/errno.h not found in '%s' search paths\n", cc_exe);
271 #endif
272 }
273 }
274 return ErrorFileNotFound;
275}
276
277Error zig_libc_cc_print_file_name(const char *o_file, Buf *out, bool want_dirname, bool verbose) {
278 const char *cc_exe = getenv("CC");
279 cc_exe = (cc_exe == nullptr) ? CC_EXE : cc_exe;
280 ZigList<const char *> args = {};
281 args.append(cc_exe);
282 args.append(buf_ptr(buf_sprintf("-print-file-name=%s", o_file)));
283 Termination term;
284 Buf *out_stderr = buf_alloc();
285 Buf *out_stdout = buf_alloc();
286 Error err;
287 if ((err = os_exec_process(args, &term, out_stderr, out_stdout))) {
288 if (err == ErrorFileNotFound)
289 return ErrorNoCCompilerInstalled;
290 if (verbose) {
291 fprintf(stderr, "unable to determine libc library path: executing '%s': %s\n", cc_exe, err_str(err));
292 }
293 return err;
294 }
295 if (term.how != TerminationIdClean || term.code != 0) {
296 if (verbose) {
297 fprintf(stderr, "unable to determine libc library path: executing '%s' failed\n", cc_exe);
298 }
299 return ErrorCCompileErrors;
300 }
301 #if defined(ZIG_OS_WINDOWS)
302 if (buf_ends_with_str(out_stdout, "\r\n")) {
303 buf_resize(out_stdout, buf_len(out_stdout) - 2);
304 }
305 #else
306 if (buf_ends_with_str(out_stdout, "\n")) {
307 buf_resize(out_stdout, buf_len(out_stdout) - 1);
308 }
309 #endif
310 if (buf_len(out_stdout) == 0 || buf_eql_str(out_stdout, o_file)) {
311 return ErrorCCompilerCannotFindFile;
312 }
313 if (want_dirname) {
314 os_path_dirname(out_stdout, out);
315 } else {
316 buf_init_from_buf(out, out_stdout);
317 }
318 return ErrorNone;
319}
320
321#undef CC_EXE
322
323#if defined(ZIG_OS_WINDOWS) || defined(ZIG_OS_LINUX) || defined(ZIG_OS_DRAGONFLY)
324static Error zig_libc_find_native_crt_dir_posix(ZigLibCInstallation *self, bool verbose) {
325 return zig_libc_cc_print_file_name("crt1.o", &self->crt_dir, true, verbose);
326}
327#endif
328
329#if defined(ZIG_OS_WINDOWS)
330static Error zig_libc_find_native_static_crt_dir_posix(ZigLibCInstallation *self, bool verbose) {
331 return zig_libc_cc_print_file_name("crtbegin.o", &self->static_crt_dir, true, verbose);
332}
333
334static Error zig_libc_find_native_include_dir_windows(ZigLibCInstallation *self, ZigWindowsSDK *sdk, bool verbose) {
335 Error err;
336 if ((err = os_get_win32_ucrt_include_path(sdk, &self->include_dir))) {
337 if (verbose) {
338 fprintf(stderr, "Unable to determine libc include path: %s\n", err_str(err));
339 }
340 return err;
341 }
342 return ErrorNone;
343}
344
345static Error zig_libc_find_native_crt_dir_windows(ZigLibCInstallation *self, ZigWindowsSDK *sdk, ZigTarget *target,
346 bool verbose)
347{
348 Error err;
349 if ((err = os_get_win32_ucrt_lib_path(sdk, &self->crt_dir, target->arch))) {
350 if (verbose) {
351 fprintf(stderr, "Unable to determine ucrt path: %s\n", err_str(err));
352 }
353 return err;
354 }
355 return ErrorNone;
356}
357
358static Error zig_libc_find_kernel32_lib_dir(ZigLibCInstallation *self, ZigWindowsSDK *sdk, ZigTarget *target,
359 bool verbose)
360{
361 Error err;
362 if ((err = os_get_win32_kern32_path(sdk, &self->kernel32_lib_dir, target->arch))) {
363 if (verbose) {
364 fprintf(stderr, "Unable to determine kernel32 path: %s\n", err_str(err));
365 }
366 return err;
367 }
368 return ErrorNone;
369}
370
371static Error zig_libc_find_native_msvc_lib_dir(ZigLibCInstallation *self, ZigWindowsSDK *sdk, bool verbose) {
372 if (sdk->msvc_lib_dir_ptr == nullptr) {
373 if (verbose) {
374 fprintf(stderr, "Unable to determine vcruntime.lib path\n");
375 }
376 return ErrorFileNotFound;
377 }
378 buf_init_from_mem(&self->msvc_lib_dir, sdk->msvc_lib_dir_ptr, sdk->msvc_lib_dir_len);
379 return ErrorNone;
380}
381
382static Error zig_libc_find_native_msvc_include_dir(ZigLibCInstallation *self, ZigWindowsSDK *sdk, bool verbose) {
383 Error err;
384 if (sdk->msvc_lib_dir_ptr == nullptr) {
385 if (verbose) {
386 fprintf(stderr, "Unable to determine vcruntime.h path\n");
387 }
388 return ErrorFileNotFound;
389 }
390 Buf search_path = BUF_INIT;
391 buf_init_from_mem(&search_path, sdk->msvc_lib_dir_ptr, sdk->msvc_lib_dir_len);
392 buf_append_str(&search_path, "..\\..\\include");
393
394 Buf *vcruntime_path = buf_sprintf("%s\\vcruntime.h", buf_ptr(&search_path));
395 bool exists;
396 if ((err = os_file_exists(vcruntime_path, &exists))) {
397 exists = false;
398 }
399 if (exists) {
400 self->sys_include_dir = search_path;
401 return ErrorNone;
402 }
403
404 if (verbose) {
405 fprintf(stderr, "Unable to determine vcruntime.h path\n");
406 }
407 return ErrorFileNotFound;
408}
409#endif
410
411void zig_libc_render(ZigLibCInstallation *self, FILE *file) {
412 fprintf(file,
413 "# The directory that contains `stdlib.h`.\n"
414 "# On POSIX-like systems, include directories be found with: `cc -E -Wp,-v -xc /dev/null`\n"
415 "include_dir=%s\n"
416 "\n"
417 "# The system-specific include directory. May be the same as `include_dir`.\n"
418 "# On Windows it's the directory that includes `vcruntime.h`.\n"
419 "# On POSIX it's the directory that includes `sys/errno.h`.\n"
420 "sys_include_dir=%s\n"
421 "\n"
422 "# The directory that contains `crt1.o` or `crt2.o`.\n"
423 "# On POSIX, can be found with `cc -print-file-name=crt1.o`.\n"
424 "# Not needed when targeting MacOS.\n"
425 "crt_dir=%s\n"
426 "\n"
427 "# The directory that contains `crtbegin.o`.\n"
428 "# On POSIX, can be found with `cc -print-file-name=crtbegin.o`.\n"
429 "# Not needed when targeting MacOS.\n"
430 "static_crt_dir=%s\n"
431 "\n"
432 "# The directory that contains `vcruntime.lib`.\n"
433 "# Only needed when targeting MSVC on Windows.\n"
434 "msvc_lib_dir=%s\n"
435 "\n"
436 "# The directory that contains `kernel32.lib`.\n"
437 "# Only needed when targeting MSVC on Windows.\n"
438 "kernel32_lib_dir=%s\n"
439 "\n",
440 buf_ptr(&self->include_dir),
441 buf_ptr(&self->sys_include_dir),
442 buf_ptr(&self->crt_dir),
443 buf_ptr(&self->static_crt_dir),
444 buf_ptr(&self->msvc_lib_dir),
445 buf_ptr(&self->kernel32_lib_dir)
446 );
447}
448
449Error zig_libc_find_native(ZigLibCInstallation *self, bool verbose) {
450 Error err;
451 zig_libc_init_empty(self);
452#if defined(ZIG_OS_WINDOWS)
453 ZigTarget native_target;
454 get_native_target(&native_target);
455 if (target_abi_is_gnu(native_target.abi)) {
456 if ((err = zig_libc_find_native_include_dir_posix(self, verbose)))
457 return err;
458 if ((err = zig_libc_find_native_crt_dir_posix(self, verbose)))
459 return err;
460 if ((err = zig_libc_find_native_static_crt_dir_posix(self, verbose)))
461 return err;
462 return ErrorNone;
463 } else {
464 ZigWindowsSDK *sdk;
465 switch (zig_find_windows_sdk(&sdk)) {
466 case ZigFindWindowsSdkErrorNone:
467 if ((err = zig_libc_find_native_msvc_include_dir(self, sdk, verbose)))
468 return err;
469 if ((err = zig_libc_find_native_msvc_lib_dir(self, sdk, verbose)))
470 return err;
471 if ((err = zig_libc_find_kernel32_lib_dir(self, sdk, &native_target, verbose)))
472 return err;
473 if ((err = zig_libc_find_native_include_dir_windows(self, sdk, verbose)))
474 return err;
475 if ((err = zig_libc_find_native_crt_dir_windows(self, sdk, &native_target, verbose)))
476 return err;
477 return ErrorNone;
478 case ZigFindWindowsSdkErrorOutOfMemory:
479 return ErrorNoMem;
480 case ZigFindWindowsSdkErrorNotFound:
481 return ErrorFileNotFound;
482 case ZigFindWindowsSdkErrorPathTooLong:
483 return ErrorPathTooLong;
484 }
485 }
486 zig_unreachable();
487#else
488 if ((err = zig_libc_find_native_include_dir_posix(self, verbose)))
489 return err;
490#if defined(ZIG_OS_FREEBSD) || defined(ZIG_OS_NETBSD)
491 buf_init_from_str(&self->crt_dir, "/usr/lib");
492#elif defined(ZIG_OS_LINUX) || defined(ZIG_OS_DRAGONFLY)
493 if ((err = zig_libc_find_native_crt_dir_posix(self, verbose)))
494 return err;
495#endif
496 return ErrorNone;
497#endif
498}
src/libc_installation.hpp deleted-35
......@@ -1,35 +0,0 @@
1/*
2 * Copyright (c) 2019 Andrew Kelley
3 *
4 * This file is part of zig, which is MIT licensed.
5 * See http://opensource.org/licenses/MIT
6 */
7
8#ifndef ZIG_LIBC_INSTALLATION_HPP
9#define ZIG_LIBC_INSTALLATION_HPP
10
11#include <stdio.h>
12
13#include "buffer.hpp"
14#include "error.hpp"
15#include "target.hpp"
16
17// Must be synchronized with zig_libc_keys
18struct ZigLibCInstallation {
19 Buf include_dir;
20 Buf sys_include_dir;
21 Buf crt_dir;
22 Buf static_crt_dir;
23 Buf msvc_lib_dir;
24 Buf kernel32_lib_dir;
25};
26
27Error ATTRIBUTE_MUST_USE zig_libc_parse(ZigLibCInstallation *libc, Buf *libc_file,
28 const ZigTarget *target, bool verbose);
29void zig_libc_render(ZigLibCInstallation *self, FILE *file);
30
31Error ATTRIBUTE_MUST_USE zig_libc_find_native(ZigLibCInstallation *self, bool verbose);
32
33Error zig_libc_cc_print_file_name(const char *o_file, Buf *out, bool want_dirname, bool verbose);
34
35#endif
src/link.cpp+7-7
......@@ -1483,7 +1483,7 @@ static const char *get_libc_crt_file(CodeGen *parent, const char *file, Stage2Pr
14831483 } else {
14841484 assert(parent->libc != nullptr);
14851485 Buf *out_buf = buf_alloc();
1486 os_path_join(&parent->libc->crt_dir, buf_create_from_str(file), out_buf);
1486 os_path_join(buf_create_from_str(parent->libc->crt_dir), buf_create_from_str(file), out_buf);
14871487 return buf_ptr(out_buf);
14881488 }
14891489}
......@@ -1747,7 +1747,7 @@ static void construct_linker_job_elf(LinkJob *lj) {
17471747 if (g->libc_link_lib != nullptr) {
17481748 if (g->libc != nullptr) {
17491749 lj->args.append("-L");
1750 lj->args.append(buf_ptr(&g->libc->crt_dir));
1750 lj->args.append(g->libc->crt_dir);
17511751 }
17521752
17531753 if (g->have_dynamic_link && (is_dyn_lib || g->out_type == OutTypeExe)) {
......@@ -2251,14 +2251,14 @@ static void construct_linker_job_coff(LinkJob *lj) {
22512251 lj->args.append(buf_ptr(buf_sprintf("-OUT:%s", buf_ptr(&g->output_file_path))));
22522252
22532253 if (g->libc_link_lib != nullptr && g->libc != nullptr) {
2254 lj->args.append(buf_ptr(buf_sprintf("-LIBPATH:%s", buf_ptr(&g->libc->crt_dir))));
2254 lj->args.append(buf_ptr(buf_sprintf("-LIBPATH:%s", g->libc->crt_dir)));
22552255
22562256 if (target_abi_is_gnu(g->zig_target->abi)) {
2257 lj->args.append(buf_ptr(buf_sprintf("-LIBPATH:%s", buf_ptr(&g->libc->sys_include_dir))));
2258 lj->args.append(buf_ptr(buf_sprintf("-LIBPATH:%s", buf_ptr(&g->libc->include_dir))));
2257 lj->args.append(buf_ptr(buf_sprintf("-LIBPATH:%s", g->libc->sys_include_dir)));
2258 lj->args.append(buf_ptr(buf_sprintf("-LIBPATH:%s", g->libc->include_dir)));
22592259 } else {
2260 lj->args.append(buf_ptr(buf_sprintf("-LIBPATH:%s", buf_ptr(&g->libc->msvc_lib_dir))));
2261 lj->args.append(buf_ptr(buf_sprintf("-LIBPATH:%s", buf_ptr(&g->libc->kernel32_lib_dir))));
2260 lj->args.append(buf_ptr(buf_sprintf("-LIBPATH:%s", g->libc->msvc_lib_dir)));
2261 lj->args.append(buf_ptr(buf_sprintf("-LIBPATH:%s", g->libc->kernel32_lib_dir)));
22622262 }
22632263 }
22642264
src/main.cpp+15-9
......@@ -14,7 +14,6 @@
1414#include "heap.hpp"
1515#include "os.hpp"
1616#include "target.hpp"
17#include "libc_installation.hpp"
1817#include "userland.h"
1918#include "glibc.hpp"
2019#include "dump_analysis.hpp"
......@@ -1027,15 +1026,22 @@ static int main0(int argc, char **argv) {
10271026 switch (cmd) {
10281027 case CmdLibC: {
10291028 if (in_file) {
1030 ZigLibCInstallation libc;
1031 if ((err = zig_libc_parse(&libc, buf_create_from_str(in_file), &target, true)))
1029 Stage2LibCInstallation libc;
1030 if ((err = stage2_libc_parse(&libc, in_file))) {
1031 fprintf(stderr, "unable to parse libc file: %s\n", err_str(err));
10321032 return main_exit(root_progress_node, EXIT_FAILURE);
1033 }
10331034 return main_exit(root_progress_node, EXIT_SUCCESS);
10341035 }
1035 ZigLibCInstallation libc;
1036 if ((err = zig_libc_find_native(&libc, true)))
1036 Stage2LibCInstallation libc;
1037 if ((err = stage2_libc_find_native(&libc))) {
1038 fprintf(stderr, "unable to find native libc file: %s\n", err_str(err));
1039 return main_exit(root_progress_node, EXIT_FAILURE);
1040 }
1041 if ((err = stage2_libc_render(&libc, stdout))) {
1042 fprintf(stderr, "unable to print libc file: %s\n", err_str(err));
10371043 return main_exit(root_progress_node, EXIT_FAILURE);
1038 zig_libc_render(&libc, stdout);
1044 }
10391045 return main_exit(root_progress_node, EXIT_SUCCESS);
10401046 }
10411047 case CmdBuiltin: {
......@@ -1125,10 +1131,10 @@ static int main0(int argc, char **argv) {
11251131 if (cmd == CmdRun && buf_out_name == nullptr) {
11261132 buf_out_name = buf_create_from_str("run");
11271133 }
1128 ZigLibCInstallation *libc = nullptr;
1134 Stage2LibCInstallation *libc = nullptr;
11291135 if (libc_txt != nullptr) {
1130 libc = heap::c_allocator.create<ZigLibCInstallation>();
1131 if ((err = zig_libc_parse(libc, buf_create_from_str(libc_txt), &target, true))) {
1136 libc = heap::c_allocator.create<Stage2LibCInstallation>();
1137 if ((err = stage2_libc_parse(libc, libc_txt))) {
11321138 fprintf(stderr, "Unable to parse --libc text file: %s\n", err_str(err));
11331139 return main_exit(root_progress_node, EXIT_FAILURE);
11341140 }
src/os.cpp-102
......@@ -1551,108 +1551,6 @@ void os_stderr_set_color(TermColor color) {
15511551#endif
15521552}
15531553
1554Error os_get_win32_ucrt_lib_path(ZigWindowsSDK *sdk, Buf* output_buf, ZigLLVM_ArchType platform_type) {
1555#if defined(ZIG_OS_WINDOWS)
1556 buf_resize(output_buf, 0);
1557 buf_appendf(output_buf, "%sLib\\%s\\ucrt\\", sdk->path10_ptr, sdk->version10_ptr);
1558 switch (platform_type) {
1559 case ZigLLVM_x86:
1560 buf_append_str(output_buf, "x86\\");
1561 break;
1562 case ZigLLVM_x86_64:
1563 buf_append_str(output_buf, "x64\\");
1564 break;
1565 case ZigLLVM_arm:
1566 buf_append_str(output_buf, "arm\\");
1567 break;
1568 default:
1569 zig_panic("Attempted to use vcruntime for non-supported platform.");
1570 }
1571 Buf* tmp_buf = buf_alloc();
1572 buf_init_from_buf(tmp_buf, output_buf);
1573 buf_append_str(tmp_buf, "ucrt.lib");
1574 if (GetFileAttributesA(buf_ptr(tmp_buf)) != INVALID_FILE_ATTRIBUTES) {
1575 return ErrorNone;
1576 }
1577 else {
1578 buf_resize(output_buf, 0);
1579 return ErrorFileNotFound;
1580 }
1581#else
1582 return ErrorFileNotFound;
1583#endif
1584}
1585
1586Error os_get_win32_ucrt_include_path(ZigWindowsSDK *sdk, Buf* output_buf) {
1587#if defined(ZIG_OS_WINDOWS)
1588 buf_resize(output_buf, 0);
1589 buf_appendf(output_buf, "%sInclude\\%s\\ucrt", sdk->path10_ptr, sdk->version10_ptr);
1590 if (GetFileAttributesA(buf_ptr(output_buf)) != INVALID_FILE_ATTRIBUTES) {
1591 return ErrorNone;
1592 }
1593 else {
1594 buf_resize(output_buf, 0);
1595 return ErrorFileNotFound;
1596 }
1597#else
1598 return ErrorFileNotFound;
1599#endif
1600}
1601
1602Error os_get_win32_kern32_path(ZigWindowsSDK *sdk, Buf* output_buf, ZigLLVM_ArchType platform_type) {
1603#if defined(ZIG_OS_WINDOWS)
1604 {
1605 buf_resize(output_buf, 0);
1606 buf_appendf(output_buf, "%sLib\\%s\\um\\", sdk->path10_ptr, sdk->version10_ptr);
1607 switch (platform_type) {
1608 case ZigLLVM_x86:
1609 buf_append_str(output_buf, "x86\\");
1610 break;
1611 case ZigLLVM_x86_64:
1612 buf_append_str(output_buf, "x64\\");
1613 break;
1614 case ZigLLVM_arm:
1615 buf_append_str(output_buf, "arm\\");
1616 break;
1617 default:
1618 zig_panic("Attempted to use vcruntime for non-supported platform.");
1619 }
1620 Buf* tmp_buf = buf_alloc();
1621 buf_init_from_buf(tmp_buf, output_buf);
1622 buf_append_str(tmp_buf, "kernel32.lib");
1623 if (GetFileAttributesA(buf_ptr(tmp_buf)) != INVALID_FILE_ATTRIBUTES) {
1624 return ErrorNone;
1625 }
1626 }
1627 {
1628 buf_resize(output_buf, 0);
1629 buf_appendf(output_buf, "%sLib\\%s\\um\\", sdk->path81_ptr, sdk->version81_ptr);
1630 switch (platform_type) {
1631 case ZigLLVM_x86:
1632 buf_append_str(output_buf, "x86\\");
1633 break;
1634 case ZigLLVM_x86_64:
1635 buf_append_str(output_buf, "x64\\");
1636 break;
1637 case ZigLLVM_arm:
1638 buf_append_str(output_buf, "arm\\");
1639 break;
1640 default:
1641 zig_panic("Attempted to use vcruntime for non-supported platform.");
1642 }
1643 Buf* tmp_buf = buf_alloc();
1644 buf_init_from_buf(tmp_buf, output_buf);
1645 buf_append_str(tmp_buf, "kernel32.lib");
1646 if (GetFileAttributesA(buf_ptr(tmp_buf)) != INVALID_FILE_ATTRIBUTES) {
1647 return ErrorNone;
1648 }
1649 }
1650 return ErrorFileNotFound;
1651#else
1652 return ErrorFileNotFound;
1653#endif
1654}
1655
16561554#if defined(ZIG_OS_WINDOWS)
16571555// Ported from std/unicode.zig
16581556struct Utf16LeIterator {
src/os.hpp-4
......@@ -152,10 +152,6 @@ Error ATTRIBUTE_MUST_USE os_self_exe_path(Buf *out_path);
152152
153153Error ATTRIBUTE_MUST_USE os_get_app_data_dir(Buf *out_path, const char *appname);
154154
155Error ATTRIBUTE_MUST_USE os_get_win32_ucrt_include_path(ZigWindowsSDK *sdk, Buf *output_buf);
156Error ATTRIBUTE_MUST_USE os_get_win32_ucrt_lib_path(ZigWindowsSDK *sdk, Buf *output_buf, ZigLLVM_ArchType platform_type);
157Error ATTRIBUTE_MUST_USE os_get_win32_kern32_path(ZigWindowsSDK *sdk, Buf *output_buf, ZigLLVM_ArchType platform_type);
158
159155Error ATTRIBUTE_MUST_USE os_self_exe_shared_libs(ZigList<Buf *> &paths);
160156
161157#endif
src/userland.cpp+22
......@@ -144,3 +144,25 @@ int stage2_cmd_targets(const char *zig_triple) {
144144 const char *msg = "stage0 called stage2_cmd_targets";
145145 stage2_panic(msg, strlen(msg));
146146}
147
148enum Error stage2_libc_parse(struct Stage2LibCInstallation *libc, const char *libc_file) {
149 const char *msg = "stage0 called stage2_libc_parse";
150 stage2_panic(msg, strlen(msg));
151}
152
153enum Error stage2_libc_render(struct Stage2LibCInstallation *self, FILE *file) {
154 const char *msg = "stage0 called stage2_libc_render";
155 stage2_panic(msg, strlen(msg));
156}
157
158enum Error stage2_libc_find_native(struct Stage2LibCInstallation *libc) {
159 const char *msg = "stage0 called stage2_libc_find_native";
160 stage2_panic(msg, strlen(msg));
161}
162
163enum Error stage2_libc_cc_print_file_name(char **out_ptr, size_t *out_len,
164 const char *o_file, bool want_dirname)
165{
166 const char *msg = "stage0 called stage2_libc_cc_print_file_name";
167 stage2_panic(msg, strlen(msg));
168}
src/userland.h+40-1
......@@ -85,6 +85,20 @@ enum Error {
8585 ErrorInvalidLlvmCpuFeaturesFormat,
8686 ErrorUnknownApplicationBinaryInterface,
8787 ErrorASTUnitFailure,
88 ErrorBadPathName,
89 ErrorSymLinkLoop,
90 ErrorProcessFdQuotaExceeded,
91 ErrorSystemFdQuotaExceeded,
92 ErrorNoDevice,
93 ErrorDeviceBusy,
94 ErrorUnableToSpawnCCompiler,
95 ErrorCCompilerExitCode,
96 ErrorCCompilerCrashed,
97 ErrorCCompilerCannotFindHeaders,
98 ErrorLibCRuntimeNotFound,
99 ErrorLibCStdLibHeaderNotFound,
100 ErrorLibCKernel32LibNotFound,
101 ErrorUnsupportedArchitecture,
88102};
89103
90104// ABI warning
......@@ -185,7 +199,7 @@ ZIG_EXTERN_C void stage2_progress_update_node(Stage2ProgressNode *node,
185199struct Stage2CpuFeatures;
186200
187201// ABI warning
188ZIG_EXTERN_C Error stage2_cpu_features_parse(struct Stage2CpuFeatures **result,
202ZIG_EXTERN_C enum Error stage2_cpu_features_parse(struct Stage2CpuFeatures **result,
189203 const char *zig_triple, const char *cpu_name, const char *cpu_features);
190204
191205// ABI warning
......@@ -205,5 +219,30 @@ ZIG_EXTERN_C void stage2_cpu_features_get_cache_hash(const struct Stage2CpuFeatu
205219// ABI warning
206220ZIG_EXTERN_C int stage2_cmd_targets(const char *zig_triple);
207221
222// ABI warning
223struct Stage2LibCInstallation {
224 const char *include_dir;
225 size_t include_dir_len;
226 const char *sys_include_dir;
227 size_t sys_include_dir_len;
228 const char *crt_dir;
229 size_t crt_dir_len;
230 const char *static_crt_dir;
231 size_t static_crt_dir_len;
232 const char *msvc_lib_dir;
233 size_t msvc_lib_dir_len;
234 const char *kernel32_lib_dir;
235 size_t kernel32_lib_dir_len;
236};
237
238// ABI warning
239ZIG_EXTERN_C enum Error stage2_libc_parse(struct Stage2LibCInstallation *libc, const char *libc_file);
240// ABI warning
241ZIG_EXTERN_C enum Error stage2_libc_render(struct Stage2LibCInstallation *self, FILE *file);
242// ABI warning
243ZIG_EXTERN_C enum Error stage2_libc_find_native(struct Stage2LibCInstallation *libc);
244// ABI warning
245ZIG_EXTERN_C enum Error stage2_libc_cc_print_file_name(char **out_ptr, size_t *out_len,
246 const char *o_file, bool want_dirname);
208247
209248#endif
src/windows_sdk.h+4
......@@ -16,6 +16,7 @@
1616
1717#include <stddef.h>
1818
19// ABI warning - src-self-hosted/windows_sdk.zig
1920struct ZigWindowsSDK {
2021 const char *path10_ptr;
2122 size_t path10_len;
......@@ -33,6 +34,7 @@ struct ZigWindowsSDK {
3334 size_t msvc_lib_dir_len;
3435};
3536
37// ABI warning - src-self-hosted/windows_sdk.zig
3638enum ZigFindWindowsSdkError {
3739 ZigFindWindowsSdkErrorNone,
3840 ZigFindWindowsSdkErrorOutOfMemory,
......@@ -40,8 +42,10 @@ enum ZigFindWindowsSdkError {
4042 ZigFindWindowsSdkErrorPathTooLong,
4143};
4244
45// ABI warning - src-self-hosted/windows_sdk.zig
4346ZIG_EXTERN_C enum ZigFindWindowsSdkError zig_find_windows_sdk(struct ZigWindowsSDK **out_sdk);
4447
48// ABI warning - src-self-hosted/windows_sdk.zig
4549ZIG_EXTERN_C void zig_free_windows_sdk(struct ZigWindowsSDK *sdk);
4650
4751#endif