diff --git a/CMakeLists.txt b/CMakeLists.txt index 71d4e2cda3..5a4a6d3199 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -457,7 +457,6 @@ set(ZIG_SOURCES "${CMAKE_SOURCE_DIR}/src/heap.cpp" "${CMAKE_SOURCE_DIR}/src/ir.cpp" "${CMAKE_SOURCE_DIR}/src/ir_print.cpp" - "${CMAKE_SOURCE_DIR}/src/libc_installation.cpp" "${CMAKE_SOURCE_DIR}/src/link.cpp" "${CMAKE_SOURCE_DIR}/src/mem.cpp" "${CMAKE_SOURCE_DIR}/src/os.cpp" diff --git a/build.zig b/build.zig index 673fbfab7d..4567b63029 100644 --- a/build.zig +++ b/build.zig @@ -175,7 +175,7 @@ fn dependOnLib(b: *Builder, lib_exe_obj: var, dep: LibraryDep) void { } fn fileExists(filename: []const u8) !bool { - fs.File.access(filename) catch |err| switch (err) { + fs.cwd().access(filename, .{}) catch |err| switch (err) { error.FileNotFound => return false, else => return err, }; diff --git a/lib/std/c.zig b/lib/std/c.zig index e0c84beb78..93c6c24664 100644 --- a/lib/std/c.zig +++ b/lib/std/c.zig @@ -96,6 +96,7 @@ pub extern "c" fn getcwd(buf: [*]u8, size: usize) ?[*]u8; pub extern "c" fn waitpid(pid: c_int, stat_loc: *c_uint, options: c_uint) c_int; pub extern "c" fn fork() c_int; pub extern "c" fn access(path: [*:0]const u8, mode: c_uint) c_int; +pub extern "c" fn faccessat(dirfd: fd_t, path: [*:0]const u8, mode: c_uint, flags: c_uint) c_int; pub extern "c" fn pipe(fds: *[2]fd_t) c_int; pub extern "c" fn pipe2(fds: *[2]fd_t, flags: u32) c_int; pub extern "c" fn mkdir(path: [*:0]const u8, mode: c_uint) c_int; diff --git a/lib/std/child_process.zig b/lib/std/child_process.zig index fd67e3a680..f3626f5249 100644 --- a/lib/std/child_process.zig +++ b/lib/std/child_process.zig @@ -48,7 +48,6 @@ pub const ChildProcess = struct { cwd: ?[]const u8, err_pipe: if (builtin.os == .windows) void else [2]os.fd_t, - llnode: if (builtin.os == .windows) void else TailQueue(*ChildProcess).Node, pub const SpawnError = error{ OutOfMemory, @@ -90,7 +89,6 @@ pub const ChildProcess = struct { .handle = undefined, .thread_handle = undefined, .err_pipe = undefined, - .llnode = undefined, .term = null, .env_map = null, .cwd = null, @@ -453,7 +451,6 @@ pub const ChildProcess = struct { self.pid = pid; self.err_pipe = err_pipe; - self.llnode = TailQueue(*ChildProcess).Node.init(self); self.term = null; if (self.stdin_behavior == StdIo.Pipe) { diff --git a/lib/std/event.zig b/lib/std/event.zig index 64d73a25e1..d042e2ad9b 100644 --- a/lib/std/event.zig +++ b/lib/std/event.zig @@ -1,6 +1,7 @@ pub const Channel = @import("event/channel.zig").Channel; pub const Future = @import("event/future.zig").Future; pub const Group = @import("event/group.zig").Group; +pub const Batch = @import("event/batch.zig").Batch; pub const Lock = @import("event/lock.zig").Lock; pub const Locked = @import("event/locked.zig").Locked; pub const RwLock = @import("event/rwlock.zig").RwLock; @@ -11,6 +12,7 @@ test "import event tests" { _ = @import("event/channel.zig"); _ = @import("event/future.zig"); _ = @import("event/group.zig"); + _ = @import("event/batch.zig"); _ = @import("event/lock.zig"); _ = @import("event/locked.zig"); _ = @import("event/rwlock.zig"); diff --git a/lib/std/event/batch.zig b/lib/std/event/batch.zig new file mode 100644 index 0000000000..82dadb91be --- /dev/null +++ b/lib/std/event/batch.zig @@ -0,0 +1,139 @@ +const std = @import("../std.zig"); +const testing = std.testing; + +/// Performs multiple async functions in parallel, without heap allocation. +/// Async function frames are managed externally to this abstraction, and +/// passed in via the `add` function. Once all the jobs are added, call `wait`. +/// This API is *not* thread-safe. The object must be accessed from one thread at +/// a time, however, it need not be the same thread. +pub fn Batch( + /// The return value for each job. + /// If a job slot was re-used due to maxed out concurrency, then its result + /// value will be overwritten. The values can be accessed with the `results` field. + comptime Result: type, + /// How many jobs to run in parallel. + comptime max_jobs: comptime_int, + /// Controls whether the `add` and `wait` functions will be async functions. + comptime async_behavior: enum { + /// Observe the value of `std.io.is_async` to decide whether `add` + /// and `wait` will be async functions. Asserts that the jobs do not suspend when + /// `std.io.mode == .blocking`. This is a generally safe assumption, and the + /// usual recommended option for this parameter. + auto_async, + + /// Always uses the `noasync` keyword when using `await` on the jobs, + /// making `add` and `wait` non-async functions. Asserts that the jobs do not suspend. + never_async, + + /// `add` and `wait` use regular `await` keyword, making them async functions. + always_async, + }, +) type { + return struct { + jobs: [max_jobs]Job, + next_job_index: usize, + collected_result: CollectedResult, + + const Job = struct { + frame: ?anyframe->Result, + result: Result, + }; + + const Self = @This(); + + const CollectedResult = switch (@typeInfo(Result)) { + .ErrorUnion => Result, + else => void, + }; + + const async_ok = switch (async_behavior) { + .auto_async => std.io.is_async, + .never_async => false, + .always_async => true, + }; + + pub fn init() Self { + return Self{ + .jobs = [1]Job{ + .{ + .frame = null, + .result = undefined, + }, + } ** max_jobs, + .next_job_index = 0, + .collected_result = {}, + }; + } + + /// Add a frame to the Batch. If all jobs are in-flight, then this function + /// waits until one completes. + /// This function is *not* thread-safe. It must be called from one thread at + /// a time, however, it need not be the same thread. + /// TODO: "select" language feature to use the next available slot, rather than + /// awaiting the next index. + pub fn add(self: *Self, frame: anyframe->Result) void { + const job = &self.jobs[self.next_job_index]; + self.next_job_index = (self.next_job_index + 1) % max_jobs; + if (job.frame) |existing| { + job.result = if (async_ok) await existing else noasync await existing; + if (CollectedResult != void) { + job.result catch |err| { + self.collected_result = err; + }; + } + } + job.frame = frame; + } + + /// Wait for all the jobs to complete. + /// Safe to call any number of times. + /// If `Result` is an error union, this function returns the last error that occurred, if any. + /// Unlike the `results` field, the return value of `wait` will report any error that occurred; + /// hitting max parallelism will not compromise the result. + /// This function is *not* thread-safe. It must be called from one thread at + /// a time, however, it need not be the same thread. + pub fn wait(self: *Self) CollectedResult { + for (self.jobs) |*job| if (job.frame) |f| { + job.result = if (async_ok) await f else noasync await f; + if (CollectedResult != void) { + job.result catch |err| { + self.collected_result = err; + }; + } + job.frame = null; + }; + return self.collected_result; + } + }; +} + +test "std.event.Batch" { + var count: usize = 0; + var batch = Batch(void, 2).init(); + batch.add(&async sleepALittle(&count)); + batch.add(&async increaseByTen(&count)); + batch.wait(); + testing.expect(count == 11); + + var another = Batch(anyerror!void, 2).init(); + another.add(&async somethingElse()); + another.add(&async doSomethingThatFails()); + testing.expectError(error.ItBroke, another.wait()); +} + +fn sleepALittle(count: *usize) void { + std.time.sleep(1 * std.time.millisecond); + _ = @atomicRmw(usize, count, .Add, 1, .SeqCst); +} + +fn increaseByTen(count: *usize) void { + var i: usize = 0; + while (i < 10) : (i += 1) { + _ = @atomicRmw(usize, count, .Add, 1, .SeqCst); + } +} + +fn doSomethingThatFails() anyerror!void {} +fn somethingElse() anyerror!void { + return error.ItBroke; +} diff --git a/lib/std/event/group.zig b/lib/std/event/group.zig index 98ebdbd1f8..ac1bf68245 100644 --- a/lib/std/event/group.zig +++ b/lib/std/event/group.zig @@ -5,6 +5,11 @@ const testing = std.testing; const Allocator = std.mem.Allocator; /// ReturnType must be `void` or `E!void` +/// TODO This API was created back with the old design of async/await, when calling any +/// async function required an allocator. There is an ongoing experiment to transition +/// all uses of this API to the simpler and more resource-aware `std.event.Batch` API. +/// If the transition goes well, all usages of `Group` will be gone, and this API +/// will be deleted. pub fn Group(comptime ReturnType: type) type { return struct { frame_stack: Stack, diff --git a/lib/std/fs.zig b/lib/std/fs.zig index a39f283e55..c96aeeafc7 100644 --- a/lib/std/fs.zig +++ b/lib/std/fs.zig @@ -1323,6 +1323,38 @@ pub const Dir = struct { defer file.close(); try file.write(data); } + + pub const AccessError = os.AccessError; + + /// Test accessing `path`. + /// `path` is UTF8-encoded. + /// Be careful of Time-Of-Check-Time-Of-Use race conditions when using this function. + /// For example, instead of testing if a file exists and then opening it, just + /// open it and handle the error for file not found. + pub fn access(self: Dir, sub_path: []const u8, flags: File.OpenFlags) AccessError!void { + const path_c = try os.toPosixPath(sub_path); + return self.accessZ(&path_c, flags); + } + + /// Same as `access` except the path parameter is null-terminated. + pub fn accessZ(self: Dir, sub_path: [*:0]const u8, flags: File.OpenFlags) AccessError!void { + const os_mode = if (flags.write and flags.read) + @as(u32, os.R_OK | os.W_OK) + else if (flags.write) + @as(u32, os.W_OK) + else + @as(u32, os.F_OK); + const result = if (need_async_thread) + std.event.Loop.instance.?.faccessatZ(self.fd, sub_path, os_mode) + else + os.faccessatZ(self.fd, sub_path, os_mode, 0); + return result; + } + + /// Same as `access` except the parameter is null-terminated UTF16LE-encoded. + pub fn accessW(self: Dir, sub_path: [*:0]const u16, flags: File.OpenFlags) AccessError!void { + return os.faccessatW(self.fd, sub_path, 0, 0); + } }; /// Returns an handle to the current working directory that is open for traversal. diff --git a/lib/std/fs/file.zig b/lib/std/fs/file.zig index 92dcfefad0..a3a428e77b 100644 --- a/lib/std/fs/file.zig +++ b/lib/std/fs/file.zig @@ -60,31 +60,6 @@ pub const File = struct { mode: Mode = default_mode, }; - /// Test for the existence of `path`. - /// `path` is UTF8-encoded. - /// In general it is recommended to avoid this function. For example, - /// instead of testing if a file exists and then opening it, just - /// open it and handle the error for file not found. - /// TODO: deprecate this and move it to `std.fs.Dir`. - /// TODO: integrate with async I/O - pub fn access(path: []const u8) !void { - return os.access(path, os.F_OK); - } - - /// Same as `access` except the parameter is null-terminated. - /// TODO: deprecate this and move it to `std.fs.Dir`. - /// TODO: integrate with async I/O - pub fn accessC(path: [*:0]const u8) !void { - return os.accessC(path, os.F_OK); - } - - /// Same as `access` except the parameter is null-terminated UTF16LE-encoded. - /// TODO: deprecate this and move it to `std.fs.Dir`. - /// TODO: integrate with async I/O - pub fn accessW(path: [*:0]const u16) !void { - return os.accessW(path, os.F_OK); - } - /// Upon success, the stream is in an uninitialized state. To continue using it, /// you must use the open() function. pub fn close(self: File) void { diff --git a/lib/std/mem.zig b/lib/std/mem.zig index a1b4a3c809..71be738def 100644 --- a/lib/std/mem.zig +++ b/lib/std/mem.zig @@ -387,13 +387,21 @@ pub fn allEqual(comptime T: type, slice: []const T, scalar: T) bool { return true; } -/// Copies ::m to newly allocated memory. Caller is responsible to free it. +/// Copies `m` to newly allocated memory. Caller owns the memory. pub fn dupe(allocator: *Allocator, comptime T: type, m: []const T) ![]T { const new_buf = try allocator.alloc(T, m.len); copy(T, new_buf, m); return new_buf; } +/// Copies `m` to newly allocated memory, with a null-terminated element. Caller owns the memory. +pub fn dupeZ(allocator: *Allocator, comptime T: type, m: []const T) ![:0]T { + const new_buf = try allocator.alloc(T, m.len + 1); + copy(T, new_buf, m); + new_buf[m.len] = 0; + return new_buf[0..m.len :0]; +} + /// Remove values from the beginning of a slice. pub fn trimLeft(comptime T: type, slice: []const T, values_to_strip: []const T) []const T { var begin: usize = 0; diff --git a/lib/std/os.zig b/lib/std/os.zig index 73b6cbb302..fd85f8801b 100644 --- a/lib/std/os.zig +++ b/lib/std/os.zig @@ -950,7 +950,7 @@ pub fn execvpeC(file: [*:0]const u8, child_argv: [*:null]const ?[*:0]const u8, e const file_slice = mem.toSliceConst(u8, file); if (mem.indexOfScalar(u8, file_slice, '/') != null) return execveC(file, child_argv, envp); - const PATH = getenv("PATH") orelse "/usr/local/bin:/bin/:/usr/bin"; + const PATH = getenvZ("PATH") orelse "/usr/local/bin:/bin/:/usr/bin"; var path_buf: [MAX_PATH_BYTES]u8 = undefined; var it = mem.tokenize(PATH, ":"); var seen_eacces = false; @@ -1038,7 +1038,7 @@ pub fn freeNullDelimitedEnvMap(allocator: *mem.Allocator, envp_buf: []?[*:0]u8) } /// Get an environment variable. -/// See also `getenvC`. +/// See also `getenvZ`. /// TODO make this go through libc when we have it pub fn getenv(key: []const u8) ?[]const u8 { for (environ) |ptr| { @@ -1056,9 +1056,12 @@ pub fn getenv(key: []const u8) ?[]const u8 { return null; } +/// Deprecated in favor of `getenvZ`. +pub const getenvC = getenvZ; + /// Get an environment variable with a null-terminated name. /// See also `getenv`. -pub fn getenvC(key: [*:0]const u8) ?[]const u8 { +pub fn getenvZ(key: [*:0]const u8) ?[]const u8 { if (builtin.link_libc) { const value = system.getenv(key) orelse return null; return mem.toSliceConst(u8, value); @@ -2452,6 +2455,9 @@ pub const AccessError = error{ InputOutput, SystemResources, BadPathName, + FileBusy, + SymLinkLoop, + ReadOnlyFileSystem, /// On Windows, file paths must be valid Unicode. InvalidUtf8, @@ -2469,8 +2475,11 @@ pub fn access(path: []const u8, mode: u32) AccessError!void { return accessC(&path_c, mode); } +/// Deprecated in favor of `accessZ`. +pub const accessC = accessZ; + /// Same as `access` except `path` is null-terminated. -pub fn accessC(path: [*:0]const u8, mode: u32) AccessError!void { +pub fn accessZ(path: [*:0]const u8, mode: u32) AccessError!void { if (builtin.os == .windows) { const path_w = try windows.cStrToPrefixedFileW(path); _ = try windows.GetFileAttributesW(&path_w); @@ -2479,12 +2488,11 @@ pub fn accessC(path: [*:0]const u8, mode: u32) AccessError!void { switch (errno(system.access(path, mode))) { 0 => return, EACCES => return error.PermissionDenied, - EROFS => return error.PermissionDenied, - ELOOP => return error.PermissionDenied, - ETXTBSY => return error.PermissionDenied, + EROFS => return error.ReadOnlyFileSystem, + ELOOP => return error.SymLinkLoop, + ETXTBSY => return error.FileBusy, ENOTDIR => return error.FileNotFound, ENOENT => return error.FileNotFound, - ENAMETOOLONG => return error.NameTooLong, EINVAL => unreachable, EFAULT => unreachable, @@ -2510,6 +2518,47 @@ pub fn accessW(path: [*:0]const u16, mode: u32) windows.GetFileAttributesError!v } } +/// Check user's permissions for a file, based on an open directory handle. +/// TODO currently this ignores `mode` and `flags` on Windows. +pub fn faccessat(dirfd: fd_t, path: []const u8, mode: u32, flags: u32) AccessError!void { + if (builtin.os == .windows) { + const path_w = try windows.sliceToPrefixedFileW(path); + return faccessatW(dirfd, &path_w, mode, flags); + } + const path_c = try toPosixPath(path); + return faccessatZ(dirfd, &path_c, mode, flags); +} + +/// Same as `faccessat` except the path parameter is null-terminated. +pub fn faccessatZ(dirfd: fd_t, path: [*:0]const u8, mode: u32, flags: u32) AccessError!void { + if (builtin.os == .windows) { + const path_w = try windows.cStrToPrefixedFileW(path); + return faccessatW(dirfd, &path_w, mode, flags); + } + switch (errno(system.faccessat(dirfd, path, mode, flags))) { + 0 => return, + EACCES => return error.PermissionDenied, + EROFS => return error.ReadOnlyFileSystem, + ELOOP => return error.SymLinkLoop, + ETXTBSY => return error.FileBusy, + ENOTDIR => return error.FileNotFound, + ENOENT => return error.FileNotFound, + ENAMETOOLONG => return error.NameTooLong, + EINVAL => unreachable, + EFAULT => unreachable, + EIO => return error.InputOutput, + ENOMEM => return error.SystemResources, + else => |err| return unexpectedErrno(err), + } +} + +/// Same as `faccessat` except asserts the target is Windows and the path parameter +/// is null-terminated WTF-16 encoded. +/// TODO currently this ignores `mode` and `flags` +pub fn faccessatW(dirfd: fd_t, path: [*:0]const u16, mode: u32, flags: u32) AccessError!void { + @compileError("TODO implement faccessatW on Windows"); +} + pub const PipeError = error{ SystemFdQuotaExceeded, ProcessFdQuotaExceeded, diff --git a/lib/std/os/test.zig b/lib/std/os/test.zig index 9b967955ec..9ca4044a75 100644 --- a/lib/std/os/test.zig +++ b/lib/std/os/test.zig @@ -29,7 +29,7 @@ test "makePath, put some files in it, deleteTree" { test "access file" { try fs.makePath(a, "os_test_tmp"); - if (File.access("os_test_tmp" ++ fs.path.sep_str ++ "file.txt")) |ok| { + if (fs.cwd().access("os_test_tmp" ++ fs.path.sep_str ++ "file.txt", .{})) |ok| { @panic("expected error"); } else |err| { expect(err == error.FileNotFound); diff --git a/lib/std/target.zig b/lib/std/target.zig index 308f1d9923..fb3fe96075 100644 --- a/lib/std/target.zig +++ b/lib/std/target.zig @@ -1037,6 +1037,13 @@ pub const Target = union(enum) { }; } + pub fn isDragonFlyBSD(self: Target) bool { + return switch (self.getOs()) { + .dragonfly => true, + else => false, + }; + } + pub fn isUefi(self: Target) bool { return switch (self.getOs()) { .uefi => true, diff --git a/lib/std/time.zig b/lib/std/time.zig index 716c999577..a5f66122cb 100644 --- a/lib/std/time.zig +++ b/lib/std/time.zig @@ -8,6 +8,7 @@ const math = std.math; pub const epoch = @import("time/epoch.zig"); /// Spurious wakeups are possible and no precision of timing is guaranteed. +/// TODO integrate with evented I/O pub fn sleep(nanoseconds: u64) void { if (builtin.os == .windows) { const ns_per_ms = ns_per_s / ms_per_s; diff --git a/src-self-hosted/c.zig b/src-self-hosted/c.zig index 1de2b6a24c..ae9a886d1b 100644 --- a/src-self-hosted/c.zig +++ b/src-self-hosted/c.zig @@ -4,5 +4,4 @@ pub usingnamespace @cImport({ @cInclude("inttypes.h"); @cInclude("config.h"); @cInclude("zig_llvm.h"); - @cInclude("windows_sdk.h"); }); diff --git a/src-self-hosted/libc_installation.zig b/src-self-hosted/libc_installation.zig index 701823c4be..8f62e91e38 100644 --- a/src-self-hosted/libc_installation.zig +++ b/src-self-hosted/libc_installation.zig @@ -1,20 +1,29 @@ const std = @import("std"); const builtin = @import("builtin"); -const event = std.event; const util = @import("util.zig"); const Target = std.Target; -const c = @import("c.zig"); const fs = std.fs; const Allocator = std.mem.Allocator; +const Batch = std.event.Batch; + +const is_darwin = Target.current.isDarwin(); +const is_windows = Target.current.isWindows(); +const is_freebsd = Target.current.isFreeBSD(); +const is_netbsd = Target.current.isNetBSD(); +const is_linux = Target.current.isLinux(); +const is_dragonfly = Target.current.isDragonFlyBSD(); +const is_gnu = Target.current.isGnu(); + +usingnamespace @import("windows_sdk.zig"); /// See the render function implementation for documentation of the fields. pub const LibCInstallation = struct { - include_dir: []const u8, - lib_dir: ?[]const u8, - static_lib_dir: ?[]const u8, - msvc_lib_dir: ?[]const u8, - kernel32_lib_dir: ?[]const u8, - dynamic_linker_path: ?[]const u8, + include_dir: ?[:0]const u8 = null, + sys_include_dir: ?[:0]const u8 = null, + crt_dir: ?[:0]const u8 = null, + static_crt_dir: ?[:0]const u8 = null, + msvc_lib_dir: ?[:0]const u8 = null, + kernel32_lib_dir: ?[:0]const u8 = null, pub const FindError = error{ OutOfMemory, @@ -30,28 +39,20 @@ pub const LibCInstallation = struct { }; pub fn parse( - self: *LibCInstallation, allocator: *Allocator, libc_file: []const u8, stderr: *std.io.OutStream(fs.File.WriteError), - ) !void { - self.initEmpty(); + ) !LibCInstallation { + var self: LibCInstallation = .{}; - const keys = [_][]const u8{ - "include_dir", - "lib_dir", - "static_lib_dir", - "msvc_lib_dir", - "kernel32_lib_dir", - "dynamic_linker_path", - }; + const fields = std.meta.fields(LibCInstallation); const FoundKey = struct { found: bool, - allocated: ?[]u8, + allocated: ?[:0]u8, }; - var found_keys = [1]FoundKey{FoundKey{ .found = false, .allocated = null }} ** keys.len; + var found_keys = [1]FoundKey{FoundKey{ .found = false, .allocated = null }} ** fields.len; errdefer { - self.initEmpty(); + self = .{}; for (found_keys) |found_key| { if (found_key.allocated) |s| allocator.free(s); } @@ -69,152 +70,188 @@ pub const LibCInstallation = struct { return error.ParseError; }; const value = line_it.rest(); - inline for (keys) |key, i| { - if (std.mem.eql(u8, name, key)) { + inline for (fields) |field, i| { + if (std.mem.eql(u8, name, field.name)) { found_keys[i].found = true; - switch (@typeInfo(@TypeOf(@field(self, key)))) { - .Optional => { - if (value.len == 0) { - @field(self, key) = null; - } else { - found_keys[i].allocated = try std.mem.dupe(allocator, u8, value); - @field(self, key) = found_keys[i].allocated; - } - }, - else => { - if (value.len == 0) { - try stderr.print("field cannot be empty: {}\n", .{key}); - return error.ParseError; - } - const dupe = try std.mem.dupe(allocator, u8, value); - found_keys[i].allocated = dupe; - @field(self, key) = dupe; - }, + if (value.len == 0) { + @field(self, field.name) = null; + } else { + found_keys[i].allocated = try std.mem.dupeZ(allocator, u8, value); + @field(self, field.name) = found_keys[i].allocated; } break; } } } - for (found_keys) |found_key, i| { - if (!found_key.found) { - try stderr.print("missing field: {}\n", .{keys[i]}); + inline for (fields) |field, i| { + if (!found_keys[i].found) { + try stderr.print("missing field: {}\n", .{field.name}); return error.ParseError; } } + if (self.include_dir == null) { + try stderr.print("include_dir may not be empty\n", .{}); + return error.ParseError; + } + if (self.sys_include_dir == null) { + try stderr.print("sys_include_dir may not be empty\n", .{}); + return error.ParseError; + } + if (self.crt_dir == null and is_darwin) { + try stderr.print("crt_dir may not be empty for {}\n", .{@tagName(Target.current.getOs())}); + return error.ParseError; + } + if (self.static_crt_dir == null and is_windows and is_gnu) { + try stderr.print("static_crt_dir may not be empty for {}-{}\n", .{ + @tagName(Target.current.getOs()), + @tagName(Target.current.getAbi()), + }); + return error.ParseError; + } + if (self.msvc_lib_dir == null and is_windows and !is_gnu) { + try stderr.print("msvc_lib_dir may not be empty for {}-{}\n", .{ + @tagName(Target.current.getOs()), + @tagName(Target.current.getAbi()), + }); + return error.ParseError; + } + if (self.kernel32_lib_dir == null and is_windows and !is_gnu) { + try stderr.print("kernel32_lib_dir may not be empty for {}-{}\n", .{ + @tagName(Target.current.getOs()), + @tagName(Target.current.getAbi()), + }); + return error.ParseError; + } + + return self; } - pub fn render(self: *const LibCInstallation, out: *std.io.OutStream(fs.File.WriteError)) !void { + pub fn render(self: LibCInstallation, out: *std.io.OutStream(fs.File.WriteError)) !void { @setEvalBranchQuota(4000); - const lib_dir = self.lib_dir orelse ""; - const static_lib_dir = self.static_lib_dir orelse ""; + const include_dir = self.include_dir orelse ""; + const sys_include_dir = self.sys_include_dir orelse ""; + const crt_dir = self.crt_dir orelse ""; + const static_crt_dir = self.static_crt_dir orelse ""; const msvc_lib_dir = self.msvc_lib_dir orelse ""; const kernel32_lib_dir = self.kernel32_lib_dir orelse ""; - const dynamic_linker_path = self.dynamic_linker_path orelse util.getDynamicLinkerPath(Target{ .Native = {} }); + try out.print( \\# The directory that contains `stdlib.h`. - \\# On Linux, can be found with: `cc -E -Wp,-v -xc /dev/null` + \\# On POSIX-like systems, include directories be found with: `cc -E -Wp,-v -xc /dev/null` \\include_dir={} \\ - \\# The directory that contains `crt1.o`. - \\# On Linux, can be found with `cc -print-file-name=crt1.o`. + \\# The system-specific include directory. May be the same as `include_dir`. + \\# On Windows it's the directory that includes `vcruntime.h`. + \\# On POSIX it's the directory that includes `sys/errno.h`. + \\sys_include_dir={} + \\ + \\# The directory that contains `crt1.o` or `crt2.o`. + \\# On POSIX, can be found with `cc -print-file-name=crt1.o`. \\# Not needed when targeting MacOS. - \\lib_dir={} + \\crt_dir={} \\ \\# The directory that contains `crtbegin.o`. - \\# On Linux, can be found with `cc -print-file-name=crtbegin.o`. - \\# Not needed when targeting MacOS or Windows. - \\static_lib_dir={} + \\# On POSIX, can be found with `cc -print-file-name=crtbegin.o`. + \\# Not needed when targeting MacOS. + \\static_crt_dir={} \\ \\# The directory that contains `vcruntime.lib`. - \\# Only needed when targeting Windows. + \\# Only needed when targeting MSVC on Windows. \\msvc_lib_dir={} \\ \\# The directory that contains `kernel32.lib`. - \\# Only needed when targeting Windows. + \\# Only needed when targeting MSVC on Windows. \\kernel32_lib_dir={} \\ - \\# The full path to the dynamic linker, on the target system. - \\# Only needed when targeting Linux. - \\dynamic_linker_path={} - \\ - , .{ self.include_dir, lib_dir, static_lib_dir, msvc_lib_dir, kernel32_lib_dir, dynamic_linker_path }); + , .{ + include_dir, + sys_include_dir, + crt_dir, + static_crt_dir, + msvc_lib_dir, + kernel32_lib_dir, + }); } /// Finds the default, native libc. - pub fn findNative(self: *LibCInstallation, allocator: *Allocator) !void { - self.initEmpty(); - var group = event.Group(FindError!void).init(allocator); - errdefer group.wait() catch {}; - var windows_sdk: ?*c.ZigWindowsSDK = null; - errdefer if (windows_sdk) |sdk| c.zig_free_windows_sdk(@ptrCast(?[*]c.ZigWindowsSDK, sdk)); + pub fn findNative(allocator: *Allocator) !LibCInstallation { + var self: LibCInstallation = .{}; - switch (builtin.os) { - .windows => { - var sdk: *c.ZigWindowsSDK = undefined; - switch (c.zig_find_windows_sdk(@ptrCast(?[*]?[*]c.ZigWindowsSDK, &sdk))) { - c.ZigFindWindowsSdkError.None => { - windows_sdk = sdk; + if (is_windows) { + if (is_gnu) { + var batch = Batch(FindError!void, 3, .auto_async).init(); + batch.add(&async self.findNativeIncludeDirPosix(allocator)); + batch.add(&async self.findNativeCrtDirPosix(allocator)); + batch.add(&async self.findNativeStaticCrtDirPosix(allocator)); + try batch.wait(); + } else { + var sdk: *ZigWindowsSDK = undefined; + switch (zig_find_windows_sdk(&sdk)) { + .None => { + defer zig_free_windows_sdk(sdk); - if (sdk.msvc_lib_dir_ptr != 0) { - self.msvc_lib_dir = try std.mem.dupe(allocator, u8, sdk.msvc_lib_dir_ptr[0..sdk.msvc_lib_dir_len]); - } - try group.call(findNativeKernel32LibDir, .{ allocator, self, sdk }); - try group.call(findNativeIncludeDirWindows, .{ self, allocator, sdk }); - try group.call(findNativeLibDirWindows, .{ self, allocator, sdk }); + var batch = Batch(FindError!void, 5, .auto_async).init(); + batch.add(&async self.findNativeMsvcIncludeDir(allocator, sdk)); + batch.add(&async self.findNativeMsvcLibDir(allocator, sdk)); + batch.add(&async self.findNativeKernel32LibDir(allocator, sdk)); + batch.add(&async self.findNativeIncludeDirWindows(allocator, sdk)); + batch.add(&async self.findNativeCrtDirWindows(allocator, sdk)); + try batch.wait(); }, - c.ZigFindWindowsSdkError.OutOfMemory => return error.OutOfMemory, - c.ZigFindWindowsSdkError.NotFound => return error.NotFound, - c.ZigFindWindowsSdkError.PathTooLong => return error.NotFound, + .OutOfMemory => return error.OutOfMemory, + .NotFound => return error.NotFound, + .PathTooLong => return error.NotFound, } - }, - .linux => { - try group.call(findNativeIncludeDirLinux, .{ self, allocator }); - try group.call(findNativeLibDirLinux, .{ self, allocator }); - try group.call(findNativeStaticLibDir, .{ self, allocator }); - try group.call(findNativeDynamicLinker, .{ self, allocator }); - }, - .macosx, .freebsd, .netbsd => { - self.include_dir = try std.mem.dupe(allocator, u8, "/usr/include"); - }, - else => @compileError("unimplemented: find libc for this OS"), + } + } else { + var batch = Batch(FindError!void, 2, .auto_async).init(); + batch.add(&async self.findNativeIncludeDirPosix(allocator)); + if (is_freebsd or is_netbsd) { + self.crt_dir = try std.mem.dupeZ(allocator, u8, "/usr/lib"); + } else if (is_linux or is_dragonfly) { + batch.add(&async self.findNativeCrtDirPosix(allocator)); + } + try batch.wait(); } - return group.wait(); + return self; } - async fn findNativeIncludeDirLinux(self: *LibCInstallation, allocator: *Allocator) FindError!void { - const cc_exe = std.os.getenv("CC") orelse "cc"; + /// Must be the same allocator passed to `parse` or `findNative`. + pub fn deinit(self: *LibCInstallation, allocator: *Allocator) void { + const fields = std.meta.fields(LibCInstallation); + inline for (fields) |field| { + if (@field(self, field.name)) |payload| { + allocator.free(payload); + } + } + self.* = undefined; + } + + fn findNativeIncludeDirPosix(self: *LibCInstallation, allocator: *Allocator) FindError!void { + const dev_null = if (is_windows) "nul" else "/dev/null"; + const cc_exe = std.os.getenvZ("CC") orelse default_cc_exe; const argv = [_][]const u8{ cc_exe, "-E", "-Wp,-v", "-xc", - "/dev/null", + dev_null, }; - // TODO make this use event loop - const errorable_result = std.ChildProcess.exec(allocator, &argv, null, null, 1024 * 1024); - const exec_result = if (std.debug.runtime_safety) blk: { - break :blk errorable_result catch unreachable; - } else blk: { - break :blk errorable_result catch |err| switch (err) { - error.OutOfMemory => return error.OutOfMemory, - else => return error.UnableToSpawnCCompiler, - }; + const max_bytes = 1024 * 1024; + const exec_res = std.ChildProcess.exec(allocator, &argv, null, null, max_bytes) catch |err| switch (err) { + error.OutOfMemory => return error.OutOfMemory, + else => return error.UnableToSpawnCCompiler, }; defer { - allocator.free(exec_result.stdout); - allocator.free(exec_result.stderr); + allocator.free(exec_res.stdout); + allocator.free(exec_res.stderr); + } + switch (exec_res.term) { + .Exited => |code| if (code != 0) return error.CCompilerExitCode, + else => return error.CCompilerCrashed, } - switch (exec_result.term) { - .Exited => |code| { - if (code != 0) return error.CCompilerExitCode; - }, - else => { - return error.CCompilerCrashed; - }, - } - - var it = std.mem.tokenize(exec_result.stderr, "\n\r"); + var it = std.mem.tokenize(exec_res.stderr, "\n\r"); var search_paths = std.ArrayList([]const u8).init(allocator); defer search_paths.deinit(); while (it.next()) |line| { @@ -226,16 +263,44 @@ pub const LibCInstallation = struct { return error.CCompilerCannotFindHeaders; } - // search in reverse order + const include_dir_example_file = "stdlib.h"; + const sys_include_dir_example_file = if (is_windows) "sys\\types.h" else "sys/errno.h"; + var path_i: usize = 0; while (path_i < search_paths.len) : (path_i += 1) { + // search in reverse order const search_path_untrimmed = search_paths.at(search_paths.len - path_i - 1); const search_path = std.mem.trimLeft(u8, search_path_untrimmed, " "); - const stdlib_path = try fs.path.join(allocator, &[_][]const u8{ search_path, "stdlib.h" }); - defer allocator.free(stdlib_path); + var search_dir = fs.cwd().openDirList(search_path) catch |err| switch (err) { + error.FileNotFound, + error.NotDir, + error.NoDevice, + => continue, - if (try fileExists(stdlib_path)) { - self.include_dir = try std.mem.dupe(allocator, u8, search_path); + else => return error.FileSystem, + }; + defer search_dir.close(); + + if (self.include_dir == null) { + if (search_dir.accessZ(include_dir_example_file, .{})) |_| { + self.include_dir = try std.mem.dupeZ(allocator, u8, search_path); + } else |err| switch (err) { + error.FileNotFound => {}, + else => return error.FileSystem, + } + } + + if (self.sys_include_dir == null) { + if (search_dir.accessZ(sys_include_dir_example_file, .{})) |_| { + self.sys_include_dir = try std.mem.dupeZ(allocator, u8, search_path); + } else |err| switch (err) { + error.FileNotFound => {}, + else => return error.FileSystem, + } + } + + if (self.include_dir != null and self.sys_include_dir != null) { + // Success. return; } } @@ -243,7 +308,7 @@ pub const LibCInstallation = struct { return error.LibCStdLibHeaderNotFound; } - async fn findNativeIncludeDirWindows(self: *LibCInstallation, allocator: *Allocator, sdk: *c.ZigWindowsSDK) !void { + fn findNativeIncludeDirWindows(self: *LibCInstallation, allocator: *Allocator, sdk: *ZigWindowsSDK) !void { var search_buf: [2]Search = undefined; const searches = fillSearch(&search_buf, sdk); @@ -255,179 +320,152 @@ pub const LibCInstallation = struct { const stream = &std.io.BufferOutStream.init(&result_buf).stream; try stream.print("{}\\Include\\{}\\ucrt", .{ search.path, search.version }); - const stdlib_path = try fs.path.join( - allocator, - [_][]const u8{ result_buf.toSliceConst(), "stdlib.h" }, - ); - defer allocator.free(stdlib_path); + var dir = fs.cwd().openDirList(result_buf.toSliceConst()) catch |err| switch (err) { + error.FileNotFound, + error.NotDir, + error.NoDevice, + => continue, - if (try fileExists(stdlib_path)) { - self.include_dir = result_buf.toOwnedSlice(); - return; - } + else => return error.FileSystem, + }; + defer dir.close(); + + dir.accessZ("stdlib.h", .{}) catch |err| switch (err) { + error.FileNotFound => continue, + else => return error.FileSystem, + }; + + self.include_dir = result_buf.toOwnedSlice(); + return; } return error.LibCStdLibHeaderNotFound; } - async fn findNativeLibDirWindows(self: *LibCInstallation, allocator: *Allocator, sdk: *c.ZigWindowsSDK) FindError!void { + fn findNativeCrtDirWindows(self: *LibCInstallation, allocator: *Allocator, sdk: *ZigWindowsSDK) FindError!void { var search_buf: [2]Search = undefined; const searches = fillSearch(&search_buf, sdk); var result_buf = try std.Buffer.initSize(allocator, 0); defer result_buf.deinit(); + const arch_sub_dir = switch (builtin.arch) { + .i386 => "x86", + .x86_64 => "x64", + .arm, .armeb => "arm", + else => return error.UnsupportedArchitecture, + }; + for (searches) |search| { result_buf.shrink(0); const stream = &std.io.BufferOutStream.init(&result_buf).stream; - try stream.print("{}\\Lib\\{}\\ucrt\\", .{ search.path, search.version }); - switch (builtin.arch) { - .i386 => try stream.write("x86"), - .x86_64 => try stream.write("x64"), - .aarch64 => try stream.write("arm"), - else => return error.UnsupportedArchitecture, - } - const ucrt_lib_path = try fs.path.join( - allocator, - [_][]const u8{ result_buf.toSliceConst(), "ucrt.lib" }, - ); - defer allocator.free(ucrt_lib_path); - if (try fileExists(ucrt_lib_path)) { - self.lib_dir = result_buf.toOwnedSlice(); - return; - } + try stream.print("{}\\Lib\\{}\\ucrt\\{}", .{ search.path, search.version, arch_sub_dir }); + + var dir = fs.cwd().openDirList(result_buf.toSliceConst()) catch |err| switch (err) { + error.FileNotFound, + error.NotDir, + error.NoDevice, + => continue, + + else => return error.FileSystem, + }; + defer dir.close(); + + dir.accessZ("ucrt.lib", .{}) catch |err| switch (err) { + error.FileNotFound => continue, + else => return error.FileSystem, + }; + + self.crt_dir = result_buf.toOwnedSlice(); + return; } return error.LibCRuntimeNotFound; } - async fn findNativeLibDirLinux(self: *LibCInstallation, allocator: *Allocator) FindError!void { - self.lib_dir = try ccPrintFileName(allocator, "crt1.o", true); + fn findNativeCrtDirPosix(self: *LibCInstallation, allocator: *Allocator) FindError!void { + self.crt_dir = try ccPrintFileName(allocator, "crt1.o", .only_dir); } - async fn findNativeStaticLibDir(self: *LibCInstallation, allocator: *Allocator) FindError!void { - self.static_lib_dir = try ccPrintFileName(allocator, "crtbegin.o", true); + fn findNativeStaticCrtDirPosix(self: *LibCInstallation, allocator: *Allocator) FindError!void { + self.static_crt_dir = try ccPrintFileName(allocator, "crtbegin.o", .only_dir); } - async fn findNativeDynamicLinker(self: *LibCInstallation, allocator: *Allocator) FindError!void { - var dyn_tests = [_]DynTest{ - DynTest{ - .name = "ld-linux-x86-64.so.2", - .result = null, - }, - DynTest{ - .name = "ld-musl-x86_64.so.1", - .result = null, - }, - }; - var group = event.Group(FindError!void).init(allocator); - errdefer group.wait() catch {}; - for (dyn_tests) |*dyn_test| { - try group.call(testNativeDynamicLinker, .{ self, allocator, dyn_test }); - } - try group.wait(); - for (dyn_tests) |*dyn_test| { - if (dyn_test.result) |result| { - self.dynamic_linker_path = result; - return; - } - } - } - - const DynTest = struct { - name: []const u8, - result: ?[]const u8, - }; - - async fn testNativeDynamicLinker(self: *LibCInstallation, allocator: *Allocator, dyn_test: *DynTest) FindError!void { - if (ccPrintFileName(allocator, dyn_test.name, false)) |result| { - dyn_test.result = result; - return; - } else |err| switch (err) { - error.LibCRuntimeNotFound => return, - else => return err, - } - } - - async fn findNativeKernel32LibDir(self: *LibCInstallation, allocator: *Allocator, sdk: *c.ZigWindowsSDK) FindError!void { + fn findNativeKernel32LibDir(self: *LibCInstallation, allocator: *Allocator, sdk: *ZigWindowsSDK) FindError!void { var search_buf: [2]Search = undefined; const searches = fillSearch(&search_buf, sdk); var result_buf = try std.Buffer.initSize(allocator, 0); defer result_buf.deinit(); + const arch_sub_dir = switch (builtin.arch) { + .i386 => "x86", + .x86_64 => "x64", + .arm, .armeb => "arm", + else => return error.UnsupportedArchitecture, + }; + for (searches) |search| { result_buf.shrink(0); const stream = &std.io.BufferOutStream.init(&result_buf).stream; - try stream.print("{}\\Lib\\{}\\um\\", .{ search.path, search.version }); - switch (builtin.arch) { - .i386 => try stream.write("x86\\"), - .x86_64 => try stream.write("x64\\"), - .aarch64 => try stream.write("arm\\"), - else => return error.UnsupportedArchitecture, - } - const kernel32_path = try fs.path.join( - allocator, - [_][]const u8{ result_buf.toSliceConst(), "kernel32.lib" }, - ); - defer allocator.free(kernel32_path); - if (try fileExists(kernel32_path)) { - self.kernel32_lib_dir = result_buf.toOwnedSlice(); - return; - } + try stream.print("{}\\Lib\\{}\\um\\{}", .{ search.path, search.version, arch_sub_dir }); + + var dir = fs.cwd().openDirList(result_buf.toSliceConst()) catch |err| switch (err) { + error.FileNotFound, + error.NotDir, + error.NoDevice, + => continue, + + else => return error.FileSystem, + }; + defer dir.close(); + + dir.accessZ("kernel32.lib", .{}) catch |err| switch (err) { + error.FileNotFound => continue, + else => return error.FileSystem, + }; + + self.kernel32_lib_dir = result_buf.toOwnedSlice(); + return; } return error.LibCKernel32LibNotFound; } - - fn initEmpty(self: *LibCInstallation) void { - self.* = LibCInstallation{ - .include_dir = @as([*]const u8, undefined)[0..0], - .lib_dir = null, - .static_lib_dir = null, - .msvc_lib_dir = null, - .kernel32_lib_dir = null, - .dynamic_linker_path = null, - }; - } }; +const default_cc_exe = if (is_windows) "cc.exe" else "cc"; + /// caller owns returned memory -fn ccPrintFileName(allocator: *Allocator, o_file: []const u8, want_dirname: bool) ![]u8 { - const cc_exe = std.os.getenv("CC") orelse "cc"; +pub fn ccPrintFileName( + allocator: *Allocator, + o_file: []const u8, + want_dirname: enum { full_path, only_dir }, +) ![:0]u8 { + const cc_exe = std.os.getenvZ("CC") orelse default_cc_exe; const arg1 = try std.fmt.allocPrint(allocator, "-print-file-name={}", .{o_file}); defer allocator.free(arg1); const argv = [_][]const u8{ cc_exe, arg1 }; - // TODO This simulates evented I/O for the child process exec - event.Loop.startCpuBoundOperation(); - const errorable_result = std.ChildProcess.exec(allocator, &argv, null, null, 1024 * 1024); - const exec_result = if (std.debug.runtime_safety) blk: { - break :blk errorable_result catch unreachable; - } else blk: { - break :blk errorable_result catch |err| switch (err) { - error.OutOfMemory => return error.OutOfMemory, - else => return error.UnableToSpawnCCompiler, - }; + const max_bytes = 1024 * 1024; + const exec_res = std.ChildProcess.exec(allocator, &argv, null, null, max_bytes) catch |err| switch (err) { + error.OutOfMemory => return error.OutOfMemory, + else => return error.UnableToSpawnCCompiler, }; defer { - allocator.free(exec_result.stdout); - allocator.free(exec_result.stderr); + allocator.free(exec_res.stdout); + allocator.free(exec_res.stderr); } - switch (exec_result.term) { - .Exited => |code| { - if (code != 0) return error.CCompilerExitCode; - }, - else => { - return error.CCompilerCrashed; - }, + switch (exec_res.term) { + .Exited => |code| if (code != 0) return error.CCompilerExitCode, + else => return error.CCompilerCrashed, } - var it = std.mem.tokenize(exec_result.stdout, "\n\r"); - const line = it.next() orelse return error.LibCRuntimeNotFound; - const dirname = fs.path.dirname(line) orelse return error.LibCRuntimeNotFound; - if (want_dirname) { - return std.mem.dupe(allocator, u8, dirname); - } else { - return std.mem.dupe(allocator, u8, line); + var it = std.mem.tokenize(exec_res.stdout, "\n\r"); + const line = it.next() orelse return error.LibCRuntimeNotFound; + switch (want_dirname) { + .full_path => return std.mem.dupeZ(allocator, u8, line), + .only_dir => { + const dirname = fs.path.dirname(line) orelse return error.LibCRuntimeNotFound; + return std.mem.dupeZ(allocator, u8, dirname); + }, } } @@ -436,34 +474,25 @@ const Search = struct { version: []const u8, }; -fn fillSearch(search_buf: *[2]Search, sdk: *c.ZigWindowsSDK) []Search { +fn fillSearch(search_buf: *[2]Search, sdk: *ZigWindowsSDK) []Search { var search_end: usize = 0; - if (sdk.path10_ptr != 0) { - if (sdk.version10_ptr != 0) { + if (sdk.path10_ptr) |path10_ptr| { + if (sdk.version10_ptr) |version10_ptr| { search_buf[search_end] = Search{ - .path = sdk.path10_ptr[0..sdk.path10_len], - .version = sdk.version10_ptr[0..sdk.version10_len], + .path = path10_ptr[0..sdk.path10_len], + .version = version10_ptr[0..sdk.version10_len], }; search_end += 1; } } - if (sdk.path81_ptr != 0) { - if (sdk.version81_ptr != 0) { + if (sdk.path81_ptr) |path81_ptr| { + if (sdk.version81_ptr) |version81_ptr| { search_buf[search_end] = Search{ - .path = sdk.path81_ptr[0..sdk.path81_len], - .version = sdk.version81_ptr[0..sdk.version81_len], + .path = path81_ptr[0..sdk.path81_len], + .version = version81_ptr[0..sdk.version81_len], }; search_end += 1; } } return search_buf[0..search_end]; } - -fn fileExists(path: []const u8) !bool { - if (fs.File.access(path)) |_| { - return true; - } else |err| switch (err) { - error.FileNotFound => return false, - else => return error.FileSystem, - } -} diff --git a/src-self-hosted/stage1.zig b/src-self-hosted/stage1.zig index ffa0754bb5..95719d47a9 100644 --- a/src-self-hosted/stage1.zig +++ b/src-self-hosted/stage1.zig @@ -14,6 +14,7 @@ const self_hosted_main = @import("main.zig"); const errmsg = @import("errmsg.zig"); const DepTokenizer = @import("dep_tokenizer.zig").Tokenizer; const assert = std.debug.assert; +const LibCInstallation = @import("libc_installation.zig").LibCInstallation; var stderr_file: fs.File = undefined; var stderr: *io.OutStream(fs.File.WriteError) = undefined; @@ -93,6 +94,20 @@ const Error = extern enum { InvalidLlvmCpuFeaturesFormat, UnknownApplicationBinaryInterface, ASTUnitFailure, + BadPathName, + SymLinkLoop, + ProcessFdQuotaExceeded, + SystemFdQuotaExceeded, + NoDevice, + DeviceBusy, + UnableToSpawnCCompiler, + CCompilerExitCode, + CCompilerCrashed, + CCompilerCannotFindHeaders, + LibCRuntimeNotFound, + LibCStdLibHeaderNotFound, + LibCKernel32LibNotFound, + UnsupportedArchitecture, }; const FILE = std.c.FILE; @@ -113,12 +128,12 @@ export fn stage2_translate_c( error.SemanticAnalyzeFail => { out_errors_ptr.* = errors.ptr; out_errors_len.* = errors.len; - return Error.CCompileErrors; + return .CCompileErrors; }, - error.ASTUnitFailure => return Error.ASTUnitFailure, - error.OutOfMemory => return Error.OutOfMemory, + error.ASTUnitFailure => return .ASTUnitFailure, + error.OutOfMemory => return .OutOfMemory, }; - return Error.None; + return .None; } export 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 { const c_out_stream = &std.io.COutStream.init(output_file).stream; _ = std.zig.render(std.heap.c_allocator, c_out_stream, tree) catch |e| switch (e) { error.WouldBlock => unreachable, // stage1 opens stuff in exclusively blocking mode - error.SystemResources => return Error.SystemResources, - error.OperationAborted => return Error.OperationAborted, - error.BrokenPipe => return Error.BrokenPipe, - error.DiskQuota => return Error.DiskQuota, - error.FileTooBig => return Error.FileTooBig, - error.NoSpaceLeft => return Error.NoSpaceLeft, - error.AccessDenied => return Error.AccessDenied, - error.OutOfMemory => return Error.OutOfMemory, - error.Unexpected => return Error.Unexpected, - error.InputOutput => return Error.FileSystem, + error.SystemResources => return .SystemResources, + error.OperationAborted => return .OperationAborted, + error.BrokenPipe => return .BrokenPipe, + error.DiskQuota => return .DiskQuota, + error.FileTooBig => return .FileTooBig, + error.NoSpaceLeft => return .NoSpaceLeft, + error.AccessDenied => return .AccessDenied, + error.OutOfMemory => return .OutOfMemory, + error.Unexpected => return .Unexpected, + error.InputOutput => return .FileSystem, }; - return Error.None; + return .None; } // 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 export fn stage2_cpu_features_get_llvm_features(cpu_features: *const Stage2CpuFeatures) ?[*:0]const u8 { return cpu_features.llvm_features_str; } + +// ABI warning +const Stage2LibCInstallation = extern struct { + include_dir: [*:0]const u8, + include_dir_len: usize, + sys_include_dir: [*:0]const u8, + sys_include_dir_len: usize, + crt_dir: [*:0]const u8, + crt_dir_len: usize, + static_crt_dir: [*:0]const u8, + static_crt_dir_len: usize, + msvc_lib_dir: [*:0]const u8, + msvc_lib_dir_len: usize, + kernel32_lib_dir: [*:0]const u8, + kernel32_lib_dir_len: usize, + + fn initFromStage2(self: *Stage2LibCInstallation, libc: LibCInstallation) void { + if (libc.include_dir) |s| { + self.include_dir = s.ptr; + self.include_dir_len = s.len; + } else { + self.include_dir = ""; + self.include_dir_len = 0; + } + if (libc.sys_include_dir) |s| { + self.sys_include_dir = s.ptr; + self.sys_include_dir_len = s.len; + } else { + self.sys_include_dir = ""; + self.sys_include_dir_len = 0; + } + if (libc.crt_dir) |s| { + self.crt_dir = s.ptr; + self.crt_dir_len = s.len; + } else { + self.crt_dir = ""; + self.crt_dir_len = 0; + } + if (libc.static_crt_dir) |s| { + self.static_crt_dir = s.ptr; + self.static_crt_dir_len = s.len; + } else { + self.static_crt_dir = ""; + self.static_crt_dir_len = 0; + } + if (libc.msvc_lib_dir) |s| { + self.msvc_lib_dir = s.ptr; + self.msvc_lib_dir_len = s.len; + } else { + self.msvc_lib_dir = ""; + self.msvc_lib_dir_len = 0; + } + if (libc.kernel32_lib_dir) |s| { + self.kernel32_lib_dir = s.ptr; + self.kernel32_lib_dir_len = s.len; + } else { + self.kernel32_lib_dir = ""; + self.kernel32_lib_dir_len = 0; + } + } + + fn toStage2(self: Stage2LibCInstallation) LibCInstallation { + var libc: LibCInstallation = .{}; + if (self.include_dir_len != 0) { + libc.include_dir = self.include_dir[0..self.include_dir_len :0]; + } + if (self.sys_include_dir_len != 0) { + libc.sys_include_dir = self.sys_include_dir[0..self.sys_include_dir_len :0]; + } + if (self.crt_dir_len != 0) { + libc.crt_dir = self.crt_dir[0..self.crt_dir_len :0]; + } + if (self.static_crt_dir_len != 0) { + libc.static_crt_dir = self.static_crt_dir[0..self.static_crt_dir_len :0]; + } + if (self.msvc_lib_dir_len != 0) { + libc.msvc_lib_dir = self.msvc_lib_dir[0..self.msvc_lib_dir_len :0]; + } + if (self.kernel32_lib_dir_len != 0) { + libc.kernel32_lib_dir = self.kernel32_lib_dir[0..self.kernel32_lib_dir_len :0]; + } + return libc; + } +}; + +// ABI warning +export fn stage2_libc_parse(stage1_libc: *Stage2LibCInstallation, libc_file_z: [*:0]const u8) Error { + stderr_file = std.io.getStdErr(); + stderr = &stderr_file.outStream().stream; + const libc_file = mem.toSliceConst(u8, libc_file_z); + var libc = LibCInstallation.parse(std.heap.c_allocator, libc_file, stderr) catch |err| switch (err) { + error.ParseError => return .SemanticAnalyzeFail, + error.DiskQuota => return .DiskQuota, + error.FileTooBig => return .FileTooBig, + error.InputOutput => return .FileSystem, + error.NoSpaceLeft => return .NoSpaceLeft, + error.AccessDenied => return .AccessDenied, + error.BrokenPipe => return .BrokenPipe, + error.SystemResources => return .SystemResources, + error.OperationAborted => return .OperationAborted, + error.WouldBlock => unreachable, + error.Unexpected => return .Unexpected, + error.EndOfStream => return .EndOfFile, + error.IsDir => return .IsDir, + error.ConnectionResetByPeer => unreachable, + error.OutOfMemory => return .OutOfMemory, + error.Unseekable => unreachable, + error.SharingViolation => return .SharingViolation, + error.PathAlreadyExists => unreachable, + error.FileNotFound => return .FileNotFound, + error.PipeBusy => return .PipeBusy, + error.NameTooLong => return .PathTooLong, + error.InvalidUtf8 => return .BadPathName, + error.BadPathName => return .BadPathName, + error.SymLinkLoop => return .SymLinkLoop, + error.ProcessFdQuotaExceeded => return .ProcessFdQuotaExceeded, + error.SystemFdQuotaExceeded => return .SystemFdQuotaExceeded, + error.NoDevice => return .NoDevice, + error.NotDir => return .NotDir, + error.DeviceBusy => return .DeviceBusy, + }; + stage1_libc.initFromStage2(libc); + return .None; +} + +// ABI warning +export fn stage2_libc_find_native(stage1_libc: *Stage2LibCInstallation) Error { + var libc = LibCInstallation.findNative(std.heap.c_allocator) catch |err| switch (err) { + error.OutOfMemory => return .OutOfMemory, + error.FileSystem => return .FileSystem, + error.UnableToSpawnCCompiler => return .UnableToSpawnCCompiler, + error.CCompilerExitCode => return .CCompilerExitCode, + error.CCompilerCrashed => return .CCompilerCrashed, + error.CCompilerCannotFindHeaders => return .CCompilerCannotFindHeaders, + error.LibCRuntimeNotFound => return .LibCRuntimeNotFound, + error.LibCStdLibHeaderNotFound => return .LibCStdLibHeaderNotFound, + error.LibCKernel32LibNotFound => return .LibCKernel32LibNotFound, + error.UnsupportedArchitecture => return .UnsupportedArchitecture, + }; + stage1_libc.initFromStage2(libc); + return .None; +} + +// ABI warning +export fn stage2_libc_render(stage1_libc: *Stage2LibCInstallation, output_file: *FILE) Error { + var libc = stage1_libc.toStage2(); + const c_out_stream = &std.io.COutStream.init(output_file).stream; + libc.render(c_out_stream) catch |err| switch (err) { + error.WouldBlock => unreachable, // stage1 opens stuff in exclusively blocking mode + error.SystemResources => return .SystemResources, + error.OperationAborted => return .OperationAborted, + error.BrokenPipe => return .BrokenPipe, + error.DiskQuota => return .DiskQuota, + error.FileTooBig => return .FileTooBig, + error.NoSpaceLeft => return .NoSpaceLeft, + error.AccessDenied => return .AccessDenied, + error.Unexpected => return .Unexpected, + error.InputOutput => return .FileSystem, + }; + return .None; +} + +// ABI warning +export fn stage2_libc_cc_print_file_name( + out_ptr: *[*:0]u8, + out_len: *usize, + o_file: [*:0]const u8, + want_dirname: bool, +) Error { + const result = @import("libc_installation.zig").ccPrintFileName( + std.heap.c_allocator, + mem.toSliceConst(u8, o_file), + if (want_dirname) .only_dir else .full_path, + ) catch |err| switch (err) { + error.OutOfMemory => return .OutOfMemory, + error.LibCRuntimeNotFound => return .FileNotFound, + error.CCompilerExitCode => return .CCompilerExitCode, + error.CCompilerCrashed => return .CCompilerCrashed, + error.UnableToSpawnCCompiler => return .UnableToSpawnCCompiler, + }; + out_ptr.* = result.ptr; + out_len.* = result.len; + return .None; +} diff --git a/src-self-hosted/windows_sdk.zig b/src-self-hosted/windows_sdk.zig new file mode 100644 index 0000000000..6dfdeb99fd --- /dev/null +++ b/src-self-hosted/windows_sdk.zig @@ -0,0 +1,22 @@ +// C API bindings for src/windows_sdk.h + +pub const ZigWindowsSDK = extern struct { + path10_ptr: ?[*]const u8, + path10_len: usize, + version10_ptr: ?[*]const u8, + version10_len: usize, + path81_ptr: ?[*]const u8, + path81_len: usize, + version81_ptr: ?[*]const u8, + version81_len: usize, + msvc_lib_dir_ptr: ?[*]const u8, + msvc_lib_dir_len: usize, +}; +pub const ZigFindWindowsSdkError = extern enum { + None, + OutOfMemory, + NotFound, + PathTooLong, +}; +pub extern fn zig_find_windows_sdk(out_sdk: **ZigWindowsSDK) ZigFindWindowsSdkError; +pub extern fn zig_free_windows_sdk(sdk: *ZigWindowsSDK) void; diff --git a/src/all_types.hpp b/src/all_types.hpp index 95f291b343..c5804a3dc4 100644 --- a/src/all_types.hpp +++ b/src/all_types.hpp @@ -18,7 +18,6 @@ #include "bigfloat.hpp" #include "target.hpp" #include "tokenizer.hpp" -#include "libc_installation.hpp" struct AstNode; struct ZigFn; @@ -2139,7 +2138,7 @@ struct CodeGen { // As an input parameter, mutually exclusive with enable_cache. But it gets // populated in codegen_build_and_link. Buf *output_dir; - Buf **libc_include_dir_list; + const char **libc_include_dir_list; size_t libc_include_dir_len; Buf *zig_c_headers_dir; // Cannot be overridden; derived from zig_lib_dir. @@ -2220,7 +2219,7 @@ struct CodeGen { ZigList lib_dirs; ZigList framework_dirs; - ZigLibCInstallation *libc; + Stage2LibCInstallation *libc; size_t version_major; size_t version_minor; diff --git a/src/codegen.cpp b/src/codegen.cpp index a0bc56f093..22d76c1372 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -8982,17 +8982,20 @@ static void detect_dynamic_linker(CodeGen *g) { #if defined(ZIG_OS_LINUX) { Error err; - Buf *result = buf_alloc(); for (size_t i = 0; possible_ld_names[i] != NULL; i += 1) { const char *lib_name = possible_ld_names[i]; - if ((err = zig_libc_cc_print_file_name(lib_name, result, false, true))) { + char *result_ptr; + size_t result_len; + if ((err = stage2_libc_cc_print_file_name(&result_ptr, &result_len, lib_name, false))) { if (err != ErrorCCompilerCannotFindFile && err != ErrorNoCCompilerInstalled) { fprintf(stderr, "Unable to detect native dynamic linker: %s\n", err_str(err)); exit(1); } continue; } - g->dynamic_linker_path = result; + g->dynamic_linker_path = buf_create_from_mem(result_ptr, result_len); + // Skips heap::c_allocator because the memory is allocated by stage2 library. + free(result_ptr); return; } } @@ -9028,16 +9031,16 @@ static void detect_libc(CodeGen *g) { buf_ptr(g->zig_lib_dir), target_os_name(g->zig_target->os)); g->libc_include_dir_len = 4; - g->libc_include_dir_list = heap::c_allocator.allocate(g->libc_include_dir_len); - g->libc_include_dir_list[0] = arch_include_dir; - g->libc_include_dir_list[1] = generic_include_dir; - g->libc_include_dir_list[2] = arch_os_include_dir; - g->libc_include_dir_list[3] = generic_os_include_dir; + g->libc_include_dir_list = heap::c_allocator.allocate(g->libc_include_dir_len); + g->libc_include_dir_list[0] = buf_ptr(arch_include_dir); + g->libc_include_dir_list[1] = buf_ptr(generic_include_dir); + g->libc_include_dir_list[2] = buf_ptr(arch_os_include_dir); + g->libc_include_dir_list[3] = buf_ptr(generic_os_include_dir); return; } if (g->zig_target->is_native) { - g->libc = heap::c_allocator.create(); + g->libc = heap::c_allocator.create(); // search for native_libc.txt in following dirs: // - LOCAL_CACHE_DIR @@ -9082,8 +9085,8 @@ static void detect_libc(CodeGen *g) { if (libc_txt == nullptr) libc_txt = &global_libc_txt; - if ((err = zig_libc_parse(g->libc, libc_txt, g->zig_target, false))) { - if ((err = zig_libc_find_native(g->libc, true))) { + if ((err = stage2_libc_parse(g->libc, buf_ptr(libc_txt)))) { + if ((err = stage2_libc_find_native(g->libc))) { fprintf(stderr, "Unable to link against libc: Unable to find libc installation: %s\n" "See `zig libc --help` for more details.\n", err_str(err)); @@ -9103,7 +9106,7 @@ static void detect_libc(CodeGen *g) { fprintf(stderr, "Unable to open %s: %s\n", buf_ptr(native_libc_tmp), strerror(errno)); exit(1); } - zig_libc_render(g->libc, file); + stage2_libc_render(g->libc, file); if (fclose(file) != 0) { fprintf(stderr, "Unable to save %s: %s\n", buf_ptr(native_libc_tmp), strerror(errno)); exit(1); @@ -9113,27 +9116,28 @@ static void detect_libc(CodeGen *g) { exit(1); } } - bool want_sys_dir = !buf_eql_buf(&g->libc->include_dir, &g->libc->sys_include_dir); + bool want_sys_dir = !mem_eql_mem(g->libc->include_dir, g->libc->include_dir_len, + g->libc->sys_include_dir, g->libc->sys_include_dir_len); size_t want_um_and_shared_dirs = (g->zig_target->os == OsWindows) ? 2 : 0; size_t dir_count = 1 + want_sys_dir + want_um_and_shared_dirs; g->libc_include_dir_len = 0; - g->libc_include_dir_list = heap::c_allocator.allocate(dir_count); + g->libc_include_dir_list = heap::c_allocator.allocate(dir_count); - g->libc_include_dir_list[g->libc_include_dir_len] = &g->libc->include_dir; + g->libc_include_dir_list[g->libc_include_dir_len] = g->libc->include_dir; g->libc_include_dir_len += 1; if (want_sys_dir) { - g->libc_include_dir_list[g->libc_include_dir_len] = &g->libc->sys_include_dir; + g->libc_include_dir_list[g->libc_include_dir_len] = g->libc->sys_include_dir; g->libc_include_dir_len += 1; } if (want_um_and_shared_dirs != 0) { - g->libc_include_dir_list[g->libc_include_dir_len] = buf_sprintf("%s" OS_SEP ".." OS_SEP "um", - buf_ptr(&g->libc->include_dir)); + g->libc_include_dir_list[g->libc_include_dir_len] = buf_ptr(buf_sprintf( + "%s" OS_SEP ".." OS_SEP "um", g->libc->include_dir)); g->libc_include_dir_len += 1; - g->libc_include_dir_list[g->libc_include_dir_len] = buf_sprintf("%s" OS_SEP ".." OS_SEP "shared", - buf_ptr(&g->libc->include_dir)); + g->libc_include_dir_list[g->libc_include_dir_len] = buf_ptr(buf_sprintf( + "%s" OS_SEP ".." OS_SEP "shared", g->libc->include_dir)); g->libc_include_dir_len += 1; } assert(g->libc_include_dir_len == dir_count); @@ -9208,9 +9212,9 @@ void add_cc_args(CodeGen *g, ZigList &args, const char *out_dep_pa args.append(buf_ptr(g->zig_c_headers_dir)); for (size_t i = 0; i < g->libc_include_dir_len; i += 1) { - Buf *include_dir = g->libc_include_dir_list[i]; + const char *include_dir = g->libc_include_dir_list[i]; args.append("-isystem"); - args.append(buf_ptr(include_dir)); + args.append(include_dir); } if (g->zig_target->is_native) { @@ -9666,7 +9670,7 @@ Error create_c_object_cache(CodeGen *g, CacheHash **out_cache_hash, bool verbose cache_buf(cache_hash, compiler_id); cache_int(cache_hash, g->err_color); cache_buf(cache_hash, g->zig_c_headers_dir); - cache_list_of_buf(cache_hash, g->libc_include_dir_list, g->libc_include_dir_len); + cache_list_of_str(cache_hash, g->libc_include_dir_list, g->libc_include_dir_len); cache_int(cache_hash, g->zig_target->is_native); cache_int(cache_hash, g->zig_target->arch); cache_int(cache_hash, g->zig_target->sub_arch); @@ -10482,11 +10486,11 @@ static Error check_cache(CodeGen *g, Buf *manifest_dir, Buf *digest) { cache_list_of_str(ch, g->lib_dirs.items, g->lib_dirs.length); cache_list_of_str(ch, g->framework_dirs.items, g->framework_dirs.length); if (g->libc) { - cache_buf(ch, &g->libc->include_dir); - cache_buf(ch, &g->libc->sys_include_dir); - cache_buf(ch, &g->libc->crt_dir); - cache_buf(ch, &g->libc->msvc_lib_dir); - cache_buf(ch, &g->libc->kernel32_lib_dir); + cache_str(ch, g->libc->include_dir); + cache_str(ch, g->libc->sys_include_dir); + cache_str(ch, g->libc->crt_dir); + cache_str(ch, g->libc->msvc_lib_dir); + cache_str(ch, g->libc->kernel32_lib_dir); } cache_buf_opt(ch, g->dynamic_linker_path); 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 } CodeGen *create_child_codegen(CodeGen *parent_gen, Buf *root_src_path, OutType out_type, - ZigLibCInstallation *libc, const char *name, Stage2ProgressNode *parent_progress_node) + Stage2LibCInstallation *libc, const char *name, Stage2ProgressNode *parent_progress_node) { Stage2ProgressNode *child_progress_node = stage2_progress_start( 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 CodeGen *codegen_create(Buf *main_pkg_path, Buf *root_src_path, const ZigTarget *target, OutType out_type, BuildMode build_mode, Buf *override_lib_dir, - ZigLibCInstallation *libc, Buf *cache_dir, bool is_test_build, Stage2ProgressNode *progress_node) + Stage2LibCInstallation *libc, Buf *cache_dir, bool is_test_build, Stage2ProgressNode *progress_node) { CodeGen *g = heap::c_allocator.create(); g->pass1_arena = heap::ArenaAllocator::construct(&heap::c_allocator, &heap::c_allocator, "pass1"); diff --git a/src/codegen.hpp b/src/codegen.hpp index b9bcf05d28..623ae12fef 100644 --- a/src/codegen.hpp +++ b/src/codegen.hpp @@ -11,17 +11,16 @@ #include "parser.hpp" #include "errmsg.hpp" #include "target.hpp" -#include "libc_installation.hpp" #include "userland.h" #include CodeGen *codegen_create(Buf *main_pkg_path, Buf *root_src_path, const ZigTarget *target, OutType out_type, BuildMode build_mode, Buf *zig_lib_dir, - ZigLibCInstallation *libc, Buf *cache_dir, bool is_test_build, Stage2ProgressNode *progress_node); + Stage2LibCInstallation *libc, Buf *cache_dir, bool is_test_build, Stage2ProgressNode *progress_node); CodeGen *create_child_codegen(CodeGen *parent_gen, Buf *root_src_path, OutType out_type, - ZigLibCInstallation *libc, const char *name, Stage2ProgressNode *progress_node); + Stage2LibCInstallation *libc, const char *name, Stage2ProgressNode *progress_node); void codegen_set_clang_argv(CodeGen *codegen, const char **args, size_t len); void codegen_set_llvm_argv(CodeGen *codegen, const char **args, size_t len); diff --git a/src/error.cpp b/src/error.cpp index 871b6b9e35..1e0267eb67 100644 --- a/src/error.cpp +++ b/src/error.cpp @@ -65,6 +65,20 @@ const char *err_str(Error err) { case ErrorInvalidLlvmCpuFeaturesFormat: return "invalid LLVM CPU features format"; case ErrorUnknownApplicationBinaryInterface: return "unknown application binary interface"; 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"; + case ErrorBadPathName: return "bad path name"; + case ErrorSymLinkLoop: return "sym link loop"; + case ErrorProcessFdQuotaExceeded: return "process fd quota exceeded"; + case ErrorSystemFdQuotaExceeded: return "system fd quota exceeded"; + case ErrorNoDevice: return "no device"; + case ErrorDeviceBusy: return "device busy"; + case ErrorUnableToSpawnCCompiler: return "unable to spawn system C compiler"; + case ErrorCCompilerExitCode: return "system C compiler exited with failure code"; + case ErrorCCompilerCrashed: return "system C compiler crashed"; + case ErrorCCompilerCannotFindHeaders: return "system C compiler cannot find libc headers"; + case ErrorLibCRuntimeNotFound: return "libc runtime not found"; + case ErrorLibCStdLibHeaderNotFound: return "libc std lib headers not found"; + case ErrorLibCKernel32LibNotFound: return "kernel32 library not found"; + case ErrorUnsupportedArchitecture: return "unsupported architecture"; } return "(invalid error)"; } diff --git a/src/libc_installation.cpp b/src/libc_installation.cpp deleted file mode 100644 index 2adc1cb69d..0000000000 --- a/src/libc_installation.cpp +++ /dev/null @@ -1,498 +0,0 @@ -/* - * Copyright (c) 2019 Andrew Kelley - * - * This file is part of zig, which is MIT licensed. - * See http://opensource.org/licenses/MIT - */ - -#include "libc_installation.hpp" -#include "os.hpp" -#include "windows_sdk.h" -#include "target.hpp" - -static const char *zig_libc_keys[] = { - "include_dir", - "sys_include_dir", - "crt_dir", - "static_crt_dir", - "msvc_lib_dir", - "kernel32_lib_dir", -}; - -static const size_t zig_libc_keys_len = array_length(zig_libc_keys); - -static bool zig_libc_match_key(Slice name, Slice value, bool *found_keys, - size_t index, Buf *field_ptr) -{ - if (!memEql(name, str(zig_libc_keys[index]))) return false; - buf_init_from_mem(field_ptr, (const char*)value.ptr, value.len); - found_keys[index] = true; - return true; -} - -static void zig_libc_init_empty(ZigLibCInstallation *libc) { - *libc = {}; - buf_init_from_str(&libc->include_dir, ""); - buf_init_from_str(&libc->sys_include_dir, ""); - buf_init_from_str(&libc->crt_dir, ""); - buf_init_from_str(&libc->static_crt_dir, ""); - buf_init_from_str(&libc->msvc_lib_dir, ""); - buf_init_from_str(&libc->kernel32_lib_dir, ""); -} - -Error zig_libc_parse(ZigLibCInstallation *libc, Buf *libc_file, const ZigTarget *target, bool verbose) { - Error err; - zig_libc_init_empty(libc); - - bool found_keys[array_length(zig_libc_keys)] = {}; - - Buf *contents = buf_alloc(); - if ((err = os_fetch_file_path(libc_file, contents))) { - if (err != ErrorFileNotFound && verbose) { - fprintf(stderr, "Unable to read '%s': %s\n", buf_ptr(libc_file), err_str(err)); - } - return err; - } - - SplitIterator it = memSplit(buf_to_slice(contents), str("\n")); - for (;;) { - Optional> opt_line = SplitIterator_next(&it); - if (!opt_line.is_some) - break; - - if (opt_line.value.len == 0 || opt_line.value.ptr[0] == '#') - continue; - - SplitIterator line_it = memSplit(opt_line.value, str("=")); - Slice name; - if (!SplitIterator_next(&line_it).unwrap(&name)) { - if (verbose) { - fprintf(stderr, "missing equal sign after field name\n"); - } - return ErrorSemanticAnalyzeFail; - } - Slice value = SplitIterator_rest(&line_it); - bool match = false; - match = match || zig_libc_match_key(name, value, found_keys, 0, &libc->include_dir); - match = match || zig_libc_match_key(name, value, found_keys, 1, &libc->sys_include_dir); - match = match || zig_libc_match_key(name, value, found_keys, 2, &libc->crt_dir); - match = match || zig_libc_match_key(name, value, found_keys, 3, &libc->static_crt_dir); - match = match || zig_libc_match_key(name, value, found_keys, 4, &libc->msvc_lib_dir); - match = match || zig_libc_match_key(name, value, found_keys, 5, &libc->kernel32_lib_dir); - } - - for (size_t i = 0; i < zig_libc_keys_len; i += 1) { - if (!found_keys[i]) { - if (verbose) { - fprintf(stderr, "missing field: %s\n", zig_libc_keys[i]); - } - return ErrorSemanticAnalyzeFail; - } - } - - if (buf_len(&libc->include_dir) == 0) { - if (verbose) { - fprintf(stderr, "include_dir may not be empty\n"); - } - return ErrorSemanticAnalyzeFail; - } - - if (buf_len(&libc->sys_include_dir) == 0) { - if (verbose) { - fprintf(stderr, "sys_include_dir may not be empty\n"); - } - return ErrorSemanticAnalyzeFail; - } - - if (buf_len(&libc->crt_dir) == 0) { - if (!target_os_is_darwin(target->os)) { - if (verbose) { - fprintf(stderr, "crt_dir may not be empty for %s\n", target_os_name(target->os)); - } - return ErrorSemanticAnalyzeFail; - } - } - - if (buf_len(&libc->static_crt_dir) == 0) { - if (target->os == OsWindows && target_abi_is_gnu(target->abi)) { - if (verbose) { - fprintf(stderr, "static_crt_dir may not be empty for %s\n", target_os_name(target->os)); - } - return ErrorSemanticAnalyzeFail; - } - } - - if (buf_len(&libc->msvc_lib_dir) == 0) { - if (target->os == OsWindows && !target_abi_is_gnu(target->abi)) { - if (verbose) { - fprintf(stderr, "msvc_lib_dir may not be empty for %s\n", target_os_name(target->os)); - } - return ErrorSemanticAnalyzeFail; - } - } - - if (buf_len(&libc->kernel32_lib_dir) == 0) { - if (target->os == OsWindows && !target_abi_is_gnu(target->abi)) { - if (verbose) { - fprintf(stderr, "kernel32_lib_dir may not be empty for %s\n", target_os_name(target->os)); - } - return ErrorSemanticAnalyzeFail; - } - } - - return ErrorNone; -} - -#if defined(ZIG_OS_WINDOWS) -#define CC_EXE "cc.exe" -#else -#define CC_EXE "cc" -#endif - -static Error zig_libc_find_native_include_dir_posix(ZigLibCInstallation *self, bool verbose) { - const char *cc_exe = getenv("CC"); - cc_exe = (cc_exe == nullptr) ? CC_EXE : cc_exe; - ZigList args = {}; - args.append(cc_exe); - args.append("-E"); - args.append("-Wp,-v"); - args.append("-xc"); - #if defined(ZIG_OS_WINDOWS) - args.append("nul"); - #else - args.append("/dev/null"); - #endif - - Termination term; - Buf *out_stderr = buf_alloc(); - Buf *out_stdout = buf_alloc(); - Error err; - if ((err = os_exec_process(args, &term, out_stderr, out_stdout))) { - if (verbose) { - fprintf(stderr, "unable to determine libc include path: executing '%s': %s\n", cc_exe, err_str(err)); - } - return err; - } - if (term.how != TerminationIdClean || term.code != 0) { - if (verbose) { - fprintf(stderr, "unable to determine libc include path: executing '%s' failed\n", cc_exe); - } - return ErrorCCompileErrors; - } - char *prev_newline = buf_ptr(out_stderr); - ZigList search_paths = {}; - for (;;) { - char *newline = strchr(prev_newline, '\n'); - if (newline == nullptr) { - break; - } - - #if defined(ZIG_OS_WINDOWS) - *(newline - 1) = 0; - #endif - *newline = 0; - - if (prev_newline[0] == ' ') { - search_paths.append(prev_newline); - } - prev_newline = newline + 1; - } - if (search_paths.length == 0) { - if (verbose) { - fprintf(stderr, "unable to determine libc include path: '%s' cannot find libc headers\n", cc_exe); - } - return ErrorCCompileErrors; - } - for (size_t i = 0; i < search_paths.length; i += 1) { - // search in reverse order - const char *search_path = search_paths.items[search_paths.length - i - 1]; - // cut off spaces - while (*search_path == ' ') { - search_path += 1; - } - - #if defined(ZIG_OS_WINDOWS) - if (buf_len(&self->include_dir) == 0) { - Buf *stdlib_path = buf_sprintf("%s\\stdlib.h", search_path); - bool exists; - if ((err = os_file_exists(stdlib_path, &exists))) { - exists = false; - } - if (exists) { - buf_init_from_str(&self->include_dir, search_path); - } - } - if (buf_len(&self->sys_include_dir) == 0) { - Buf *stdlib_path = buf_sprintf("%s\\sys\\types.h", search_path); - bool exists; - if ((err = os_file_exists(stdlib_path, &exists))) { - exists = false; - } - if (exists) { - buf_init_from_str(&self->sys_include_dir, search_path); - } - } - #else - if (buf_len(&self->include_dir) == 0) { - Buf *stdlib_path = buf_sprintf("%s/stdlib.h", search_path); - bool exists; - if ((err = os_file_exists(stdlib_path, &exists))) { - exists = false; - } - if (exists) { - buf_init_from_str(&self->include_dir, search_path); - } - } - if (buf_len(&self->sys_include_dir) == 0) { - Buf *stdlib_path = buf_sprintf("%s/sys/errno.h", search_path); - bool exists; - if ((err = os_file_exists(stdlib_path, &exists))) { - exists = false; - } - if (exists) { - buf_init_from_str(&self->sys_include_dir, search_path); - } - } - #endif - - if (buf_len(&self->include_dir) != 0 && buf_len(&self->sys_include_dir) != 0) { - return ErrorNone; - } - } - if (verbose) { - if (buf_len(&self->include_dir) == 0) { - fprintf(stderr, "unable to determine libc include path: stdlib.h not found in '%s' search paths\n", cc_exe); - } - if (buf_len(&self->sys_include_dir) == 0) { - #if defined(ZIG_OS_WINDOWS) - fprintf(stderr, "unable to determine libc include path: sys/types.h not found in '%s' search paths\n", cc_exe); - #else - fprintf(stderr, "unable to determine libc include path: sys/errno.h not found in '%s' search paths\n", cc_exe); - #endif - } - } - return ErrorFileNotFound; -} - -Error zig_libc_cc_print_file_name(const char *o_file, Buf *out, bool want_dirname, bool verbose) { - const char *cc_exe = getenv("CC"); - cc_exe = (cc_exe == nullptr) ? CC_EXE : cc_exe; - ZigList args = {}; - args.append(cc_exe); - args.append(buf_ptr(buf_sprintf("-print-file-name=%s", o_file))); - Termination term; - Buf *out_stderr = buf_alloc(); - Buf *out_stdout = buf_alloc(); - Error err; - if ((err = os_exec_process(args, &term, out_stderr, out_stdout))) { - if (err == ErrorFileNotFound) - return ErrorNoCCompilerInstalled; - if (verbose) { - fprintf(stderr, "unable to determine libc library path: executing '%s': %s\n", cc_exe, err_str(err)); - } - return err; - } - if (term.how != TerminationIdClean || term.code != 0) { - if (verbose) { - fprintf(stderr, "unable to determine libc library path: executing '%s' failed\n", cc_exe); - } - return ErrorCCompileErrors; - } - #if defined(ZIG_OS_WINDOWS) - if (buf_ends_with_str(out_stdout, "\r\n")) { - buf_resize(out_stdout, buf_len(out_stdout) - 2); - } - #else - if (buf_ends_with_str(out_stdout, "\n")) { - buf_resize(out_stdout, buf_len(out_stdout) - 1); - } - #endif - if (buf_len(out_stdout) == 0 || buf_eql_str(out_stdout, o_file)) { - return ErrorCCompilerCannotFindFile; - } - if (want_dirname) { - os_path_dirname(out_stdout, out); - } else { - buf_init_from_buf(out, out_stdout); - } - return ErrorNone; -} - -#undef CC_EXE - -#if defined(ZIG_OS_WINDOWS) || defined(ZIG_OS_LINUX) || defined(ZIG_OS_DRAGONFLY) -static Error zig_libc_find_native_crt_dir_posix(ZigLibCInstallation *self, bool verbose) { - return zig_libc_cc_print_file_name("crt1.o", &self->crt_dir, true, verbose); -} -#endif - -#if defined(ZIG_OS_WINDOWS) -static Error zig_libc_find_native_static_crt_dir_posix(ZigLibCInstallation *self, bool verbose) { - return zig_libc_cc_print_file_name("crtbegin.o", &self->static_crt_dir, true, verbose); -} - -static Error zig_libc_find_native_include_dir_windows(ZigLibCInstallation *self, ZigWindowsSDK *sdk, bool verbose) { - Error err; - if ((err = os_get_win32_ucrt_include_path(sdk, &self->include_dir))) { - if (verbose) { - fprintf(stderr, "Unable to determine libc include path: %s\n", err_str(err)); - } - return err; - } - return ErrorNone; -} - -static Error zig_libc_find_native_crt_dir_windows(ZigLibCInstallation *self, ZigWindowsSDK *sdk, ZigTarget *target, - bool verbose) -{ - Error err; - if ((err = os_get_win32_ucrt_lib_path(sdk, &self->crt_dir, target->arch))) { - if (verbose) { - fprintf(stderr, "Unable to determine ucrt path: %s\n", err_str(err)); - } - return err; - } - return ErrorNone; -} - -static Error zig_libc_find_kernel32_lib_dir(ZigLibCInstallation *self, ZigWindowsSDK *sdk, ZigTarget *target, - bool verbose) -{ - Error err; - if ((err = os_get_win32_kern32_path(sdk, &self->kernel32_lib_dir, target->arch))) { - if (verbose) { - fprintf(stderr, "Unable to determine kernel32 path: %s\n", err_str(err)); - } - return err; - } - return ErrorNone; -} - -static Error zig_libc_find_native_msvc_lib_dir(ZigLibCInstallation *self, ZigWindowsSDK *sdk, bool verbose) { - if (sdk->msvc_lib_dir_ptr == nullptr) { - if (verbose) { - fprintf(stderr, "Unable to determine vcruntime.lib path\n"); - } - return ErrorFileNotFound; - } - buf_init_from_mem(&self->msvc_lib_dir, sdk->msvc_lib_dir_ptr, sdk->msvc_lib_dir_len); - return ErrorNone; -} - -static Error zig_libc_find_native_msvc_include_dir(ZigLibCInstallation *self, ZigWindowsSDK *sdk, bool verbose) { - Error err; - if (sdk->msvc_lib_dir_ptr == nullptr) { - if (verbose) { - fprintf(stderr, "Unable to determine vcruntime.h path\n"); - } - return ErrorFileNotFound; - } - Buf search_path = BUF_INIT; - buf_init_from_mem(&search_path, sdk->msvc_lib_dir_ptr, sdk->msvc_lib_dir_len); - buf_append_str(&search_path, "..\\..\\include"); - - Buf *vcruntime_path = buf_sprintf("%s\\vcruntime.h", buf_ptr(&search_path)); - bool exists; - if ((err = os_file_exists(vcruntime_path, &exists))) { - exists = false; - } - if (exists) { - self->sys_include_dir = search_path; - return ErrorNone; - } - - if (verbose) { - fprintf(stderr, "Unable to determine vcruntime.h path\n"); - } - return ErrorFileNotFound; -} -#endif - -void zig_libc_render(ZigLibCInstallation *self, FILE *file) { - fprintf(file, - "# The directory that contains `stdlib.h`.\n" - "# On POSIX-like systems, include directories be found with: `cc -E -Wp,-v -xc /dev/null`\n" - "include_dir=%s\n" - "\n" - "# The system-specific include directory. May be the same as `include_dir`.\n" - "# On Windows it's the directory that includes `vcruntime.h`.\n" - "# On POSIX it's the directory that includes `sys/errno.h`.\n" - "sys_include_dir=%s\n" - "\n" - "# The directory that contains `crt1.o` or `crt2.o`.\n" - "# On POSIX, can be found with `cc -print-file-name=crt1.o`.\n" - "# Not needed when targeting MacOS.\n" - "crt_dir=%s\n" - "\n" - "# The directory that contains `crtbegin.o`.\n" - "# On POSIX, can be found with `cc -print-file-name=crtbegin.o`.\n" - "# Not needed when targeting MacOS.\n" - "static_crt_dir=%s\n" - "\n" - "# The directory that contains `vcruntime.lib`.\n" - "# Only needed when targeting MSVC on Windows.\n" - "msvc_lib_dir=%s\n" - "\n" - "# The directory that contains `kernel32.lib`.\n" - "# Only needed when targeting MSVC on Windows.\n" - "kernel32_lib_dir=%s\n" - "\n", - buf_ptr(&self->include_dir), - buf_ptr(&self->sys_include_dir), - buf_ptr(&self->crt_dir), - buf_ptr(&self->static_crt_dir), - buf_ptr(&self->msvc_lib_dir), - buf_ptr(&self->kernel32_lib_dir) - ); -} - -Error zig_libc_find_native(ZigLibCInstallation *self, bool verbose) { - Error err; - zig_libc_init_empty(self); -#if defined(ZIG_OS_WINDOWS) - ZigTarget native_target; - get_native_target(&native_target); - if (target_abi_is_gnu(native_target.abi)) { - if ((err = zig_libc_find_native_include_dir_posix(self, verbose))) - return err; - if ((err = zig_libc_find_native_crt_dir_posix(self, verbose))) - return err; - if ((err = zig_libc_find_native_static_crt_dir_posix(self, verbose))) - return err; - return ErrorNone; - } else { - ZigWindowsSDK *sdk; - switch (zig_find_windows_sdk(&sdk)) { - case ZigFindWindowsSdkErrorNone: - if ((err = zig_libc_find_native_msvc_include_dir(self, sdk, verbose))) - return err; - if ((err = zig_libc_find_native_msvc_lib_dir(self, sdk, verbose))) - return err; - if ((err = zig_libc_find_kernel32_lib_dir(self, sdk, &native_target, verbose))) - return err; - if ((err = zig_libc_find_native_include_dir_windows(self, sdk, verbose))) - return err; - if ((err = zig_libc_find_native_crt_dir_windows(self, sdk, &native_target, verbose))) - return err; - return ErrorNone; - case ZigFindWindowsSdkErrorOutOfMemory: - return ErrorNoMem; - case ZigFindWindowsSdkErrorNotFound: - return ErrorFileNotFound; - case ZigFindWindowsSdkErrorPathTooLong: - return ErrorPathTooLong; - } - } - zig_unreachable(); -#else - if ((err = zig_libc_find_native_include_dir_posix(self, verbose))) - return err; -#if defined(ZIG_OS_FREEBSD) || defined(ZIG_OS_NETBSD) - buf_init_from_str(&self->crt_dir, "/usr/lib"); -#elif defined(ZIG_OS_LINUX) || defined(ZIG_OS_DRAGONFLY) - if ((err = zig_libc_find_native_crt_dir_posix(self, verbose))) - return err; -#endif - return ErrorNone; -#endif -} diff --git a/src/libc_installation.hpp b/src/libc_installation.hpp deleted file mode 100644 index 8ecad7ce61..0000000000 --- a/src/libc_installation.hpp +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (c) 2019 Andrew Kelley - * - * This file is part of zig, which is MIT licensed. - * See http://opensource.org/licenses/MIT - */ - -#ifndef ZIG_LIBC_INSTALLATION_HPP -#define ZIG_LIBC_INSTALLATION_HPP - -#include - -#include "buffer.hpp" -#include "error.hpp" -#include "target.hpp" - -// Must be synchronized with zig_libc_keys -struct ZigLibCInstallation { - Buf include_dir; - Buf sys_include_dir; - Buf crt_dir; - Buf static_crt_dir; - Buf msvc_lib_dir; - Buf kernel32_lib_dir; -}; - -Error ATTRIBUTE_MUST_USE zig_libc_parse(ZigLibCInstallation *libc, Buf *libc_file, - const ZigTarget *target, bool verbose); -void zig_libc_render(ZigLibCInstallation *self, FILE *file); - -Error ATTRIBUTE_MUST_USE zig_libc_find_native(ZigLibCInstallation *self, bool verbose); - -Error zig_libc_cc_print_file_name(const char *o_file, Buf *out, bool want_dirname, bool verbose); - -#endif diff --git a/src/link.cpp b/src/link.cpp index 2771b694c1..8b31b2519e 100644 --- a/src/link.cpp +++ b/src/link.cpp @@ -1483,7 +1483,7 @@ static const char *get_libc_crt_file(CodeGen *parent, const char *file, Stage2Pr } else { assert(parent->libc != nullptr); Buf *out_buf = buf_alloc(); - os_path_join(&parent->libc->crt_dir, buf_create_from_str(file), out_buf); + os_path_join(buf_create_from_str(parent->libc->crt_dir), buf_create_from_str(file), out_buf); return buf_ptr(out_buf); } } @@ -1747,7 +1747,7 @@ static void construct_linker_job_elf(LinkJob *lj) { if (g->libc_link_lib != nullptr) { if (g->libc != nullptr) { lj->args.append("-L"); - lj->args.append(buf_ptr(&g->libc->crt_dir)); + lj->args.append(g->libc->crt_dir); } if (g->have_dynamic_link && (is_dyn_lib || g->out_type == OutTypeExe)) { @@ -2251,14 +2251,14 @@ static void construct_linker_job_coff(LinkJob *lj) { lj->args.append(buf_ptr(buf_sprintf("-OUT:%s", buf_ptr(&g->output_file_path)))); if (g->libc_link_lib != nullptr && g->libc != nullptr) { - lj->args.append(buf_ptr(buf_sprintf("-LIBPATH:%s", buf_ptr(&g->libc->crt_dir)))); + lj->args.append(buf_ptr(buf_sprintf("-LIBPATH:%s", g->libc->crt_dir))); if (target_abi_is_gnu(g->zig_target->abi)) { - lj->args.append(buf_ptr(buf_sprintf("-LIBPATH:%s", buf_ptr(&g->libc->sys_include_dir)))); - lj->args.append(buf_ptr(buf_sprintf("-LIBPATH:%s", buf_ptr(&g->libc->include_dir)))); + lj->args.append(buf_ptr(buf_sprintf("-LIBPATH:%s", g->libc->sys_include_dir))); + lj->args.append(buf_ptr(buf_sprintf("-LIBPATH:%s", g->libc->include_dir))); } else { - lj->args.append(buf_ptr(buf_sprintf("-LIBPATH:%s", buf_ptr(&g->libc->msvc_lib_dir)))); - lj->args.append(buf_ptr(buf_sprintf("-LIBPATH:%s", buf_ptr(&g->libc->kernel32_lib_dir)))); + lj->args.append(buf_ptr(buf_sprintf("-LIBPATH:%s", g->libc->msvc_lib_dir))); + lj->args.append(buf_ptr(buf_sprintf("-LIBPATH:%s", g->libc->kernel32_lib_dir))); } } diff --git a/src/main.cpp b/src/main.cpp index 2ff0ab7352..d5883d2bb3 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -14,7 +14,6 @@ #include "heap.hpp" #include "os.hpp" #include "target.hpp" -#include "libc_installation.hpp" #include "userland.h" #include "glibc.hpp" #include "dump_analysis.hpp" @@ -1027,15 +1026,22 @@ static int main0(int argc, char **argv) { switch (cmd) { case CmdLibC: { if (in_file) { - ZigLibCInstallation libc; - if ((err = zig_libc_parse(&libc, buf_create_from_str(in_file), &target, true))) + Stage2LibCInstallation libc; + if ((err = stage2_libc_parse(&libc, in_file))) { + fprintf(stderr, "unable to parse libc file: %s\n", err_str(err)); return main_exit(root_progress_node, EXIT_FAILURE); + } return main_exit(root_progress_node, EXIT_SUCCESS); } - ZigLibCInstallation libc; - if ((err = zig_libc_find_native(&libc, true))) + Stage2LibCInstallation libc; + if ((err = stage2_libc_find_native(&libc))) { + fprintf(stderr, "unable to find native libc file: %s\n", err_str(err)); return main_exit(root_progress_node, EXIT_FAILURE); - zig_libc_render(&libc, stdout); + } + if ((err = stage2_libc_render(&libc, stdout))) { + fprintf(stderr, "unable to print libc file: %s\n", err_str(err)); + return main_exit(root_progress_node, EXIT_FAILURE); + } return main_exit(root_progress_node, EXIT_SUCCESS); } case CmdBuiltin: { @@ -1125,10 +1131,10 @@ static int main0(int argc, char **argv) { if (cmd == CmdRun && buf_out_name == nullptr) { buf_out_name = buf_create_from_str("run"); } - ZigLibCInstallation *libc = nullptr; + Stage2LibCInstallation *libc = nullptr; if (libc_txt != nullptr) { - libc = heap::c_allocator.create(); - if ((err = zig_libc_parse(libc, buf_create_from_str(libc_txt), &target, true))) { + libc = heap::c_allocator.create(); + if ((err = stage2_libc_parse(libc, libc_txt))) { fprintf(stderr, "Unable to parse --libc text file: %s\n", err_str(err)); return main_exit(root_progress_node, EXIT_FAILURE); } diff --git a/src/os.cpp b/src/os.cpp index 3eeab2b755..aa09166fc5 100644 --- a/src/os.cpp +++ b/src/os.cpp @@ -1551,108 +1551,6 @@ void os_stderr_set_color(TermColor color) { #endif } -Error os_get_win32_ucrt_lib_path(ZigWindowsSDK *sdk, Buf* output_buf, ZigLLVM_ArchType platform_type) { -#if defined(ZIG_OS_WINDOWS) - buf_resize(output_buf, 0); - buf_appendf(output_buf, "%sLib\\%s\\ucrt\\", sdk->path10_ptr, sdk->version10_ptr); - switch (platform_type) { - case ZigLLVM_x86: - buf_append_str(output_buf, "x86\\"); - break; - case ZigLLVM_x86_64: - buf_append_str(output_buf, "x64\\"); - break; - case ZigLLVM_arm: - buf_append_str(output_buf, "arm\\"); - break; - default: - zig_panic("Attempted to use vcruntime for non-supported platform."); - } - Buf* tmp_buf = buf_alloc(); - buf_init_from_buf(tmp_buf, output_buf); - buf_append_str(tmp_buf, "ucrt.lib"); - if (GetFileAttributesA(buf_ptr(tmp_buf)) != INVALID_FILE_ATTRIBUTES) { - return ErrorNone; - } - else { - buf_resize(output_buf, 0); - return ErrorFileNotFound; - } -#else - return ErrorFileNotFound; -#endif -} - -Error os_get_win32_ucrt_include_path(ZigWindowsSDK *sdk, Buf* output_buf) { -#if defined(ZIG_OS_WINDOWS) - buf_resize(output_buf, 0); - buf_appendf(output_buf, "%sInclude\\%s\\ucrt", sdk->path10_ptr, sdk->version10_ptr); - if (GetFileAttributesA(buf_ptr(output_buf)) != INVALID_FILE_ATTRIBUTES) { - return ErrorNone; - } - else { - buf_resize(output_buf, 0); - return ErrorFileNotFound; - } -#else - return ErrorFileNotFound; -#endif -} - -Error os_get_win32_kern32_path(ZigWindowsSDK *sdk, Buf* output_buf, ZigLLVM_ArchType platform_type) { -#if defined(ZIG_OS_WINDOWS) - { - buf_resize(output_buf, 0); - buf_appendf(output_buf, "%sLib\\%s\\um\\", sdk->path10_ptr, sdk->version10_ptr); - switch (platform_type) { - case ZigLLVM_x86: - buf_append_str(output_buf, "x86\\"); - break; - case ZigLLVM_x86_64: - buf_append_str(output_buf, "x64\\"); - break; - case ZigLLVM_arm: - buf_append_str(output_buf, "arm\\"); - break; - default: - zig_panic("Attempted to use vcruntime for non-supported platform."); - } - Buf* tmp_buf = buf_alloc(); - buf_init_from_buf(tmp_buf, output_buf); - buf_append_str(tmp_buf, "kernel32.lib"); - if (GetFileAttributesA(buf_ptr(tmp_buf)) != INVALID_FILE_ATTRIBUTES) { - return ErrorNone; - } - } - { - buf_resize(output_buf, 0); - buf_appendf(output_buf, "%sLib\\%s\\um\\", sdk->path81_ptr, sdk->version81_ptr); - switch (platform_type) { - case ZigLLVM_x86: - buf_append_str(output_buf, "x86\\"); - break; - case ZigLLVM_x86_64: - buf_append_str(output_buf, "x64\\"); - break; - case ZigLLVM_arm: - buf_append_str(output_buf, "arm\\"); - break; - default: - zig_panic("Attempted to use vcruntime for non-supported platform."); - } - Buf* tmp_buf = buf_alloc(); - buf_init_from_buf(tmp_buf, output_buf); - buf_append_str(tmp_buf, "kernel32.lib"); - if (GetFileAttributesA(buf_ptr(tmp_buf)) != INVALID_FILE_ATTRIBUTES) { - return ErrorNone; - } - } - return ErrorFileNotFound; -#else - return ErrorFileNotFound; -#endif -} - #if defined(ZIG_OS_WINDOWS) // Ported from std/unicode.zig struct Utf16LeIterator { diff --git a/src/os.hpp b/src/os.hpp index 7da846a509..6823d901b5 100644 --- a/src/os.hpp +++ b/src/os.hpp @@ -152,10 +152,6 @@ Error ATTRIBUTE_MUST_USE os_self_exe_path(Buf *out_path); Error ATTRIBUTE_MUST_USE os_get_app_data_dir(Buf *out_path, const char *appname); -Error ATTRIBUTE_MUST_USE os_get_win32_ucrt_include_path(ZigWindowsSDK *sdk, Buf *output_buf); -Error ATTRIBUTE_MUST_USE os_get_win32_ucrt_lib_path(ZigWindowsSDK *sdk, Buf *output_buf, ZigLLVM_ArchType platform_type); -Error ATTRIBUTE_MUST_USE os_get_win32_kern32_path(ZigWindowsSDK *sdk, Buf *output_buf, ZigLLVM_ArchType platform_type); - Error ATTRIBUTE_MUST_USE os_self_exe_shared_libs(ZigList &paths); #endif diff --git a/src/userland.cpp b/src/userland.cpp index ba0e906cf9..539735f436 100644 --- a/src/userland.cpp +++ b/src/userland.cpp @@ -144,3 +144,25 @@ int stage2_cmd_targets(const char *zig_triple) { const char *msg = "stage0 called stage2_cmd_targets"; stage2_panic(msg, strlen(msg)); } + +enum Error stage2_libc_parse(struct Stage2LibCInstallation *libc, const char *libc_file) { + const char *msg = "stage0 called stage2_libc_parse"; + stage2_panic(msg, strlen(msg)); +} + +enum Error stage2_libc_render(struct Stage2LibCInstallation *self, FILE *file) { + const char *msg = "stage0 called stage2_libc_render"; + stage2_panic(msg, strlen(msg)); +} + +enum Error stage2_libc_find_native(struct Stage2LibCInstallation *libc) { + const char *msg = "stage0 called stage2_libc_find_native"; + stage2_panic(msg, strlen(msg)); +} + +enum Error stage2_libc_cc_print_file_name(char **out_ptr, size_t *out_len, + const char *o_file, bool want_dirname) +{ + const char *msg = "stage0 called stage2_libc_cc_print_file_name"; + stage2_panic(msg, strlen(msg)); +} diff --git a/src/userland.h b/src/userland.h index a59f6b7da8..7903dc8fe4 100644 --- a/src/userland.h +++ b/src/userland.h @@ -85,6 +85,20 @@ enum Error { ErrorInvalidLlvmCpuFeaturesFormat, ErrorUnknownApplicationBinaryInterface, ErrorASTUnitFailure, + ErrorBadPathName, + ErrorSymLinkLoop, + ErrorProcessFdQuotaExceeded, + ErrorSystemFdQuotaExceeded, + ErrorNoDevice, + ErrorDeviceBusy, + ErrorUnableToSpawnCCompiler, + ErrorCCompilerExitCode, + ErrorCCompilerCrashed, + ErrorCCompilerCannotFindHeaders, + ErrorLibCRuntimeNotFound, + ErrorLibCStdLibHeaderNotFound, + ErrorLibCKernel32LibNotFound, + ErrorUnsupportedArchitecture, }; // ABI warning @@ -185,7 +199,7 @@ ZIG_EXTERN_C void stage2_progress_update_node(Stage2ProgressNode *node, struct Stage2CpuFeatures; // ABI warning -ZIG_EXTERN_C Error stage2_cpu_features_parse(struct Stage2CpuFeatures **result, +ZIG_EXTERN_C enum Error stage2_cpu_features_parse(struct Stage2CpuFeatures **result, const char *zig_triple, const char *cpu_name, const char *cpu_features); // ABI warning @@ -205,5 +219,30 @@ ZIG_EXTERN_C void stage2_cpu_features_get_cache_hash(const struct Stage2CpuFeatu // ABI warning ZIG_EXTERN_C int stage2_cmd_targets(const char *zig_triple); +// ABI warning +struct Stage2LibCInstallation { + const char *include_dir; + size_t include_dir_len; + const char *sys_include_dir; + size_t sys_include_dir_len; + const char *crt_dir; + size_t crt_dir_len; + const char *static_crt_dir; + size_t static_crt_dir_len; + const char *msvc_lib_dir; + size_t msvc_lib_dir_len; + const char *kernel32_lib_dir; + size_t kernel32_lib_dir_len; +}; + +// ABI warning +ZIG_EXTERN_C enum Error stage2_libc_parse(struct Stage2LibCInstallation *libc, const char *libc_file); +// ABI warning +ZIG_EXTERN_C enum Error stage2_libc_render(struct Stage2LibCInstallation *self, FILE *file); +// ABI warning +ZIG_EXTERN_C enum Error stage2_libc_find_native(struct Stage2LibCInstallation *libc); +// ABI warning +ZIG_EXTERN_C enum Error stage2_libc_cc_print_file_name(char **out_ptr, size_t *out_len, + const char *o_file, bool want_dirname); #endif diff --git a/src/windows_sdk.h b/src/windows_sdk.h index 2d531ad372..e887209559 100644 --- a/src/windows_sdk.h +++ b/src/windows_sdk.h @@ -16,6 +16,7 @@ #include +// ABI warning - src-self-hosted/windows_sdk.zig struct ZigWindowsSDK { const char *path10_ptr; size_t path10_len; @@ -33,6 +34,7 @@ struct ZigWindowsSDK { size_t msvc_lib_dir_len; }; +// ABI warning - src-self-hosted/windows_sdk.zig enum ZigFindWindowsSdkError { ZigFindWindowsSdkErrorNone, ZigFindWindowsSdkErrorOutOfMemory, @@ -40,8 +42,10 @@ enum ZigFindWindowsSdkError { ZigFindWindowsSdkErrorPathTooLong, }; +// ABI warning - src-self-hosted/windows_sdk.zig ZIG_EXTERN_C enum ZigFindWindowsSdkError zig_find_windows_sdk(struct ZigWindowsSDK **out_sdk); +// ABI warning - src-self-hosted/windows_sdk.zig ZIG_EXTERN_C void zig_free_windows_sdk(struct ZigWindowsSDK *sdk); #endif