diff --git a/CMakeLists.txt b/CMakeLists.txt index 71d4e2cda3c0f841316c69f875367e4cff9f2615..ab4607d2309f6ec636309f617bad8a7e0397f84c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -434,8 +434,8 @@ find_package(Threads) # CMake doesn't let us create an empty executable, so we hang on to this one separately. set(ZIG_MAIN_SRC "${CMAKE_SOURCE_DIR}/src/main.cpp") -# This is our shim which will be replaced by libuserland written in Zig. -set(ZIG0_SHIM_SRC "${CMAKE_SOURCE_DIR}/src/userland.cpp") +# This is our shim which will be replaced by libstage2 written in Zig. +set(ZIG0_SHIM_SRC "${CMAKE_SOURCE_DIR}/src/stage2.cpp") if(ZIG_ENABLE_MEM_PROFILE) set(ZIG_SOURCES_MEM_PROFILE "${CMAKE_SOURCE_DIR}/src/mem_profile.cpp") @@ -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" @@ -566,12 +565,12 @@ set_target_properties(opt_c_util PROPERTIES COMPILE_FLAGS "${OPTIMIZED_C_FLAGS}" ) -add_library(compiler STATIC ${ZIG_SOURCES}) -set_target_properties(compiler PROPERTIES +add_library(zigcompiler STATIC ${ZIG_SOURCES}) +set_target_properties(zigcompiler PROPERTIES COMPILE_FLAGS ${EXE_CFLAGS} LINK_FLAGS ${EXE_LDFLAGS} ) -target_link_libraries(compiler LINK_PUBLIC +target_link_libraries(zigcompiler LINK_PUBLIC zig_cpp opt_c_util ${SOFTFLOAT_LIBRARIES} @@ -581,15 +580,15 @@ target_link_libraries(compiler LINK_PUBLIC ${CMAKE_THREAD_LIBS_INIT} ) if(NOT MSVC) - target_link_libraries(compiler LINK_PUBLIC ${LIBXML2}) + target_link_libraries(zigcompiler LINK_PUBLIC ${LIBXML2}) endif() if(ZIG_DIA_GUIDS_LIB) - target_link_libraries(compiler LINK_PUBLIC ${ZIG_DIA_GUIDS_LIB}) + target_link_libraries(zigcompiler LINK_PUBLIC ${ZIG_DIA_GUIDS_LIB}) endif() if(MSVC OR MINGW) - target_link_libraries(compiler LINK_PUBLIC version) + target_link_libraries(zigcompiler LINK_PUBLIC version) endif() add_executable(zig0 "${ZIG_MAIN_SRC}" "${ZIG0_SHIM_SRC}") @@ -597,40 +596,42 @@ set_target_properties(zig0 PROPERTIES COMPILE_FLAGS ${EXE_CFLAGS} LINK_FLAGS ${EXE_LDFLAGS} ) -target_link_libraries(zig0 compiler) +target_link_libraries(zig0 zigcompiler) if(MSVC) - set(LIBUSERLAND "${CMAKE_BINARY_DIR}/userland.lib") + set(LIBSTAGE2 "${CMAKE_BINARY_DIR}/zigstage2.lib") else() - set(LIBUSERLAND "${CMAKE_BINARY_DIR}/libuserland.a") + set(LIBSTAGE2 "${CMAKE_BINARY_DIR}/libzigstage2.a") endif() if("${CMAKE_BUILD_TYPE}" STREQUAL "Debug") - set(LIBUSERLAND_RELEASE_MODE "false") + set(LIBSTAGE2_RELEASE_ARG "") else() - set(LIBUSERLAND_RELEASE_MODE "true") + set(LIBSTAGE2_RELEASE_ARG --release-fast --strip) +endif() +if(WIN32) + set(LIBSTAGE2_WINDOWS_ARGS "-lntdll") +else() + set(LIBSTAGE2_WINDOWS_ARGS "") endif() -set(BUILD_LIBUSERLAND_ARGS "build" +set(BUILD_LIBSTAGE2_ARGS "build-lib" + "src-self-hosted/stage2.zig" + --name zigstage2 --override-lib-dir "${CMAKE_SOURCE_DIR}/lib" - "-Doutput-dir=${CMAKE_BINARY_DIR}" - "-Drelease=${LIBUSERLAND_RELEASE_MODE}" - "-Dlib-files-only" - --prefix "${CMAKE_INSTALL_PREFIX}" - libuserland + --cache on + --output-dir "${CMAKE_BINARY_DIR}" + ${LIBSTAGE2_RELEASE_ARG} + --disable-gen-h + --bundle-compiler-rt + -fPIC + -lc + ${LIBSTAGE2_WINDOWS_ARGS} ) -# When using Visual Studio build system generator we default to libuserland install. -if(MSVC) - set(ZIG_SKIP_INSTALL_LIB_FILES off CACHE BOOL "Disable copying lib/ files to install prefix") - if(NOT ZIG_SKIP_INSTALL_LIB_FILES) - set(BUILD_LIBUSERLAND_ARGS ${BUILD_LIBUSERLAND_ARGS} install) - endif() -endif() - -add_custom_target(zig_build_libuserland ALL - COMMAND zig0 ${BUILD_LIBUSERLAND_ARGS} +add_custom_target(zig_build_libstage2 ALL + COMMAND zig0 ${BUILD_LIBSTAGE2_ARGS} DEPENDS zig0 - BYPRODUCTS "${LIBUSERLAND}" + BYPRODUCTS "${LIBSTAGE2}" WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}" ) add_executable(zig "${ZIG_MAIN_SRC}") @@ -639,22 +640,40 @@ set_target_properties(zig PROPERTIES COMPILE_FLAGS ${EXE_CFLAGS} LINK_FLAGS ${EXE_LDFLAGS} ) -target_link_libraries(zig compiler "${LIBUSERLAND}") +target_link_libraries(zig zigcompiler "${LIBSTAGE2}") if(MSVC) target_link_libraries(zig ntdll.lib) elseif(MINGW) target_link_libraries(zig ntdll) endif() -add_dependencies(zig zig_build_libuserland) +add_dependencies(zig zig_build_libstage2) install(TARGETS zig DESTINATION bin) -# CODE has no effect with Visual Studio build system generator. -if(NOT MSVC) - get_target_property(zig0_BINARY_DIR zig0 BINARY_DIR) - install(CODE "set(zig0_EXE \"${zig0_BINARY_DIR}/zig0\")") - install(CODE "set(INSTALL_LIBUSERLAND_ARGS \"${BUILD_LIBUSERLAND_ARGS}\" install)") - install(CODE "set(BUILD_LIBUSERLAND_ARGS \"${BUILD_LIBUSERLAND_ARGS}\")") +set(ZIG_INSTALL_ARGS "build" + --override-lib-dir "${CMAKE_SOURCE_DIR}/lib" + "-Dlib-files-only" + --prefix "${CMAKE_INSTALL_PREFIX}" + install +) + +# CODE has no effect with Visual Studio build system generator, therefore +# when using Visual Studio build system generator we resort to running +# `zig build install` during the build phase. +if(MSVC) + set(ZIG_SKIP_INSTALL_LIB_FILES off CACHE BOOL + "Windows-only: Disable copying lib/ files to install prefix during the build phase") + if(NOT ZIG_SKIP_INSTALL_LIB_FILES) + add_custom_target(zig_install_lib_files ALL + COMMAND zig ${ZIG_INSTALL_ARGS} + DEPENDS zig + WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}" + ) + endif() +else() + get_target_property(zig_BINARY_DIR zig BINARY_DIR) + install(CODE "set(zig_EXE \"${zig_BINARY_DIR}/zig\")") + install(CODE "set(ZIG_INSTALL_ARGS \"${ZIG_INSTALL_ARGS}\")") install(CODE "set(CMAKE_SOURCE_DIR \"${CMAKE_SOURCE_DIR}\")") install(SCRIPT ${CMAKE_CURRENT_SOURCE_DIR}/cmake/install.cmake) endif() diff --git a/build.zig b/build.zig index 673fbfab7d6651c8b68baaeb6af4eac9311bcf0b..e1ae5cf3cdd5873df3ae40423b9b8f63708d8ddb 100644 --- a/build.zig +++ b/build.zig @@ -64,8 +64,6 @@ pub fn build(b: *Builder) !void { try configureStage2(b, test_stage2, ctx); try configureStage2(b, exe, ctx); - addLibUserlandStep(b, mode); - const skip_release = b.option(bool, "skip-release", "Main test suite skips release builds") orelse false; const skip_release_small = b.option(bool, "skip-release-small", "Main test suite skips release-small builds") orelse skip_release; const skip_release_fast = b.option(bool, "skip-release-fast", "Main test suite skips release-fast builds") orelse skip_release; @@ -175,7 +173,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, }; @@ -366,28 +364,3 @@ const Context = struct { dia_guids_lib: []const u8, llvm: LibraryDep, }; - -fn addLibUserlandStep(b: *Builder, mode: builtin.Mode) void { - const artifact = b.addStaticLibrary("userland", "src-self-hosted/stage1.zig"); - artifact.disable_gen_h = true; - artifact.bundle_compiler_rt = true; - artifact.setTarget(builtin.arch, builtin.os, builtin.abi); - artifact.setBuildMode(mode); - artifact.force_pic = true; - if (mode != .Debug) { - artifact.strip = true; - } - artifact.linkSystemLibrary("c"); - if (builtin.os == .windows) { - artifact.linkSystemLibrary("ntdll"); - } - const libuserland_step = b.step("libuserland", "Build the userland compiler library for use in stage1"); - libuserland_step.dependOn(&artifact.step); - - const output_dir = b.option( - []const u8, - "output-dir", - "For libuserland step, where to put the output", - ) orelse return; - artifact.setOutputDir(output_dir); -} diff --git a/cmake/install.cmake b/cmake/install.cmake index 415a088d6a21474f0105bf5d84af1385cfd93a86..386773e30cb67fb6912e8e115cc3d52b5c75e032 100644 --- a/cmake/install.cmake +++ b/cmake/install.cmake @@ -1,16 +1,16 @@ message("-- Installing: ${CMAKE_INSTALL_PREFIX}/lib") -if(NOT EXISTS ${zig0_EXE}) +if(NOT EXISTS ${zig_EXE}) message("::") message(":: ERROR: Executable not found") message(":: (execute_process)") message("::") - message(":: executable: ${zig0_EXE}") + message(":: executable: ${zig_EXE}") message("::") message(FATAL_ERROR) endif() -execute_process(COMMAND ${zig0_EXE} ${INSTALL_LIBUSERLAND_ARGS} +execute_process(COMMAND ${zig_EXE} ${ZIG_INSTALL_ARGS} WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} RESULT_VARIABLE _result ) @@ -19,11 +19,11 @@ if(_result) message(":: ERROR: ${_result}") message(":: (execute_process)") - string(REPLACE ";" " " s_INSTALL_LIBUSERLAND_ARGS "${INSTALL_LIBUSERLAND_ARGS}") + string(REPLACE ";" " " s_INSTALL_LIBSTAGE2_ARGS "${ZIG_INSTALL_ARGS}") message("::") - message(":: argv: ${zig0_EXE} ${s_INSTALL_LIBUSERLAND_ARGS} install") + message(":: argv: ${zig_EXE} ${s_INSTALL_LIBSTAGE2_ARGS}") - set(_args ${zig0_EXE} ${INSTALL_LIBUSERLAND_ARGS}) + set(_args ${zig_EXE} ${ZIG_INSTALL_ARGS}) list(LENGTH _args _len) math(EXPR _len "${_len} - 1") message("::") diff --git a/lib/std/c.zig b/lib/std/c.zig index e0c84beb78e260af9badf0bcd3f90fa767f9dd8a..93c6c246641f6b5183f14fb450ed6613e31a40cd 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 fd67e3a6804f7cb61d00cf48a8f418d9387bbbfa..171a330bac4edb609e142e6c493509b01aeef01e 100644 --- a/lib/std/child_process.zig +++ b/lib/std/child_process.zig @@ -48,7 +48,10 @@ 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, + + expand_arg0: Arg0Expand, + + pub const Arg0Expand = os.Arg0Expand; pub const SpawnError = error{ OutOfMemory, @@ -90,7 +93,6 @@ pub const ChildProcess = struct { .handle = undefined, .thread_handle = undefined, .err_pipe = undefined, - .llnode = undefined, .term = null, .env_map = null, .cwd = null, @@ -102,6 +104,7 @@ pub const ChildProcess = struct { .stdin_behavior = StdIo.Inherit, .stdout_behavior = StdIo.Inherit, .stderr_behavior = StdIo.Inherit, + .expand_arg0 = .no_expand, }; errdefer allocator.destroy(child); return child; @@ -174,34 +177,56 @@ pub const ChildProcess = struct { /// Spawns a child process, waits for it, collecting stdout and stderr, and then returns. /// If it succeeds, the caller owns result.stdout and result.stderr memory. + /// TODO deprecate in favor of exec2 pub fn exec( allocator: *mem.Allocator, argv: []const []const u8, cwd: ?[]const u8, env_map: ?*const BufMap, - max_output_size: usize, + max_output_bytes: usize, ) !ExecResult { - const child = try ChildProcess.init(argv, allocator); + return exec2(.{ + .allocator = allocator, + .argv = argv, + .cwd = cwd, + .env_map = env_map, + .max_output_bytes = max_output_bytes, + }); + } + + /// Spawns a child process, waits for it, collecting stdout and stderr, and then returns. + /// If it succeeds, the caller owns result.stdout and result.stderr memory. + /// TODO rename to exec + pub fn exec2(args: struct { + allocator: *mem.Allocator, + argv: []const []const u8, + cwd: ?[]const u8 = null, + env_map: ?*const BufMap = null, + max_output_bytes: usize = 50 * 1024, + expand_arg0: Arg0Expand = .no_expand, + }) !ExecResult { + const child = try ChildProcess.init(args.argv, args.allocator); defer child.deinit(); - child.stdin_behavior = ChildProcess.StdIo.Ignore; - child.stdout_behavior = ChildProcess.StdIo.Pipe; - child.stderr_behavior = ChildProcess.StdIo.Pipe; - child.cwd = cwd; - child.env_map = env_map; + child.stdin_behavior = .Ignore; + child.stdout_behavior = .Pipe; + child.stderr_behavior = .Pipe; + child.cwd = args.cwd; + child.env_map = args.env_map; + child.expand_arg0 = args.expand_arg0; try child.spawn(); - var stdout = Buffer.initNull(allocator); - var stderr = Buffer.initNull(allocator); + var stdout = Buffer.initNull(args.allocator); + var stderr = Buffer.initNull(args.allocator); defer Buffer.deinit(&stdout); defer Buffer.deinit(&stderr); var stdout_file_in_stream = child.stdout.?.inStream(); var stderr_file_in_stream = child.stderr.?.inStream(); - try stdout_file_in_stream.stream.readAllBuffer(&stdout, max_output_size); - try stderr_file_in_stream.stream.readAllBuffer(&stderr, max_output_size); + try stdout_file_in_stream.stream.readAllBuffer(&stdout, args.max_output_bytes); + try stderr_file_in_stream.stream.readAllBuffer(&stderr, args.max_output_bytes); return ExecResult{ .term = try child.wait(), @@ -420,7 +445,7 @@ pub const ChildProcess = struct { os.setreuid(uid, uid) catch |err| forkChildErrReport(err_pipe[1], err); } - const err = os.execvpe(self.allocator, self.argv, env_map); + const err = os.execvpe_expandArg0(self.allocator, self.expand_arg0, self.argv, env_map); forkChildErrReport(err_pipe[1], err); } @@ -453,7 +478,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 64d73a25e10f1badf8296d0c853b582aa1ffba57..d042e2ad9b3247102bb7c9a2ba62e16646df4463 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 0000000000000000000000000000000000000000..af59b324905d1cc4e46d4ec6358e4c037e135e00 --- /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, .auto_async).init(); + batch.add(&async sleepALittle(&count)); + batch.add(&async increaseByTen(&count)); + batch.wait(); + testing.expect(count == 11); + + var another = Batch(anyerror!void, 2, .auto_async).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 98ebdbd1f86daf6d0090acc14c43c9e61b68c7a9..ac1bf68245071cf2382f07473324c519701f76c3 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 a39f283e55a49cf4a2eabc7889f07251473d7cad..a00ba6a63f4654bc7d4dfae7c3c7b8841256579d 100644 --- a/lib/std/fs.zig +++ b/lib/std/fs.zig @@ -96,7 +96,6 @@ pub fn updateFile(source_path: []const u8, dest_path: []const u8) !PrevStatus { /// atime, and mode of the source file so that the next call to `updateFile` will not need a copy. /// Returns the previous status of the file before updating. /// If any of the directories do not exist for dest_path, they are created. -/// TODO https://github.com/ziglang/zig/issues/2885 pub fn updateFileMode(source_path: []const u8, dest_path: []const u8, mode: ?File.Mode) !PrevStatus { const my_cwd = cwd(); @@ -818,6 +817,13 @@ pub const Dir = struct { ) File.OpenError!File { const w = os.windows; + if (sub_path_w[0] == '.' and sub_path_w[1] == 0) { + return error.IsDir; + } + if (sub_path_w[0] == '.' and sub_path_w[1] == '.' and sub_path_w[2] == 0) { + return error.IsDir; + } + var result = File{ .handle = undefined, .io_mode = .blocking, @@ -839,12 +845,6 @@ pub const Dir = struct { .SecurityDescriptor = null, .SecurityQualityOfService = null, }; - if (sub_path_w[0] == '.' and sub_path_w[1] == 0) { - return error.IsDir; - } - if (sub_path_w[0] == '.' and sub_path_w[1] == '.' and sub_path_w[2] == 0) { - return error.IsDir; - } var io: w.IO_STATUS_BLOCK = undefined; const rc = w.ntdll.NtCreateFile( &result.handle, @@ -1323,6 +1323,50 @@ 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 { + if (builtin.os == .windows) { + const sub_path_w = try os.windows.sliceToPrefixedFileW(sub_path); + return self.accessW(&sub_path_w, flags); + } + 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 { + if (builtin.os == .windows) { + const sub_path_w = try os.windows.cStrToPrefixedFileW(sub_path); + return self.accessW(&sub_path_w, flags); + } + 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 asserts the target OS is Windows and the path parameter is + /// * WTF-16 encoded + /// * null-terminated + /// * NtDll prefixed + /// TODO currently this ignores `flags`. + pub fn accessW(self: Dir, sub_path_w: [*:0]const u16, flags: File.OpenFlags) AccessError!void { + return os.faccessatW(self.fd, sub_path_w, 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 92dcfefad056234212812565e760f6aa6ef38e76..a3a428e77bdf4d07b70d962cd3c0923749267fbf 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 a1b4a3c8097fa34363655400c3288093777939a1..71be738defc398f5e4d19770f9234a98fc3b7eb0 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 73b6cbb302c05f30bd638959ad3fc87f2dd6322b..0f492a25f288afbe4f7994e43888b8547df7d2c7 100644 --- a/lib/std/os.zig +++ b/lib/std/os.zig @@ -916,10 +916,13 @@ pub const ExecveError = error{ NameTooLong, } || UnexpectedError; +/// Deprecated in favor of `execveZ`. +pub const execveC = execveZ; + /// Like `execve` except the parameters are null-terminated, /// matching the syscall API on all targets. This removes the need for an allocator. -/// This function ignores PATH environment variable. See `execvpeC` for that. -pub fn execveC(path: [*:0]const u8, child_argv: [*:null]const ?[*:0]const u8, envp: [*:null]const ?[*:0]const u8) ExecveError { +/// This function ignores PATH environment variable. See `execvpeZ` for that. +pub fn execveZ(path: [*:0]const u8, child_argv: [*:null]const ?[*:0]const u8, envp: [*:null]const ?[*:0]const u8) ExecveError { switch (errno(system.execve(path, child_argv, envp))) { 0 => unreachable, EFAULT => unreachable, @@ -942,15 +945,29 @@ pub fn execveC(path: [*:0]const u8, child_argv: [*:null]const ?[*:0]const u8, en } } -/// Like `execvpe` except the parameters are null-terminated, -/// matching the syscall API on all targets. This removes the need for an allocator. -/// This function also uses the PATH environment variable to get the full path to the executable. -/// If `file` is an absolute path, this is the same as `execveC`. -pub fn execvpeC(file: [*:0]const u8, child_argv: [*:null]const ?[*:0]const u8, envp: [*:null]const ?[*:0]const u8) ExecveError { +/// Deprecated in favor of `execvpeZ`. +pub const execvpeC = execvpeZ; + +pub const Arg0Expand = enum { + expand, + no_expand, +}; + +/// Like `execvpeZ` except if `arg0_expand` is `.expand`, then `argv` is mutable, +/// and `argv[0]` is expanded to be the same absolute path that is passed to the execve syscall. +pub fn execvpeZ_expandArg0( + comptime arg0_expand: Arg0Expand, + file: [*:0]const u8, + child_argv: switch (arg0_expand) { + .expand => [*:null]?[*:0]const u8, + .no_expand => [*:null]const ?[*:0]const u8, + }, + envp: [*:null]const ?[*:0]const u8, +) ExecveError { 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; @@ -962,7 +979,12 @@ pub fn execvpeC(file: [*:0]const u8, child_argv: [*:null]const ?[*:0]const u8, e mem.copy(u8, path_buf[search_path.len + 1 ..], file_slice); const path_len = search_path.len + file_slice.len + 1; path_buf[path_len] = 0; - err = execveC(path_buf[0..path_len :0].ptr, child_argv, envp); + const full_path = path_buf[0..path_len :0].ptr; + switch (arg0_expand) { + .expand => child_argv[0] = full_path, + .no_expand => {}, + } + err = execveC(full_path, child_argv, envp); switch (err) { error.AccessDenied => seen_eacces = true, error.FileNotFound, error.NotDir => {}, @@ -973,13 +995,24 @@ pub fn execvpeC(file: [*:0]const u8, child_argv: [*:null]const ?[*:0]const u8, e return err; } -/// This function must allocate memory to add a null terminating bytes on path and each arg. -/// It must also convert to KEY=VALUE\0 format for environment variables, and include null -/// pointers after the args and after the environment variables. -/// `argv_slice[0]` is the executable path. +/// Like `execvpe` except the parameters are null-terminated, +/// matching the syscall API on all targets. This removes the need for an allocator. /// This function also uses the PATH environment variable to get the full path to the executable. -pub fn execvpe( +/// If `file` is an absolute path, this is the same as `execveC`. +pub fn execvpeZ( + file: [*:0]const u8, + argv: [*:null]const ?[*:0]const u8, + envp: [*:null]const ?[*:0]const u8, +) ExecveError { + return execvpeZ_expandArg0(.no_expand, file, argv, envp); +} + +/// This is the same as `execvpe` except if the `arg0_expand` parameter is set to `.expand`, +/// then argv[0] will be replaced with the expanded version of it, after resolving in accordance +/// with the PATH environment variable. +pub fn execvpe_expandArg0( allocator: *mem.Allocator, + arg0_expand: Arg0Expand, argv_slice: []const []const u8, env_map: *const std.BufMap, ) (ExecveError || error{OutOfMemory}) { @@ -1004,7 +1037,23 @@ pub fn execvpe( const envp_buf = try createNullDelimitedEnvMap(allocator, env_map); defer freeNullDelimitedEnvMap(allocator, envp_buf); - return execvpeC(argv_buf.ptr[0].?, argv_ptr, envp_buf.ptr); + switch (arg0_expand) { + .expand => return execvpeZ_expandArg0(.expand, argv_buf.ptr[0].?, argv_ptr, envp_buf.ptr), + .no_expand => return execvpeZ_expandArg0(.no_expand, argv_buf.ptr[0].?, argv_ptr, envp_buf.ptr), + } +} + +/// This function must allocate memory to add a null terminating bytes on path and each arg. +/// It must also convert to KEY=VALUE\0 format for environment variables, and include null +/// pointers after the args and after the environment variables. +/// `argv_slice[0]` is the executable path. +/// This function also uses the PATH environment variable to get the full path to the executable. +pub fn execvpe( + allocator: *mem.Allocator, + argv_slice: []const []const u8, + env_map: *const std.BufMap, +) (ExecveError || error{OutOfMemory}) { + return execvpe_expandArg0(allocator, .no_expand, argv_slice, env_map); } pub fn createNullDelimitedEnvMap(allocator: *mem.Allocator, env_map: *const std.BufMap) ![:null]?[*:0]u8 { @@ -1038,7 +1087,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 +1105,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 +2504,9 @@ pub const AccessError = error{ InputOutput, SystemResources, BadPathName, + FileBusy, + SymLinkLoop, + ReadOnlyFileSystem, /// On Windows, file paths must be valid Unicode. InvalidUtf8, @@ -2469,8 +2524,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 +2537,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 +2567,79 @@ 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 NtDll-prefixed, null-terminated, WTF-16 encoded. +/// TODO currently this ignores `mode` and `flags` +pub fn faccessatW(dirfd: fd_t, sub_path_w: [*:0]const u16, mode: u32, flags: u32) AccessError!void { + if (sub_path_w[0] == '.' and sub_path_w[1] == 0) { + return; + } + if (sub_path_w[0] == '.' and sub_path_w[1] == '.' and sub_path_w[2] == 0) { + return; + } + + const path_len_bytes = math.cast(u16, mem.toSliceConst(u16, sub_path_w).len * 2) catch |err| switch (err) { + error.Overflow => return error.NameTooLong, + }; + var nt_name = windows.UNICODE_STRING{ + .Length = path_len_bytes, + .MaximumLength = path_len_bytes, + .Buffer = @intToPtr([*]u16, @ptrToInt(sub_path_w)), + }; + var attr = windows.OBJECT_ATTRIBUTES{ + .Length = @sizeOf(windows.OBJECT_ATTRIBUTES), + .RootDirectory = if (std.fs.path.isAbsoluteWindowsW(sub_path_w)) null else dirfd, + .Attributes = 0, // Note we do not use OBJ_CASE_INSENSITIVE here. + .ObjectName = &nt_name, + .SecurityDescriptor = null, + .SecurityQualityOfService = null, + }; + var basic_info: windows.FILE_BASIC_INFORMATION = undefined; + switch (windows.ntdll.NtQueryAttributesFile(&attr, &basic_info)) { + .SUCCESS => return, + .OBJECT_NAME_NOT_FOUND => return error.FileNotFound, + .OBJECT_PATH_NOT_FOUND => return error.FileNotFound, + .INVALID_PARAMETER => unreachable, + .ACCESS_DENIED => return error.PermissionDenied, + .OBJECT_PATH_SYNTAX_BAD => unreachable, + else => |rc| return windows.unexpectedStatus(rc), + } +} + pub const PipeError = error{ SystemFdQuotaExceeded, ProcessFdQuotaExceeded, @@ -2844,18 +2974,26 @@ pub fn nanosleep(seconds: u64, nanoseconds: u64) void { } pub fn dl_iterate_phdr( - comptime T: type, - callback: extern fn (info: *dl_phdr_info, size: usize, data: ?*T) i32, - data: ?*T, -) isize { + context: var, + comptime Error: type, + comptime callback: fn (info: *dl_phdr_info, size: usize, context: @TypeOf(context)) Error!void, +) Error!void { + const Context = @TypeOf(context); + if (builtin.object_format != .elf) @compileError("dl_iterate_phdr is not available for this target"); if (builtin.link_libc) { - return system.dl_iterate_phdr( - @ptrCast(std.c.dl_iterate_phdr_callback, callback), - @ptrCast(?*c_void, data), - ); + switch (system.dl_iterate_phdr(struct { + fn callbackC(info: *dl_phdr_info, size: usize, data: ?*c_void) callconv(.C) c_int { + const context_ptr = @ptrCast(*const Context, @alignCast(@alignOf(*const Context), data)); + callback(info, size, context_ptr.*) catch |err| return @errorToInt(err); + return 0; + } + }.callbackC, @intToPtr(?*c_void, @ptrToInt(&context)))) { + 0 => return, + else => |err| return @errSetCast(Error, @intToError(@intCast(u16, err))), // TODO don't hardcode u16 + } } const elf_base = std.process.getBaseAddress(); @@ -2877,11 +3015,10 @@ pub fn dl_iterate_phdr( .dlpi_phnum = ehdr.e_phnum, }; - return callback(&info, @sizeOf(dl_phdr_info), data); + return callback(&info, @sizeOf(dl_phdr_info), context); } // Last return value from the callback function - var last_r: isize = 0; while (it.next()) |entry| { var dlpi_phdr: [*]elf.Phdr = undefined; var dlpi_phnum: u16 = undefined; @@ -2903,11 +3040,8 @@ pub fn dl_iterate_phdr( .dlpi_phnum = dlpi_phnum, }; - last_r = callback(&info, @sizeOf(dl_phdr_info), data); - if (last_r != 0) break; + try callback(&info, @sizeOf(dl_phdr_info), context); } - - return last_r; } pub const ClockGetTimeError = error{UnsupportedClock} || UnexpectedError; diff --git a/lib/std/os/test.zig b/lib/std/os/test.zig index 9b967955ec75333e8e7d3780654c352c79300b80..055d049f69a19ca9088c0e249cdd7af9ecf3b2d6 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); @@ -165,16 +165,19 @@ test "sigaltstack" { // analyzed const dl_phdr_info = if (@hasDecl(os, "dl_phdr_info")) os.dl_phdr_info else c_void; -fn iter_fn(info: *dl_phdr_info, size: usize, data: ?*usize) callconv(.C) i32 { - if (builtin.os == .windows or builtin.os == .wasi or builtin.os == .macosx) - return 0; +const IterFnError = error{ + MissingPtLoadSegment, + MissingLoad, + BadElfMagic, + FailedConsistencyCheck, +}; - var counter = data.?; +fn iter_fn(info: *dl_phdr_info, size: usize, counter: *usize) IterFnError!void { // Count how many libraries are loaded counter.* += @as(usize, 1); // The image should contain at least a PT_LOAD segment - if (info.dlpi_phnum < 1) return -1; + if (info.dlpi_phnum < 1) return error.MissingPtLoadSegment; // Quick & dirty validation of the phdr pointers, make sure we're not // pointing to some random gibberish @@ -189,17 +192,15 @@ fn iter_fn(info: *dl_phdr_info, size: usize, data: ?*usize) callconv(.C) i32 { // Find the ELF header const elf_header = @intToPtr(*elf.Ehdr, reloc_addr - phdr.p_offset); // Validate the magic - if (!mem.eql(u8, elf_header.e_ident[0..4], "\x7fELF")) return -1; + if (!mem.eql(u8, elf_header.e_ident[0..4], "\x7fELF")) return error.BadElfMagic; // Consistency check - if (elf_header.e_phnum != info.dlpi_phnum) return -1; + if (elf_header.e_phnum != info.dlpi_phnum) return error.FailedConsistencyCheck; found_load = true; break; } - if (!found_load) return -1; - - return 42; + if (!found_load) return error.MissingLoad; } test "dl_iterate_phdr" { @@ -207,7 +208,7 @@ test "dl_iterate_phdr" { return error.SkipZigTest; var counter: usize = 0; - expect(os.dl_iterate_phdr(usize, iter_fn, &counter) != 0); + try os.dl_iterate_phdr(&counter, IterFnError, iter_fn); expect(counter != 0); } diff --git a/lib/std/os/windows/ntdll.zig b/lib/std/os/windows/ntdll.zig index 08f22386f99c6a59983abe346fe7c9a7df27f191..e98a2e6a87de6dc9f091de6463d150b0a5ba6e4b 100644 --- a/lib/std/os/windows/ntdll.zig +++ b/lib/std/os/windows/ntdll.zig @@ -8,6 +8,12 @@ pub extern "NtDll" fn NtQueryInformationFile( Length: ULONG, FileInformationClass: FILE_INFORMATION_CLASS, ) callconv(.Stdcall) NTSTATUS; + +pub extern "NtDll" fn NtQueryAttributesFile( + ObjectAttributes: *OBJECT_ATTRIBUTES, + FileAttributes: *FILE_BASIC_INFORMATION, +) callconv(.Stdcall) NTSTATUS; + pub extern "NtDll" fn NtCreateFile( FileHandle: *HANDLE, DesiredAccess: ACCESS_MASK, diff --git a/lib/std/process.zig b/lib/std/process.zig index 0178f6fa916b5c35b70e476e072a2ba3d5c8065f..89307b44da66d49f57bb347ec7c83f64d1ba44cd 100644 --- a/lib/std/process.zig +++ b/lib/std/process.zig @@ -613,3 +613,59 @@ pub fn getBaseAddress() usize { else => @compileError("Unsupported OS"), } } + +/// Caller owns the result value and each inner slice. +pub fn getSelfExeSharedLibPaths(allocator: *Allocator) error{OutOfMemory}![][:0]u8 { + switch (builtin.link_mode) { + .Static => return &[_][:0]u8{}, + .Dynamic => {}, + } + const List = std.ArrayList([:0]u8); + switch (builtin.os) { + .linux, + .freebsd, + .netbsd, + .dragonfly, + => { + var paths = List.init(allocator); + errdefer { + const slice = paths.toOwnedSlice(); + for (slice) |item| { + allocator.free(item); + } + allocator.free(slice); + } + try os.dl_iterate_phdr(&paths, error{OutOfMemory}, struct { + fn callback(info: *os.dl_phdr_info, size: usize, list: *List) !void { + const name = info.dlpi_name orelse return; + if (name[0] == '/') { + const item = try mem.dupeZ(list.allocator, u8, mem.toSliceConst(u8, name)); + errdefer list.allocator.free(item); + try list.append(item); + } + } + }.callback); + return paths.toOwnedSlice(); + }, + .macosx, .ios, .watchos, .tvos => { + var paths = List.init(allocator); + errdefer { + const slice = paths.toOwnedSlice(); + for (slice) |item| { + allocator.free(item); + } + allocator.free(slice); + } + const img_count = std.c._dyld_image_count(); + var i: u32 = 0; + while (i < img_count) : (i += 1) { + const name = std.c._dyld_get_image_name(i); + const item = try mem.dupeZ(allocator, u8, mem.toSliceConst(u8, name)); + errdefer allocator.free(item); + try paths.append(item); + } + return paths.toOwnedSlice(); + }, + else => @compileError("getSelfExeSharedLibPaths unimplemented for this target"), + } +} diff --git a/lib/std/target.zig b/lib/std/target.zig index 308f1d9923bd062ef80c78e409dfcfbfdc4d0f3a..bc78b2dce5e795a273b8e63d82edef9e60f0c3ac 100644 --- a/lib/std/target.zig +++ b/lib/std/target.zig @@ -1037,6 +1037,20 @@ pub const Target = union(enum) { }; } + pub fn isAndroid(self: Target) bool { + return switch (self.getAbi()) { + .android => true, + else => false, + }; + } + + 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, @@ -1189,6 +1203,173 @@ pub const Target = union(enum) { return .unavailable; } + + pub const FloatAbi = enum { + hard, + soft, + soft_fp, + }; + + pub fn getFloatAbi(self: Target) FloatAbi { + return switch (self.getAbi()) { + .gnueabihf, + .eabihf, + .musleabihf, + => .hard, + else => .soft, + }; + } + + pub fn hasDynamicLinker(self: Target) bool { + switch (self.getArch()) { + .wasm32, + .wasm64, + => return false, + else => {}, + } + switch (self.getOs()) { + .freestanding, + .ios, + .tvos, + .watchos, + .macosx, + .uefi, + .windows, + .emscripten, + .other, + => return false, + else => return true, + } + } + + /// Caller owns returned memory. + pub fn getStandardDynamicLinkerPath( + self: Target, + allocator: *mem.Allocator, + ) error{ + OutOfMemory, + UnknownDynamicLinkerPath, + TargetHasNoDynamicLinker, + }![:0]u8 { + const a = allocator; + if (self.isAndroid()) { + return mem.dupeZ(a, u8, if (self.getArchPtrBitWidth() == 64) + "/system/bin/linker64" + else + "/system/bin/linker"); + } + + if (self.isMusl()) { + var result = try std.Buffer.init(allocator, "/lib/ld-musl-"); + defer result.deinit(); + + var is_arm = false; + switch (self.getArch()) { + .arm, .thumb => { + try result.append("arm"); + is_arm = true; + }, + .armeb, .thumbeb => { + try result.append("armeb"); + is_arm = true; + }, + else => |arch| try result.append(@tagName(arch)), + } + if (is_arm and self.getFloatAbi() == .hard) { + try result.append("hf"); + } + try result.append(".so.1"); + return result.toOwnedSlice(); + } + + switch (self.getOs()) { + .freebsd => return mem.dupeZ(a, u8, "/libexec/ld-elf.so.1"), + .netbsd => return mem.dupeZ(a, u8, "/libexec/ld.elf_so"), + .dragonfly => return mem.dupeZ(a, u8, "/libexec/ld-elf.so.2"), + .linux => switch (self.getArch()) { + .i386, + .sparc, + .sparcel, + => return mem.dupeZ(a, u8, "/lib/ld-linux.so.2"), + + .aarch64 => return mem.dupeZ(a, u8, "/lib/ld-linux-aarch64.so.1"), + .aarch64_be => return mem.dupeZ(a, u8, "/lib/ld-linux-aarch64_be.so.1"), + .aarch64_32 => return mem.dupeZ(a, u8, "/lib/ld-linux-aarch64_32.so.1"), + + .arm, + .armeb, + .thumb, + .thumbeb, + => return mem.dupeZ(a, u8, switch (self.getFloatAbi()) { + .hard => "/lib/ld-linux-armhf.so.3", + else => "/lib/ld-linux.so.3", + }), + + .mips, + .mipsel, + .mips64, + .mips64el, + => return error.UnknownDynamicLinkerPath, + + .powerpc => return mem.dupeZ(a, u8, "/lib/ld.so.1"), + .powerpc64, .powerpc64le => return mem.dupeZ(a, u8, "/lib64/ld64.so.2"), + .s390x => return mem.dupeZ(a, u8, "/lib64/ld64.so.1"), + .sparcv9 => return mem.dupeZ(a, u8, "/lib64/ld-linux.so.2"), + .x86_64 => return mem.dupeZ(a, u8, switch (self.getAbi()) { + .gnux32 => "/libx32/ld-linux-x32.so.2", + else => "/lib64/ld-linux-x86-64.so.2", + }), + + .riscv32 => return mem.dupeZ(a, u8, "/lib/ld-linux-riscv32-ilp32.so.1"), + .riscv64 => return mem.dupeZ(a, u8, "/lib/ld-linux-riscv64-lp64.so.1"), + + .wasm32, + .wasm64, + => return error.TargetHasNoDynamicLinker, + + .arc, + .avr, + .bpfel, + .bpfeb, + .hexagon, + .msp430, + .r600, + .amdgcn, + .tce, + .tcele, + .xcore, + .nvptx, + .nvptx64, + .le32, + .le64, + .amdil, + .amdil64, + .hsail, + .hsail64, + .spir, + .spir64, + .kalimba, + .shave, + .lanai, + .renderscript32, + .renderscript64, + => return error.UnknownDynamicLinkerPath, + }, + + .freestanding, + .ios, + .tvos, + .watchos, + .macosx, + .uefi, + .windows, + .emscripten, + .other, + => return error.TargetHasNoDynamicLinker, + + else => return error.UnknownDynamicLinkerPath, + } + } }; test "parseCpuFeatureSet" { diff --git a/lib/std/time.zig b/lib/std/time.zig index 716c999577931083b52cae1407177ddb72f13801..a5f66122cb3a495c2a976cb820404888a2be4bb6 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 1de2b6a24c3b045f67ac0de2e3004ce6b64f36f8..ae9a886d1bae58ed6b81a3a8dbff18e851e2dc0c 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/introspect.zig b/src-self-hosted/introspect.zig index 3cf18ab3632f235b87a0ca151c8536e0d18f2691..11838e7e633e384fdd065df86f5c00a123907fe2 100644 --- a/src-self-hosted/introspect.zig +++ b/src-self-hosted/introspect.zig @@ -6,6 +6,14 @@ const fs = std.fs; const warn = std.debug.warn; +pub fn detectDynamicLinker(allocator: *mem.Allocator, target: std.Target) ![:0]u8 { + if (target == .Native) { + return @import("libc_installation.zig").detectNativeDynamicLinker(allocator); + } else { + return target.getStandardDynamicLinkerPath(allocator); + } +} + /// Caller must free result pub fn testZigInstallPrefix(allocator: *mem.Allocator, test_path: []const u8) ![]u8 { const test_zig_dir = try fs.path.join(allocator, &[_][]const u8{ test_path, "lib", "zig" }); diff --git a/src-self-hosted/libc_installation.zig b/src-self-hosted/libc_installation.zig index 701823c4be790d91e57229dc6ae1f07d26e40ca9..fd920ad2911f8b9089dccdac8a65a1e3c17398c9 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, @@ -27,31 +36,24 @@ pub const LibCInstallation = struct { LibCStdLibHeaderNotFound, LibCKernel32LibNotFound, UnsupportedArchitecture, + WindowsSdkNotFound, }; 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 +71,199 @@ 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`. + \\# Only needed when targeting MinGW-w64 on Windows. + \\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) FindError!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.WindowsSdkNotFound, + .PathTooLong => return error.WindowsSdkNotFound, } - }, - .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 { + try blk: { + var batch = Batch(FindError!void, 2, .auto_async).init(); + errdefer batch.wait() catch {}; + 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)); + } + break :blk batch.wait(); + }; + } + return self; + } + + /// 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); + } } - return group.wait(); + self.* = undefined; } - async fn findNativeIncludeDirLinux(self: *LibCInstallation, allocator: *Allocator) FindError!void { - const cc_exe = std.os.getenv("CC") orelse "cc"; + 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 exec_res = std.ChildProcess.exec2(.{ + .allocator = allocator, + .argv = &argv, + .max_output_bytes = 1024 * 1024, + // Some C compilers, such as Clang, are known to rely on argv[0] to find the path + // to their own executable, without even bothering to resolve PATH. This results in the message: + // error: unable to execute command: Executable "" doesn't exist! + // So we use the expandArg0 variant of ChildProcess to give them a helping hand. + .expand_arg0 = .expand, + }) 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.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 +275,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 +320,11 @@ 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, + ) FindError!void { var search_buf: [2]Search = undefined; const searches = fillSearch(&search_buf, sdk); @@ -255,180 +336,301 @@ 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, + fn findNativeMsvcIncludeDir( + self: *LibCInstallation, + allocator: *Allocator, + sdk: *ZigWindowsSDK, + ) FindError!void { + const msvc_lib_dir_ptr = sdk.msvc_lib_dir_ptr orelse return error.LibCStdLibHeaderNotFound; + const msvc_lib_dir = msvc_lib_dir_ptr[0..sdk.msvc_lib_dir_len]; + const up1 = fs.path.dirname(msvc_lib_dir) orelse return error.LibCStdLibHeaderNotFound; + const up2 = fs.path.dirname(up1) orelse return error.LibCStdLibHeaderNotFound; + + var result_buf = try std.Buffer.init(allocator, up2); + defer result_buf.deinit(); + + try result_buf.append("\\include"); + + var dir = fs.cwd().openDirList(result_buf.toSliceConst()) catch |err| switch (err) { + error.FileNotFound, + error.NotDir, + error.NoDevice, + => return error.LibCStdLibHeaderNotFound, + + else => return error.FileSystem, }; + defer dir.close(); + + dir.accessZ("vcruntime.h", .{}) catch |err| switch (err) { + error.FileNotFound => return error.LibCStdLibHeaderNotFound, + else => return error.FileSystem, + }; + + self.sys_include_dir = result_buf.toOwnedSlice(); + } + + fn findNativeMsvcLibDir( + self: *LibCInstallation, + allocator: *Allocator, + sdk: *ZigWindowsSDK, + ) FindError!void { + const msvc_lib_dir_ptr = sdk.msvc_lib_dir_ptr orelse return error.LibCRuntimeNotFound; + self.msvc_lib_dir = try std.mem.dupeZ(allocator, u8, msvc_lib_dir_ptr[0..sdk.msvc_lib_dir_len]); } }; +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"; +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 exec_res = std.ChildProcess.exec2(.{ + .allocator = allocator, + .argv = &argv, + .max_output_bytes = 1024 * 1024, + // Some C compilers, such as Clang, are known to rely on argv[0] to find the path + // to their own executable, without even bothering to resolve PATH. This results in the message: + // error: unable to execute command: Executable "" doesn't exist! + // So we use the expandArg0 variant of ChildProcess to give them a helping hand. + .expand_arg0 = .expand, + }) 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"); + + var it = std.mem.tokenize(exec_res.stdout, "\n\r"); const line = it.next() orelse return error.LibCRuntimeNotFound; - const dirname = fs.path.dirname(line) orelse return error.LibCRuntimeNotFound; + // When this command fails, it returns exit code 0 and duplicates the input file name. + // So we detect failure by checking if the output matches exactly the input. + if (std.mem.eql(u8, line, o_file)) 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); + }, + } +} + +/// Caller owns returned memory. +pub fn detectNativeDynamicLinker(allocator: *Allocator) error{ + OutOfMemory, + TargetHasNoDynamicLinker, + UnknownDynamicLinkerPath, +}![:0]u8 { + if (!comptime Target.current.hasDynamicLinker()) { + return error.TargetHasNoDynamicLinker; + } + + // The current target's ABI cannot be relied on for this. For example, we may build the zig + // compiler for target riscv64-linux-musl and provide a tarball for users to download. + // A user could then run that zig compiler on riscv64-linux-gnu. This use case is well-defined + // and supported by Zig. But that means that we must detect the system ABI here rather than + // relying on `std.Target.current`. - if (want_dirname) { - return std.mem.dupe(allocator, u8, dirname); - } else { - return std.mem.dupe(allocator, u8, line); + const LdInfo = struct { + ld_path: []u8, + abi: Target.Abi, + }; + var ld_info_list = std.ArrayList(LdInfo).init(allocator); + defer { + for (ld_info_list.toSlice()) |ld_info| allocator.free(ld_info.ld_path); + ld_info_list.deinit(); } + + const all_abis = comptime blk: { + const fields = std.meta.fields(Target.Abi); + var array: [fields.len]Target.Abi = undefined; + inline for (fields) |field, i| { + array[i] = @field(Target.Abi, field.name); + } + break :blk array; + }; + for (all_abis) |abi| { + // This may be a nonsensical parameter. We detect this with error.UnknownDynamicLinkerPath and + // skip adding it to `ld_info_list`. + const target: Target = .{ + .Cross = .{ + .arch = Target.current.getArch(), + .os = Target.current.getOs(), + .abi = abi, + .cpu_features = Target.current.getArch().getBaselineCpuFeatures(), + }, + }; + const standard_ld_path = target.getStandardDynamicLinkerPath(allocator) catch |err| switch (err) { + error.OutOfMemory => return error.OutOfMemory, + error.UnknownDynamicLinkerPath, error.TargetHasNoDynamicLinker => continue, + }; + errdefer allocator.free(standard_ld_path); + try ld_info_list.append(.{ + .ld_path = standard_ld_path, + .abi = abi, + }); + } + + // Best case scenario: the zig compiler is dynamically linked, and we can iterate + // over our own shared objects and find a dynamic linker. + { + const lib_paths = try std.process.getSelfExeSharedLibPaths(allocator); + defer allocator.free(lib_paths); + + // This is O(N^M) but typical case here is N=2 and M=10. + for (lib_paths) |lib_path| { + for (ld_info_list.toSlice()) |ld_info| { + const standard_ld_basename = fs.path.basename(ld_info.ld_path); + if (std.mem.endsWith(u8, lib_path, standard_ld_basename)) { + return std.mem.dupeZ(allocator, u8, lib_path); + } + } + } + } + + // If Zig is statically linked, such as via distributed binary static builds, the above + // trick won't work. What are we left with? Try to run the system C compiler and get + // it to tell us the dynamic linker path. + // TODO: instead of this, look at the shared libs of /usr/bin/env. + for (ld_info_list.toSlice()) |ld_info| { + const standard_ld_basename = fs.path.basename(ld_info.ld_path); + + const full_ld_path = ccPrintFileName(allocator, standard_ld_basename, .full_path) catch |err| switch (err) { + error.OutOfMemory => return error.OutOfMemory, + error.LibCRuntimeNotFound, + error.CCompilerExitCode, + error.CCompilerCrashed, + error.UnableToSpawnCCompiler, + => continue, + }; + return full_ld_path; + } + + // Finally, we fall back on the standard path. + return Target.current.getStandardDynamicLinkerPath(allocator); } const Search = struct { @@ -436,34 +638,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 deleted file mode 100644 index ffa0754bb5782b357867fb970c37cce72ffafd7d..0000000000000000000000000000000000000000 --- a/src-self-hosted/stage1.zig +++ /dev/null @@ -1,834 +0,0 @@ -// This is Zig code that is used by both stage1 and stage2. -// The prototypes in src/userland.h must match these definitions. - -const std = @import("std"); -const io = std.io; -const mem = std.mem; -const fs = std.fs; -const process = std.process; -const Allocator = mem.Allocator; -const ArrayList = std.ArrayList; -const Buffer = std.Buffer; -const Target = std.Target; -const self_hosted_main = @import("main.zig"); -const errmsg = @import("errmsg.zig"); -const DepTokenizer = @import("dep_tokenizer.zig").Tokenizer; -const assert = std.debug.assert; - -var stderr_file: fs.File = undefined; -var stderr: *io.OutStream(fs.File.WriteError) = undefined; -var stdout: *io.OutStream(fs.File.WriteError) = undefined; - -comptime { - _ = @import("dep_tokenizer.zig"); -} - -// ABI warning -export fn stage2_zen(ptr: *[*]const u8, len: *usize) void { - const info_zen = @import("main.zig").info_zen; - ptr.* = info_zen; - len.* = info_zen.len; -} - -// ABI warning -export fn stage2_panic(ptr: [*]const u8, len: usize) void { - @panic(ptr[0..len]); -} - -// ABI warning -const Error = extern enum { - None, - OutOfMemory, - InvalidFormat, - SemanticAnalyzeFail, - AccessDenied, - Interrupted, - SystemResources, - FileNotFound, - FileSystem, - FileTooBig, - DivByZero, - Overflow, - PathAlreadyExists, - Unexpected, - ExactDivRemainder, - NegativeDenominator, - ShiftedOutOneBits, - CCompileErrors, - EndOfFile, - IsDir, - NotDir, - UnsupportedOperatingSystem, - SharingViolation, - PipeBusy, - PrimitiveTypeNotFound, - CacheUnavailable, - PathTooLong, - CCompilerCannotFindFile, - NoCCompilerInstalled, - ReadingDepFile, - InvalidDepFile, - MissingArchitecture, - MissingOperatingSystem, - UnknownArchitecture, - UnknownOperatingSystem, - UnknownABI, - InvalidFilename, - DiskQuota, - DiskSpace, - UnexpectedWriteFailure, - UnexpectedSeekFailure, - UnexpectedFileTruncationFailure, - Unimplemented, - OperationAborted, - BrokenPipe, - NoSpaceLeft, - NotLazy, - IsAsync, - ImportOutsidePkgPath, - UnknownCpu, - UnknownSubArchitecture, - UnknownCpuFeature, - InvalidCpuFeatures, - InvalidLlvmCpuFeaturesFormat, - UnknownApplicationBinaryInterface, - ASTUnitFailure, -}; - -const FILE = std.c.FILE; -const ast = std.zig.ast; -const translate_c = @import("translate_c.zig"); - -/// Args should have a null terminating last arg. -export fn stage2_translate_c( - out_ast: **ast.Tree, - out_errors_ptr: *[*]translate_c.ClangErrMsg, - out_errors_len: *usize, - args_begin: [*]?[*]const u8, - args_end: [*]?[*]const u8, - resources_path: [*:0]const u8, -) Error { - var errors = @as([*]translate_c.ClangErrMsg, undefined)[0..0]; - out_ast.* = translate_c.translate(std.heap.c_allocator, args_begin, args_end, &errors, resources_path) catch |err| switch (err) { - error.SemanticAnalyzeFail => { - out_errors_ptr.* = errors.ptr; - out_errors_len.* = errors.len; - return Error.CCompileErrors; - }, - error.ASTUnitFailure => return Error.ASTUnitFailure, - error.OutOfMemory => return Error.OutOfMemory, - }; - return Error.None; -} - -export fn stage2_free_clang_errors(errors_ptr: [*]translate_c.ClangErrMsg, errors_len: usize) void { - translate_c.freeErrors(errors_ptr[0..errors_len]); -} - -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, - }; - return Error.None; -} - -// TODO: just use the actual self-hosted zig fmt. Until https://github.com/ziglang/zig/issues/2377, -// we use a blocking implementation. -export fn stage2_fmt(argc: c_int, argv: [*]const [*:0]const u8) c_int { - if (std.debug.runtime_safety) { - fmtMain(argc, argv) catch unreachable; - } else { - fmtMain(argc, argv) catch |e| { - std.debug.warn("{}\n", .{@errorName(e)}); - return -1; - }; - } - return 0; -} - -fn fmtMain(argc: c_int, argv: [*]const [*:0]const u8) !void { - const allocator = std.heap.c_allocator; - var args_list = std.ArrayList([]const u8).init(allocator); - const argc_usize = @intCast(usize, argc); - var arg_i: usize = 0; - while (arg_i < argc_usize) : (arg_i += 1) { - try args_list.append(mem.toSliceConst(u8, argv[arg_i])); - } - - stdout = &std.io.getStdOut().outStream().stream; - stderr_file = std.io.getStdErr(); - stderr = &stderr_file.outStream().stream; - - const args = args_list.toSliceConst()[2..]; - - var color: errmsg.Color = .Auto; - var stdin_flag: bool = false; - var check_flag: bool = false; - var input_files = ArrayList([]const u8).init(allocator); - - { - var i: usize = 0; - while (i < args.len) : (i += 1) { - const arg = args[i]; - if (mem.startsWith(u8, arg, "-")) { - if (mem.eql(u8, arg, "--help")) { - try stdout.write(self_hosted_main.usage_fmt); - process.exit(0); - } else if (mem.eql(u8, arg, "--color")) { - if (i + 1 >= args.len) { - try stderr.write("expected [auto|on|off] after --color\n"); - process.exit(1); - } - i += 1; - const next_arg = args[i]; - if (mem.eql(u8, next_arg, "auto")) { - color = .Auto; - } else if (mem.eql(u8, next_arg, "on")) { - color = .On; - } else if (mem.eql(u8, next_arg, "off")) { - color = .Off; - } else { - try stderr.print("expected [auto|on|off] after --color, found '{}'\n", .{next_arg}); - process.exit(1); - } - } else if (mem.eql(u8, arg, "--stdin")) { - stdin_flag = true; - } else if (mem.eql(u8, arg, "--check")) { - check_flag = true; - } else { - try stderr.print("unrecognized parameter: '{}'", .{arg}); - process.exit(1); - } - } else { - try input_files.append(arg); - } - } - } - - if (stdin_flag) { - if (input_files.len != 0) { - try stderr.write("cannot use --stdin with positional arguments\n"); - process.exit(1); - } - - const stdin_file = io.getStdIn(); - var stdin = stdin_file.inStream(); - - const source_code = try stdin.stream.readAllAlloc(allocator, self_hosted_main.max_src_size); - defer allocator.free(source_code); - - const tree = std.zig.parse(allocator, source_code) catch |err| { - try stderr.print("error parsing stdin: {}\n", .{err}); - process.exit(1); - }; - defer tree.deinit(); - - var error_it = tree.errors.iterator(0); - while (error_it.next()) |parse_error| { - try printErrMsgToFile(allocator, parse_error, tree, "", stderr_file, color); - } - if (tree.errors.len != 0) { - process.exit(1); - } - if (check_flag) { - const anything_changed = try std.zig.render(allocator, io.null_out_stream, tree); - const code = if (anything_changed) @as(u8, 1) else @as(u8, 0); - process.exit(code); - } - - _ = try std.zig.render(allocator, stdout, tree); - return; - } - - if (input_files.len == 0) { - try stderr.write("expected at least one source file argument\n"); - process.exit(1); - } - - var fmt = Fmt{ - .seen = Fmt.SeenMap.init(allocator), - .any_error = false, - .color = color, - .allocator = allocator, - }; - - for (input_files.toSliceConst()) |file_path| { - try fmtPath(&fmt, file_path, check_flag); - } - if (fmt.any_error) { - process.exit(1); - } -} - -const FmtError = error{ - SystemResources, - OperationAborted, - IoPending, - BrokenPipe, - Unexpected, - WouldBlock, - FileClosed, - DestinationAddressRequired, - DiskQuota, - FileTooBig, - InputOutput, - NoSpaceLeft, - AccessDenied, - OutOfMemory, - RenameAcrossMountPoints, - ReadOnlyFileSystem, - LinkQuotaExceeded, - FileBusy, -} || fs.File.OpenError; - -fn fmtPath(fmt: *Fmt, file_path: []const u8, check_mode: bool) FmtError!void { - if (fmt.seen.exists(file_path)) return; - try fmt.seen.put(file_path); - - const source_code = io.readFileAlloc(fmt.allocator, file_path) catch |err| switch (err) { - error.IsDir, error.AccessDenied => { - // TODO make event based (and dir.next()) - var dir = try fs.cwd().openDirList(file_path); - defer dir.close(); - - var dir_it = dir.iterate(); - - while (try dir_it.next()) |entry| { - if (entry.kind == .Directory or mem.endsWith(u8, entry.name, ".zig")) { - const full_path = try fs.path.join(fmt.allocator, &[_][]const u8{ file_path, entry.name }); - try fmtPath(fmt, full_path, check_mode); - } - } - return; - }, - else => { - // TODO lock stderr printing - try stderr.print("unable to open '{}': {}\n", .{ file_path, err }); - fmt.any_error = true; - return; - }, - }; - defer fmt.allocator.free(source_code); - - const tree = std.zig.parse(fmt.allocator, source_code) catch |err| { - try stderr.print("error parsing file '{}': {}\n", .{ file_path, err }); - fmt.any_error = true; - return; - }; - defer tree.deinit(); - - var error_it = tree.errors.iterator(0); - while (error_it.next()) |parse_error| { - try printErrMsgToFile(fmt.allocator, parse_error, tree, file_path, stderr_file, fmt.color); - } - if (tree.errors.len != 0) { - fmt.any_error = true; - return; - } - - if (check_mode) { - const anything_changed = try std.zig.render(fmt.allocator, io.null_out_stream, tree); - if (anything_changed) { - try stderr.print("{}\n", .{file_path}); - fmt.any_error = true; - } - } else { - const baf = try io.BufferedAtomicFile.create(fmt.allocator, file_path); - defer baf.destroy(); - - const anything_changed = try std.zig.render(fmt.allocator, baf.stream(), tree); - if (anything_changed) { - try stderr.print("{}\n", .{file_path}); - try baf.finish(); - } - } -} - -const Fmt = struct { - seen: SeenMap, - any_error: bool, - color: errmsg.Color, - allocator: *mem.Allocator, - - const SeenMap = std.BufSet; -}; - -fn printErrMsgToFile( - allocator: *mem.Allocator, - parse_error: *const ast.Error, - tree: *ast.Tree, - path: []const u8, - file: fs.File, - color: errmsg.Color, -) !void { - const color_on = switch (color) { - .Auto => file.isTty(), - .On => true, - .Off => false, - }; - const lok_token = parse_error.loc(); - const span = errmsg.Span{ - .first = lok_token, - .last = lok_token, - }; - - const first_token = tree.tokens.at(span.first); - const last_token = tree.tokens.at(span.last); - const start_loc = tree.tokenLocationPtr(0, first_token); - const end_loc = tree.tokenLocationPtr(first_token.end, last_token); - - var text_buf = try std.Buffer.initSize(allocator, 0); - var out_stream = &std.io.BufferOutStream.init(&text_buf).stream; - try parse_error.render(&tree.tokens, out_stream); - const text = text_buf.toOwnedSlice(); - - const stream = &file.outStream().stream; - try stream.print("{}:{}:{}: error: {}\n", .{ path, start_loc.line + 1, start_loc.column + 1, text }); - - if (!color_on) return; - - // Print \r and \t as one space each so that column counts line up - for (tree.source[start_loc.line_start..start_loc.line_end]) |byte| { - try stream.writeByte(switch (byte) { - '\r', '\t' => ' ', - else => byte, - }); - } - try stream.writeByte('\n'); - try stream.writeByteNTimes(' ', start_loc.column); - try stream.writeByteNTimes('~', last_token.end - first_token.start); - try stream.writeByte('\n'); -} - -export fn stage2_DepTokenizer_init(input: [*]const u8, len: usize) stage2_DepTokenizer { - const t = std.heap.c_allocator.create(DepTokenizer) catch @panic("failed to create .d tokenizer"); - t.* = DepTokenizer.init(std.heap.c_allocator, input[0..len]); - return stage2_DepTokenizer{ - .handle = t, - }; -} - -export fn stage2_DepTokenizer_deinit(self: *stage2_DepTokenizer) void { - self.handle.deinit(); -} - -export fn stage2_DepTokenizer_next(self: *stage2_DepTokenizer) stage2_DepNextResult { - const otoken = self.handle.next() catch { - const textz = std.Buffer.init(&self.handle.arena.allocator, self.handle.error_text) catch @panic("failed to create .d tokenizer error text"); - return stage2_DepNextResult{ - .type_id = .error_, - .textz = textz.toSlice().ptr, - }; - }; - const token = otoken orelse { - return stage2_DepNextResult{ - .type_id = .null_, - .textz = undefined, - }; - }; - const textz = std.Buffer.init(&self.handle.arena.allocator, token.bytes) catch @panic("failed to create .d tokenizer token text"); - return stage2_DepNextResult{ - .type_id = switch (token.id) { - .target => .target, - .prereq => .prereq, - }, - .textz = textz.toSlice().ptr, - }; -} - -const stage2_DepTokenizer = extern struct { - handle: *DepTokenizer, -}; - -const stage2_DepNextResult = extern struct { - type_id: TypeId, - - // when type_id == error --> error text - // when type_id == null --> undefined - // when type_id == target --> target pathname - // when type_id == prereq --> prereq pathname - textz: [*]const u8, - - const TypeId = extern enum { - error_, - null_, - target, - prereq, - }; -}; - -// ABI warning -export fn stage2_attach_segfault_handler() void { - if (std.debug.runtime_safety and std.debug.have_segfault_handling_support) { - std.debug.attachSegfaultHandler(); - } -} - -// ABI warning -export fn stage2_progress_create() *std.Progress { - const ptr = std.heap.c_allocator.create(std.Progress) catch @panic("out of memory"); - ptr.* = std.Progress{}; - return ptr; -} - -// ABI warning -export fn stage2_progress_destroy(progress: *std.Progress) void { - std.heap.c_allocator.destroy(progress); -} - -// ABI warning -export fn stage2_progress_start_root( - progress: *std.Progress, - name_ptr: [*]const u8, - name_len: usize, - estimated_total_items: usize, -) *std.Progress.Node { - return progress.start( - name_ptr[0..name_len], - if (estimated_total_items == 0) null else estimated_total_items, - ) catch @panic("timer unsupported"); -} - -// ABI warning -export fn stage2_progress_disable_tty(progress: *std.Progress) void { - progress.terminal = null; -} - -// ABI warning -export fn stage2_progress_start( - node: *std.Progress.Node, - name_ptr: [*]const u8, - name_len: usize, - estimated_total_items: usize, -) *std.Progress.Node { - const child_node = std.heap.c_allocator.create(std.Progress.Node) catch @panic("out of memory"); - child_node.* = node.start( - name_ptr[0..name_len], - if (estimated_total_items == 0) null else estimated_total_items, - ); - child_node.activate(); - return child_node; -} - -// ABI warning -export fn stage2_progress_end(node: *std.Progress.Node) void { - node.end(); - if (&node.context.root != node) { - std.heap.c_allocator.destroy(node); - } -} - -// ABI warning -export fn stage2_progress_complete_one(node: *std.Progress.Node) void { - node.completeOne(); -} - -// ABI warning -export fn stage2_progress_update_node(node: *std.Progress.Node, done_count: usize, total_count: usize) void { - node.completed_items = done_count; - node.estimated_total_items = total_count; - node.activate(); - node.context.maybeRefresh(); -} - -fn cpuFeaturesFromLLVM( - arch: Target.Arch, - llvm_cpu_name_z: ?[*:0]const u8, - llvm_cpu_features_opt: ?[*:0]const u8, -) !Target.CpuFeatures { - var result = arch.getBaselineCpuFeatures(); - - if (llvm_cpu_name_z) |cpu_name_z| { - const llvm_cpu_name = mem.toSliceConst(u8, cpu_name_z); - - for (arch.allCpus()) |cpu| { - const this_llvm_name = cpu.llvm_name orelse continue; - if (mem.eql(u8, this_llvm_name, llvm_cpu_name)) { - // Here we use the non-dependencies-populated set, - // so that subtracting features later in this function - // affect the prepopulated set. - result = Target.CpuFeatures{ - .cpu = cpu, - .features = cpu.features, - }; - break; - } - } - } - - const all_features = arch.allFeaturesList(); - - if (llvm_cpu_features_opt) |llvm_cpu_features| { - var it = mem.tokenize(mem.toSliceConst(u8, llvm_cpu_features), ","); - while (it.next()) |decorated_llvm_feat| { - var op: enum { - add, - sub, - } = undefined; - var llvm_feat: []const u8 = undefined; - if (mem.startsWith(u8, decorated_llvm_feat, "+")) { - op = .add; - llvm_feat = decorated_llvm_feat[1..]; - } else if (mem.startsWith(u8, decorated_llvm_feat, "-")) { - op = .sub; - llvm_feat = decorated_llvm_feat[1..]; - } else { - return error.InvalidLlvmCpuFeaturesFormat; - } - for (all_features) |feature, index_usize| { - const this_llvm_name = feature.llvm_name orelse continue; - if (mem.eql(u8, llvm_feat, this_llvm_name)) { - const index = @intCast(Target.Cpu.Feature.Set.Index, index_usize); - switch (op) { - .add => result.features.addFeature(index), - .sub => result.features.removeFeature(index), - } - break; - } - } - } - } - - result.features.populateDependencies(all_features); - return result; -} - -// ABI warning -export fn stage2_cmd_targets(zig_triple: [*:0]const u8) c_int { - cmdTargets(zig_triple) catch |err| { - std.debug.warn("unable to list targets: {}\n", .{@errorName(err)}); - return -1; - }; - return 0; -} - -fn cmdTargets(zig_triple: [*:0]const u8) !void { - var target = try Target.parse(mem.toSliceConst(u8, zig_triple)); - target.Cross.cpu_features = blk: { - const llvm = @import("llvm.zig"); - const llvm_cpu_name = llvm.GetHostCPUName(); - const llvm_cpu_features = llvm.GetNativeFeatures(); - break :blk try cpuFeaturesFromLLVM(target.Cross.arch, llvm_cpu_name, llvm_cpu_features); - }; - return @import("print_targets.zig").cmdTargets( - std.heap.c_allocator, - &[0][]u8{}, - &std.io.getStdOut().outStream().stream, - target, - ); -} - -const Stage2CpuFeatures = struct { - allocator: *mem.Allocator, - cpu_features: Target.CpuFeatures, - - llvm_features_str: ?[*:0]const u8, - - builtin_str: [:0]const u8, - cache_hash: [:0]const u8, - - const Self = @This(); - - fn createFromNative(allocator: *mem.Allocator) !*Self { - const arch = Target.current.getArch(); - const llvm = @import("llvm.zig"); - const llvm_cpu_name = llvm.GetHostCPUName(); - const llvm_cpu_features = llvm.GetNativeFeatures(); - const cpu_features = try cpuFeaturesFromLLVM(arch, llvm_cpu_name, llvm_cpu_features); - return createFromCpuFeatures(allocator, arch, cpu_features); - } - - fn createFromCpuFeatures( - allocator: *mem.Allocator, - arch: Target.Arch, - cpu_features: Target.CpuFeatures, - ) !*Self { - const self = try allocator.create(Self); - errdefer allocator.destroy(self); - - const cache_hash = try std.fmt.allocPrint0(allocator, "{}\n{}", .{ - cpu_features.cpu.name, - cpu_features.features.asBytes(), - }); - errdefer allocator.free(cache_hash); - - const generic_arch_name = arch.genericName(); - var builtin_str_buffer = try std.Buffer.allocPrint(allocator, - \\CpuFeatures{{ - \\ .cpu = &Target.{}.cpu.{}, - \\ .features = Target.{}.featureSet(&[_]Target.{}.Feature{{ - \\ - , .{ - generic_arch_name, - cpu_features.cpu.name, - generic_arch_name, - generic_arch_name, - }); - defer builtin_str_buffer.deinit(); - - var llvm_features_buffer = try std.Buffer.initSize(allocator, 0); - defer llvm_features_buffer.deinit(); - - for (arch.allFeaturesList()) |feature, index_usize| { - const index = @intCast(Target.Cpu.Feature.Set.Index, index_usize); - const is_enabled = cpu_features.features.isEnabled(index); - - if (feature.llvm_name) |llvm_name| { - const plus_or_minus = "-+"[@boolToInt(is_enabled)]; - try llvm_features_buffer.appendByte(plus_or_minus); - try llvm_features_buffer.append(llvm_name); - try llvm_features_buffer.append(","); - } - - if (is_enabled) { - // TODO some kind of "zig identifier escape" function rather than - // unconditionally using @"" syntax - try builtin_str_buffer.append(" .@\""); - try builtin_str_buffer.append(feature.name); - try builtin_str_buffer.append("\",\n"); - } - } - - try builtin_str_buffer.append( - \\ }), - \\}; - \\ - ); - - assert(mem.endsWith(u8, llvm_features_buffer.toSliceConst(), ",")); - llvm_features_buffer.shrink(llvm_features_buffer.len() - 1); - - self.* = Self{ - .allocator = allocator, - .cpu_features = cpu_features, - .llvm_features_str = llvm_features_buffer.toOwnedSlice().ptr, - .builtin_str = builtin_str_buffer.toOwnedSlice(), - .cache_hash = cache_hash, - }; - return self; - } - - fn destroy(self: *Self) void { - self.allocator.free(self.cache_hash); - self.allocator.free(self.builtin_str); - // TODO if (self.llvm_features_str) |llvm_features_str| self.allocator.free(llvm_features_str); - self.allocator.destroy(self); - } -}; - -// ABI warning -export fn stage2_cpu_features_parse( - result: **Stage2CpuFeatures, - zig_triple: ?[*:0]const u8, - cpu_name: ?[*:0]const u8, - cpu_features: ?[*:0]const u8, -) Error { - result.* = stage2ParseCpuFeatures(zig_triple, cpu_name, cpu_features) catch |err| switch (err) { - error.OutOfMemory => return .OutOfMemory, - error.UnknownArchitecture => return .UnknownArchitecture, - error.UnknownSubArchitecture => return .UnknownSubArchitecture, - error.UnknownOperatingSystem => return .UnknownOperatingSystem, - error.UnknownApplicationBinaryInterface => return .UnknownApplicationBinaryInterface, - error.MissingOperatingSystem => return .MissingOperatingSystem, - error.MissingArchitecture => return .MissingArchitecture, - error.InvalidLlvmCpuFeaturesFormat => return .InvalidLlvmCpuFeaturesFormat, - error.InvalidCpuFeatures => return .InvalidCpuFeatures, - }; - return .None; -} - -fn stage2ParseCpuFeatures( - zig_triple_oz: ?[*:0]const u8, - cpu_name_oz: ?[*:0]const u8, - cpu_features_oz: ?[*:0]const u8, -) !*Stage2CpuFeatures { - const zig_triple_z = zig_triple_oz orelse return Stage2CpuFeatures.createFromNative(std.heap.c_allocator); - const target = try Target.parse(mem.toSliceConst(u8, zig_triple_z)); - const arch = target.Cross.arch; - - const cpu = if (cpu_name_oz) |cpu_name_z| blk: { - const cpu_name = mem.toSliceConst(u8, cpu_name_z); - break :blk arch.parseCpu(cpu_name) catch |err| switch (err) { - error.UnknownCpu => { - std.debug.warn("Unknown CPU: '{}'\nAvailable CPUs for architecture '{}':\n", .{ - cpu_name, - @tagName(arch), - }); - for (arch.allCpus()) |cpu| { - std.debug.warn(" {}\n", .{cpu.name}); - } - process.exit(1); - }, - else => |e| return e, - }; - } else target.Cross.cpu_features.cpu; - - var set = if (cpu_features_oz) |cpu_features_z| blk: { - const cpu_features = mem.toSliceConst(u8, cpu_features_z); - break :blk arch.parseCpuFeatureSet(cpu, cpu_features) catch |err| switch (err) { - error.UnknownCpuFeature => { - std.debug.warn( - \\Unknown CPU features specified. - \\Available CPU features for architecture '{}': - \\ - , .{@tagName(arch)}); - for (arch.allFeaturesList()) |feature| { - std.debug.warn(" {}\n", .{feature.name}); - } - process.exit(1); - }, - else => |e| return e, - }; - } else cpu.features; - - if (arch.subArchFeature()) |index| { - set.addFeature(index); - } - set.populateDependencies(arch.allFeaturesList()); - - return Stage2CpuFeatures.createFromCpuFeatures(std.heap.c_allocator, arch, .{ - .cpu = cpu, - .features = set, - }); -} - -// ABI warning -export fn stage2_cpu_features_get_cache_hash( - cpu_features: *const Stage2CpuFeatures, - ptr: *[*:0]const u8, - len: *usize, -) void { - ptr.* = cpu_features.cache_hash.ptr; - len.* = cpu_features.cache_hash.len; -} - -// ABI warning -export fn stage2_cpu_features_get_builtin_str( - cpu_features: *const Stage2CpuFeatures, - ptr: *[*:0]const u8, - len: *usize, -) void { - ptr.* = cpu_features.builtin_str.ptr; - len.* = cpu_features.builtin_str.len; -} - -// ABI warning -export fn stage2_cpu_features_get_llvm_cpu(cpu_features: *const Stage2CpuFeatures) ?[*:0]const u8 { - return if (cpu_features.cpu_features.cpu.llvm_name) |s| s.ptr else null; -} - -// ABI warning -export fn stage2_cpu_features_get_llvm_features(cpu_features: *const Stage2CpuFeatures) ?[*:0]const u8 { - return cpu_features.llvm_features_str; -} diff --git a/src-self-hosted/stage2.zig b/src-self-hosted/stage2.zig new file mode 100644 index 0000000000000000000000000000000000000000..37816464a1ea5a594db2686ec8a0fcebcd2646e4 --- /dev/null +++ b/src-self-hosted/stage2.zig @@ -0,0 +1,1118 @@ +// This is Zig code that is used by both stage1 and stage2. +// The prototypes in src/userland.h must match these definitions. + +const std = @import("std"); +const io = std.io; +const mem = std.mem; +const fs = std.fs; +const process = std.process; +const Allocator = mem.Allocator; +const ArrayList = std.ArrayList; +const Buffer = std.Buffer; +const Target = std.Target; +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; +var stdout: *io.OutStream(fs.File.WriteError) = undefined; + +comptime { + _ = @import("dep_tokenizer.zig"); +} + +// ABI warning +export fn stage2_zen(ptr: *[*]const u8, len: *usize) void { + const info_zen = @import("main.zig").info_zen; + ptr.* = info_zen; + len.* = info_zen.len; +} + +// ABI warning +export fn stage2_panic(ptr: [*]const u8, len: usize) void { + @panic(ptr[0..len]); +} + +// ABI warning +const Error = extern enum { + None, + OutOfMemory, + InvalidFormat, + SemanticAnalyzeFail, + AccessDenied, + Interrupted, + SystemResources, + FileNotFound, + FileSystem, + FileTooBig, + DivByZero, + Overflow, + PathAlreadyExists, + Unexpected, + ExactDivRemainder, + NegativeDenominator, + ShiftedOutOneBits, + CCompileErrors, + EndOfFile, + IsDir, + NotDir, + UnsupportedOperatingSystem, + SharingViolation, + PipeBusy, + PrimitiveTypeNotFound, + CacheUnavailable, + PathTooLong, + CCompilerCannotFindFile, + NoCCompilerInstalled, + ReadingDepFile, + InvalidDepFile, + MissingArchitecture, + MissingOperatingSystem, + UnknownArchitecture, + UnknownOperatingSystem, + UnknownABI, + InvalidFilename, + DiskQuota, + DiskSpace, + UnexpectedWriteFailure, + UnexpectedSeekFailure, + UnexpectedFileTruncationFailure, + Unimplemented, + OperationAborted, + BrokenPipe, + NoSpaceLeft, + NotLazy, + IsAsync, + ImportOutsidePkgPath, + UnknownCpu, + UnknownSubArchitecture, + UnknownCpuFeature, + InvalidCpuFeatures, + InvalidLlvmCpuFeaturesFormat, + UnknownApplicationBinaryInterface, + ASTUnitFailure, + BadPathName, + SymLinkLoop, + ProcessFdQuotaExceeded, + SystemFdQuotaExceeded, + NoDevice, + DeviceBusy, + UnableToSpawnCCompiler, + CCompilerExitCode, + CCompilerCrashed, + CCompilerCannotFindHeaders, + LibCRuntimeNotFound, + LibCStdLibHeaderNotFound, + LibCKernel32LibNotFound, + UnsupportedArchitecture, + WindowsSdkNotFound, + UnknownDynamicLinkerPath, + TargetHasNoDynamicLinker, +}; + +const FILE = std.c.FILE; +const ast = std.zig.ast; +const translate_c = @import("translate_c.zig"); + +/// Args should have a null terminating last arg. +export fn stage2_translate_c( + out_ast: **ast.Tree, + out_errors_ptr: *[*]translate_c.ClangErrMsg, + out_errors_len: *usize, + args_begin: [*]?[*]const u8, + args_end: [*]?[*]const u8, + resources_path: [*:0]const u8, +) Error { + var errors = @as([*]translate_c.ClangErrMsg, undefined)[0..0]; + out_ast.* = translate_c.translate(std.heap.c_allocator, args_begin, args_end, &errors, resources_path) catch |err| switch (err) { + error.SemanticAnalyzeFail => { + out_errors_ptr.* = errors.ptr; + out_errors_len.* = errors.len; + return .CCompileErrors; + }, + error.ASTUnitFailure => return .ASTUnitFailure, + error.OutOfMemory => return .OutOfMemory, + }; + return .None; +} + +export fn stage2_free_clang_errors(errors_ptr: [*]translate_c.ClangErrMsg, errors_len: usize) void { + translate_c.freeErrors(errors_ptr[0..errors_len]); +} + +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 .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 .None; +} + +// TODO: just use the actual self-hosted zig fmt. Until https://github.com/ziglang/zig/issues/2377, +// we use a blocking implementation. +export fn stage2_fmt(argc: c_int, argv: [*]const [*:0]const u8) c_int { + if (std.debug.runtime_safety) { + fmtMain(argc, argv) catch unreachable; + } else { + fmtMain(argc, argv) catch |e| { + std.debug.warn("{}\n", .{@errorName(e)}); + return -1; + }; + } + return 0; +} + +fn fmtMain(argc: c_int, argv: [*]const [*:0]const u8) !void { + const allocator = std.heap.c_allocator; + var args_list = std.ArrayList([]const u8).init(allocator); + const argc_usize = @intCast(usize, argc); + var arg_i: usize = 0; + while (arg_i < argc_usize) : (arg_i += 1) { + try args_list.append(mem.toSliceConst(u8, argv[arg_i])); + } + + stdout = &std.io.getStdOut().outStream().stream; + stderr_file = std.io.getStdErr(); + stderr = &stderr_file.outStream().stream; + + const args = args_list.toSliceConst()[2..]; + + var color: errmsg.Color = .Auto; + var stdin_flag: bool = false; + var check_flag: bool = false; + var input_files = ArrayList([]const u8).init(allocator); + + { + var i: usize = 0; + while (i < args.len) : (i += 1) { + const arg = args[i]; + if (mem.startsWith(u8, arg, "-")) { + if (mem.eql(u8, arg, "--help")) { + try stdout.write(self_hosted_main.usage_fmt); + process.exit(0); + } else if (mem.eql(u8, arg, "--color")) { + if (i + 1 >= args.len) { + try stderr.write("expected [auto|on|off] after --color\n"); + process.exit(1); + } + i += 1; + const next_arg = args[i]; + if (mem.eql(u8, next_arg, "auto")) { + color = .Auto; + } else if (mem.eql(u8, next_arg, "on")) { + color = .On; + } else if (mem.eql(u8, next_arg, "off")) { + color = .Off; + } else { + try stderr.print("expected [auto|on|off] after --color, found '{}'\n", .{next_arg}); + process.exit(1); + } + } else if (mem.eql(u8, arg, "--stdin")) { + stdin_flag = true; + } else if (mem.eql(u8, arg, "--check")) { + check_flag = true; + } else { + try stderr.print("unrecognized parameter: '{}'", .{arg}); + process.exit(1); + } + } else { + try input_files.append(arg); + } + } + } + + if (stdin_flag) { + if (input_files.len != 0) { + try stderr.write("cannot use --stdin with positional arguments\n"); + process.exit(1); + } + + const stdin_file = io.getStdIn(); + var stdin = stdin_file.inStream(); + + const source_code = try stdin.stream.readAllAlloc(allocator, self_hosted_main.max_src_size); + defer allocator.free(source_code); + + const tree = std.zig.parse(allocator, source_code) catch |err| { + try stderr.print("error parsing stdin: {}\n", .{err}); + process.exit(1); + }; + defer tree.deinit(); + + var error_it = tree.errors.iterator(0); + while (error_it.next()) |parse_error| { + try printErrMsgToFile(allocator, parse_error, tree, "", stderr_file, color); + } + if (tree.errors.len != 0) { + process.exit(1); + } + if (check_flag) { + const anything_changed = try std.zig.render(allocator, io.null_out_stream, tree); + const code = if (anything_changed) @as(u8, 1) else @as(u8, 0); + process.exit(code); + } + + _ = try std.zig.render(allocator, stdout, tree); + return; + } + + if (input_files.len == 0) { + try stderr.write("expected at least one source file argument\n"); + process.exit(1); + } + + var fmt = Fmt{ + .seen = Fmt.SeenMap.init(allocator), + .any_error = false, + .color = color, + .allocator = allocator, + }; + + for (input_files.toSliceConst()) |file_path| { + try fmtPath(&fmt, file_path, check_flag); + } + if (fmt.any_error) { + process.exit(1); + } +} + +const FmtError = error{ + SystemResources, + OperationAborted, + IoPending, + BrokenPipe, + Unexpected, + WouldBlock, + FileClosed, + DestinationAddressRequired, + DiskQuota, + FileTooBig, + InputOutput, + NoSpaceLeft, + AccessDenied, + OutOfMemory, + RenameAcrossMountPoints, + ReadOnlyFileSystem, + LinkQuotaExceeded, + FileBusy, +} || fs.File.OpenError; + +fn fmtPath(fmt: *Fmt, file_path: []const u8, check_mode: bool) FmtError!void { + if (fmt.seen.exists(file_path)) return; + try fmt.seen.put(file_path); + + const source_code = io.readFileAlloc(fmt.allocator, file_path) catch |err| switch (err) { + error.IsDir, error.AccessDenied => { + // TODO make event based (and dir.next()) + var dir = try fs.cwd().openDirList(file_path); + defer dir.close(); + + var dir_it = dir.iterate(); + + while (try dir_it.next()) |entry| { + if (entry.kind == .Directory or mem.endsWith(u8, entry.name, ".zig")) { + const full_path = try fs.path.join(fmt.allocator, &[_][]const u8{ file_path, entry.name }); + try fmtPath(fmt, full_path, check_mode); + } + } + return; + }, + else => { + // TODO lock stderr printing + try stderr.print("unable to open '{}': {}\n", .{ file_path, err }); + fmt.any_error = true; + return; + }, + }; + defer fmt.allocator.free(source_code); + + const tree = std.zig.parse(fmt.allocator, source_code) catch |err| { + try stderr.print("error parsing file '{}': {}\n", .{ file_path, err }); + fmt.any_error = true; + return; + }; + defer tree.deinit(); + + var error_it = tree.errors.iterator(0); + while (error_it.next()) |parse_error| { + try printErrMsgToFile(fmt.allocator, parse_error, tree, file_path, stderr_file, fmt.color); + } + if (tree.errors.len != 0) { + fmt.any_error = true; + return; + } + + if (check_mode) { + const anything_changed = try std.zig.render(fmt.allocator, io.null_out_stream, tree); + if (anything_changed) { + try stderr.print("{}\n", .{file_path}); + fmt.any_error = true; + } + } else { + const baf = try io.BufferedAtomicFile.create(fmt.allocator, file_path); + defer baf.destroy(); + + const anything_changed = try std.zig.render(fmt.allocator, baf.stream(), tree); + if (anything_changed) { + try stderr.print("{}\n", .{file_path}); + try baf.finish(); + } + } +} + +const Fmt = struct { + seen: SeenMap, + any_error: bool, + color: errmsg.Color, + allocator: *mem.Allocator, + + const SeenMap = std.BufSet; +}; + +fn printErrMsgToFile( + allocator: *mem.Allocator, + parse_error: *const ast.Error, + tree: *ast.Tree, + path: []const u8, + file: fs.File, + color: errmsg.Color, +) !void { + const color_on = switch (color) { + .Auto => file.isTty(), + .On => true, + .Off => false, + }; + const lok_token = parse_error.loc(); + const span = errmsg.Span{ + .first = lok_token, + .last = lok_token, + }; + + const first_token = tree.tokens.at(span.first); + const last_token = tree.tokens.at(span.last); + const start_loc = tree.tokenLocationPtr(0, first_token); + const end_loc = tree.tokenLocationPtr(first_token.end, last_token); + + var text_buf = try std.Buffer.initSize(allocator, 0); + var out_stream = &std.io.BufferOutStream.init(&text_buf).stream; + try parse_error.render(&tree.tokens, out_stream); + const text = text_buf.toOwnedSlice(); + + const stream = &file.outStream().stream; + try stream.print("{}:{}:{}: error: {}\n", .{ path, start_loc.line + 1, start_loc.column + 1, text }); + + if (!color_on) return; + + // Print \r and \t as one space each so that column counts line up + for (tree.source[start_loc.line_start..start_loc.line_end]) |byte| { + try stream.writeByte(switch (byte) { + '\r', '\t' => ' ', + else => byte, + }); + } + try stream.writeByte('\n'); + try stream.writeByteNTimes(' ', start_loc.column); + try stream.writeByteNTimes('~', last_token.end - first_token.start); + try stream.writeByte('\n'); +} + +export fn stage2_DepTokenizer_init(input: [*]const u8, len: usize) stage2_DepTokenizer { + const t = std.heap.c_allocator.create(DepTokenizer) catch @panic("failed to create .d tokenizer"); + t.* = DepTokenizer.init(std.heap.c_allocator, input[0..len]); + return stage2_DepTokenizer{ + .handle = t, + }; +} + +export fn stage2_DepTokenizer_deinit(self: *stage2_DepTokenizer) void { + self.handle.deinit(); +} + +export fn stage2_DepTokenizer_next(self: *stage2_DepTokenizer) stage2_DepNextResult { + const otoken = self.handle.next() catch { + const textz = std.Buffer.init(&self.handle.arena.allocator, self.handle.error_text) catch @panic("failed to create .d tokenizer error text"); + return stage2_DepNextResult{ + .type_id = .error_, + .textz = textz.toSlice().ptr, + }; + }; + const token = otoken orelse { + return stage2_DepNextResult{ + .type_id = .null_, + .textz = undefined, + }; + }; + const textz = std.Buffer.init(&self.handle.arena.allocator, token.bytes) catch @panic("failed to create .d tokenizer token text"); + return stage2_DepNextResult{ + .type_id = switch (token.id) { + .target => .target, + .prereq => .prereq, + }, + .textz = textz.toSlice().ptr, + }; +} + +const stage2_DepTokenizer = extern struct { + handle: *DepTokenizer, +}; + +const stage2_DepNextResult = extern struct { + type_id: TypeId, + + // when type_id == error --> error text + // when type_id == null --> undefined + // when type_id == target --> target pathname + // when type_id == prereq --> prereq pathname + textz: [*]const u8, + + const TypeId = extern enum { + error_, + null_, + target, + prereq, + }; +}; + +// ABI warning +export fn stage2_attach_segfault_handler() void { + if (std.debug.runtime_safety and std.debug.have_segfault_handling_support) { + std.debug.attachSegfaultHandler(); + } +} + +// ABI warning +export fn stage2_progress_create() *std.Progress { + const ptr = std.heap.c_allocator.create(std.Progress) catch @panic("out of memory"); + ptr.* = std.Progress{}; + return ptr; +} + +// ABI warning +export fn stage2_progress_destroy(progress: *std.Progress) void { + std.heap.c_allocator.destroy(progress); +} + +// ABI warning +export fn stage2_progress_start_root( + progress: *std.Progress, + name_ptr: [*]const u8, + name_len: usize, + estimated_total_items: usize, +) *std.Progress.Node { + return progress.start( + name_ptr[0..name_len], + if (estimated_total_items == 0) null else estimated_total_items, + ) catch @panic("timer unsupported"); +} + +// ABI warning +export fn stage2_progress_disable_tty(progress: *std.Progress) void { + progress.terminal = null; +} + +// ABI warning +export fn stage2_progress_start( + node: *std.Progress.Node, + name_ptr: [*]const u8, + name_len: usize, + estimated_total_items: usize, +) *std.Progress.Node { + const child_node = std.heap.c_allocator.create(std.Progress.Node) catch @panic("out of memory"); + child_node.* = node.start( + name_ptr[0..name_len], + if (estimated_total_items == 0) null else estimated_total_items, + ); + child_node.activate(); + return child_node; +} + +// ABI warning +export fn stage2_progress_end(node: *std.Progress.Node) void { + node.end(); + if (&node.context.root != node) { + std.heap.c_allocator.destroy(node); + } +} + +// ABI warning +export fn stage2_progress_complete_one(node: *std.Progress.Node) void { + node.completeOne(); +} + +// ABI warning +export fn stage2_progress_update_node(node: *std.Progress.Node, done_count: usize, total_count: usize) void { + node.completed_items = done_count; + node.estimated_total_items = total_count; + node.activate(); + node.context.maybeRefresh(); +} + +fn cpuFeaturesFromLLVM( + arch: Target.Arch, + llvm_cpu_name_z: ?[*:0]const u8, + llvm_cpu_features_opt: ?[*:0]const u8, +) !Target.CpuFeatures { + var result = arch.getBaselineCpuFeatures(); + + if (llvm_cpu_name_z) |cpu_name_z| { + const llvm_cpu_name = mem.toSliceConst(u8, cpu_name_z); + + for (arch.allCpus()) |cpu| { + const this_llvm_name = cpu.llvm_name orelse continue; + if (mem.eql(u8, this_llvm_name, llvm_cpu_name)) { + // Here we use the non-dependencies-populated set, + // so that subtracting features later in this function + // affect the prepopulated set. + result = Target.CpuFeatures{ + .cpu = cpu, + .features = cpu.features, + }; + break; + } + } + } + + const all_features = arch.allFeaturesList(); + + if (llvm_cpu_features_opt) |llvm_cpu_features| { + var it = mem.tokenize(mem.toSliceConst(u8, llvm_cpu_features), ","); + while (it.next()) |decorated_llvm_feat| { + var op: enum { + add, + sub, + } = undefined; + var llvm_feat: []const u8 = undefined; + if (mem.startsWith(u8, decorated_llvm_feat, "+")) { + op = .add; + llvm_feat = decorated_llvm_feat[1..]; + } else if (mem.startsWith(u8, decorated_llvm_feat, "-")) { + op = .sub; + llvm_feat = decorated_llvm_feat[1..]; + } else { + return error.InvalidLlvmCpuFeaturesFormat; + } + for (all_features) |feature, index_usize| { + const this_llvm_name = feature.llvm_name orelse continue; + if (mem.eql(u8, llvm_feat, this_llvm_name)) { + const index = @intCast(Target.Cpu.Feature.Set.Index, index_usize); + switch (op) { + .add => result.features.addFeature(index), + .sub => result.features.removeFeature(index), + } + break; + } + } + } + } + + result.features.populateDependencies(all_features); + return result; +} + +// ABI warning +export fn stage2_cmd_targets(zig_triple: [*:0]const u8) c_int { + cmdTargets(zig_triple) catch |err| { + std.debug.warn("unable to list targets: {}\n", .{@errorName(err)}); + return -1; + }; + return 0; +} + +fn cmdTargets(zig_triple: [*:0]const u8) !void { + var target = try Target.parse(mem.toSliceConst(u8, zig_triple)); + target.Cross.cpu_features = blk: { + const llvm = @import("llvm.zig"); + const llvm_cpu_name = llvm.GetHostCPUName(); + const llvm_cpu_features = llvm.GetNativeFeatures(); + break :blk try cpuFeaturesFromLLVM(target.Cross.arch, llvm_cpu_name, llvm_cpu_features); + }; + return @import("print_targets.zig").cmdTargets( + std.heap.c_allocator, + &[0][]u8{}, + &std.io.getStdOut().outStream().stream, + target, + ); +} + +const Stage2CpuFeatures = struct { + allocator: *mem.Allocator, + cpu_features: Target.CpuFeatures, + + llvm_features_str: ?[*:0]const u8, + + builtin_str: [:0]const u8, + cache_hash: [:0]const u8, + + const Self = @This(); + + fn createFromNative(allocator: *mem.Allocator) !*Self { + const arch = Target.current.getArch(); + const llvm = @import("llvm.zig"); + const llvm_cpu_name = llvm.GetHostCPUName(); + const llvm_cpu_features = llvm.GetNativeFeatures(); + const cpu_features = try cpuFeaturesFromLLVM(arch, llvm_cpu_name, llvm_cpu_features); + return createFromCpuFeatures(allocator, arch, cpu_features); + } + + fn createFromCpuFeatures( + allocator: *mem.Allocator, + arch: Target.Arch, + cpu_features: Target.CpuFeatures, + ) !*Self { + const self = try allocator.create(Self); + errdefer allocator.destroy(self); + + const cache_hash = try std.fmt.allocPrint0(allocator, "{}\n{}", .{ + cpu_features.cpu.name, + cpu_features.features.asBytes(), + }); + errdefer allocator.free(cache_hash); + + const generic_arch_name = arch.genericName(); + var builtin_str_buffer = try std.Buffer.allocPrint(allocator, + \\CpuFeatures{{ + \\ .cpu = &Target.{}.cpu.{}, + \\ .features = Target.{}.featureSet(&[_]Target.{}.Feature{{ + \\ + , .{ + generic_arch_name, + cpu_features.cpu.name, + generic_arch_name, + generic_arch_name, + }); + defer builtin_str_buffer.deinit(); + + var llvm_features_buffer = try std.Buffer.initSize(allocator, 0); + defer llvm_features_buffer.deinit(); + + for (arch.allFeaturesList()) |feature, index_usize| { + const index = @intCast(Target.Cpu.Feature.Set.Index, index_usize); + const is_enabled = cpu_features.features.isEnabled(index); + + if (feature.llvm_name) |llvm_name| { + const plus_or_minus = "-+"[@boolToInt(is_enabled)]; + try llvm_features_buffer.appendByte(plus_or_minus); + try llvm_features_buffer.append(llvm_name); + try llvm_features_buffer.append(","); + } + + if (is_enabled) { + // TODO some kind of "zig identifier escape" function rather than + // unconditionally using @"" syntax + try builtin_str_buffer.append(" .@\""); + try builtin_str_buffer.append(feature.name); + try builtin_str_buffer.append("\",\n"); + } + } + + try builtin_str_buffer.append( + \\ }), + \\}; + \\ + ); + + assert(mem.endsWith(u8, llvm_features_buffer.toSliceConst(), ",")); + llvm_features_buffer.shrink(llvm_features_buffer.len() - 1); + + self.* = Self{ + .allocator = allocator, + .cpu_features = cpu_features, + .llvm_features_str = llvm_features_buffer.toOwnedSlice().ptr, + .builtin_str = builtin_str_buffer.toOwnedSlice(), + .cache_hash = cache_hash, + }; + return self; + } + + fn destroy(self: *Self) void { + self.allocator.free(self.cache_hash); + self.allocator.free(self.builtin_str); + // TODO if (self.llvm_features_str) |llvm_features_str| self.allocator.free(llvm_features_str); + self.allocator.destroy(self); + } +}; + +// ABI warning +export fn stage2_cpu_features_parse( + result: **Stage2CpuFeatures, + zig_triple: ?[*:0]const u8, + cpu_name: ?[*:0]const u8, + cpu_features: ?[*:0]const u8, +) Error { + result.* = stage2ParseCpuFeatures(zig_triple, cpu_name, cpu_features) catch |err| switch (err) { + error.OutOfMemory => return .OutOfMemory, + error.UnknownArchitecture => return .UnknownArchitecture, + error.UnknownSubArchitecture => return .UnknownSubArchitecture, + error.UnknownOperatingSystem => return .UnknownOperatingSystem, + error.UnknownApplicationBinaryInterface => return .UnknownApplicationBinaryInterface, + error.MissingOperatingSystem => return .MissingOperatingSystem, + error.MissingArchitecture => return .MissingArchitecture, + error.InvalidLlvmCpuFeaturesFormat => return .InvalidLlvmCpuFeaturesFormat, + error.InvalidCpuFeatures => return .InvalidCpuFeatures, + }; + return .None; +} + +fn stage2ParseCpuFeatures( + zig_triple_oz: ?[*:0]const u8, + cpu_name_oz: ?[*:0]const u8, + cpu_features_oz: ?[*:0]const u8, +) !*Stage2CpuFeatures { + const zig_triple_z = zig_triple_oz orelse return Stage2CpuFeatures.createFromNative(std.heap.c_allocator); + const target = try Target.parse(mem.toSliceConst(u8, zig_triple_z)); + const arch = target.Cross.arch; + + const cpu = if (cpu_name_oz) |cpu_name_z| blk: { + const cpu_name = mem.toSliceConst(u8, cpu_name_z); + break :blk arch.parseCpu(cpu_name) catch |err| switch (err) { + error.UnknownCpu => { + std.debug.warn("Unknown CPU: '{}'\nAvailable CPUs for architecture '{}':\n", .{ + cpu_name, + @tagName(arch), + }); + for (arch.allCpus()) |cpu| { + std.debug.warn(" {}\n", .{cpu.name}); + } + process.exit(1); + }, + else => |e| return e, + }; + } else target.Cross.cpu_features.cpu; + + var set = if (cpu_features_oz) |cpu_features_z| blk: { + const cpu_features = mem.toSliceConst(u8, cpu_features_z); + break :blk arch.parseCpuFeatureSet(cpu, cpu_features) catch |err| switch (err) { + error.UnknownCpuFeature => { + std.debug.warn( + \\Unknown CPU features specified. + \\Available CPU features for architecture '{}': + \\ + , .{@tagName(arch)}); + for (arch.allFeaturesList()) |feature| { + std.debug.warn(" {}\n", .{feature.name}); + } + process.exit(1); + }, + else => |e| return e, + }; + } else cpu.features; + + if (arch.subArchFeature()) |index| { + set.addFeature(index); + } + set.populateDependencies(arch.allFeaturesList()); + + return Stage2CpuFeatures.createFromCpuFeatures(std.heap.c_allocator, arch, .{ + .cpu = cpu, + .features = set, + }); +} + +// ABI warning +export fn stage2_cpu_features_get_cache_hash( + cpu_features: *const Stage2CpuFeatures, + ptr: *[*:0]const u8, + len: *usize, +) void { + ptr.* = cpu_features.cache_hash.ptr; + len.* = cpu_features.cache_hash.len; +} + +// ABI warning +export fn stage2_cpu_features_get_builtin_str( + cpu_features: *const Stage2CpuFeatures, + ptr: *[*:0]const u8, + len: *usize, +) void { + ptr.* = cpu_features.builtin_str.ptr; + len.* = cpu_features.builtin_str.len; +} + +// ABI warning +export fn stage2_cpu_features_get_llvm_cpu(cpu_features: *const Stage2CpuFeatures) ?[*:0]const u8 { + return if (cpu_features.cpu_features.cpu.llvm_name) |s| s.ptr else null; +} + +// ABI warning +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, + error.WindowsSdkNotFound => return .WindowsSdkNotFound, + }; + 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 +const Stage2Target = extern struct { + arch: c_int, + sub_arch: c_int, + vendor: c_int, + os: c_int, + abi: c_int, + glibc_version: ?*Stage2GLibCVersion, // null means default + cpu_features: *Stage2CpuFeatures, + is_native: bool, +}; + +// ABI warning +const Stage2GLibCVersion = extern struct { + major: u32, + minor: u32, + patch: u32, +}; + +// ABI warning +export fn stage2_detect_dynamic_linker(in_target: *const Stage2Target, out_ptr: *[*:0]u8, out_len: *usize) Error { + const in_arch = in_target.arch - 1; // skip over ZigLLVM_UnknownArch + const in_sub_arch = in_target.sub_arch - 1; // skip over ZigLLVM_NoSubArch + const in_os = in_target.os; + const in_abi = in_target.abi - 1; // skip over ZigLLVM_UnknownEnvironment + const target: Target = if (in_target.is_native) .Native else .{ + .Cross = .{ + .arch = switch (enumInt(@TagType(Target.Arch), in_arch)) { + .arm => .{ .arm = enumInt(Target.Arch.Arm32, in_sub_arch) }, + .armeb => .{ .armeb = enumInt(Target.Arch.Arm32, in_sub_arch) }, + .thumb => .{ .thumb = enumInt(Target.Arch.Arm32, in_sub_arch) }, + .thumbeb => .{ .thumbeb = enumInt(Target.Arch.Arm32, in_sub_arch) }, + + .aarch64 => .{ .aarch64 = enumInt(Target.Arch.Arm64, in_sub_arch) }, + .aarch64_be => .{ .aarch64_be = enumInt(Target.Arch.Arm64, in_sub_arch) }, + .aarch64_32 => .{ .aarch64_32 = enumInt(Target.Arch.Arm64, in_sub_arch) }, + + .kalimba => .{ .kalimba = enumInt(Target.Arch.Kalimba, in_sub_arch) }, + + .arc => .arc, + .avr => .avr, + .bpfel => .bpfel, + .bpfeb => .bpfeb, + .hexagon => .hexagon, + .mips => .mips, + .mipsel => .mipsel, + .mips64 => .mips64, + .mips64el => .mips64el, + .msp430 => .msp430, + .powerpc => .powerpc, + .powerpc64 => .powerpc64, + .powerpc64le => .powerpc64le, + .r600 => .r600, + .amdgcn => .amdgcn, + .riscv32 => .riscv32, + .riscv64 => .riscv64, + .sparc => .sparc, + .sparcv9 => .sparcv9, + .sparcel => .sparcel, + .s390x => .s390x, + .tce => .tce, + .tcele => .tcele, + .i386 => .i386, + .x86_64 => .x86_64, + .xcore => .xcore, + .nvptx => .nvptx, + .nvptx64 => .nvptx64, + .le32 => .le32, + .le64 => .le64, + .amdil => .amdil, + .amdil64 => .amdil64, + .hsail => .hsail, + .hsail64 => .hsail64, + .spir => .spir, + .spir64 => .spir64, + .shave => .shave, + .lanai => .lanai, + .wasm32 => .wasm32, + .wasm64 => .wasm64, + .renderscript32 => .renderscript32, + .renderscript64 => .renderscript64, + }, + .os = enumInt(Target.Os, in_os), + .abi = enumInt(Target.Abi, in_abi), + .cpu_features = in_target.cpu_features.cpu_features, + }, + }; + const result = @import("introspect.zig").detectDynamicLinker( + std.heap.c_allocator, + target, + ) catch |err| switch (err) { + error.OutOfMemory => return .OutOfMemory, + error.UnknownDynamicLinkerPath => return .UnknownDynamicLinkerPath, + error.TargetHasNoDynamicLinker => return .TargetHasNoDynamicLinker, + }; + out_ptr.* = result.ptr; + out_len.* = result.len; + return .None; +} + +fn enumInt(comptime Enum: type, int: c_int) Enum { + return @intToEnum(Enum, @intCast(@TagType(Enum), int)); +} diff --git a/src-self-hosted/util.zig b/src-self-hosted/util.zig index 95bc72469d5a835e92449570f016bbf1d8b1d207..04c5420d26703c9d21c5838d7faa8ff409746610 100644 --- a/src-self-hosted/util.zig +++ b/src-self-hosted/util.zig @@ -2,143 +2,6 @@ const std = @import("std"); const Target = std.Target; const llvm = @import("llvm.zig"); -pub const FloatAbi = enum { - Hard, - Soft, - SoftFp, -}; - -/// TODO expose the arch and subarch separately -pub fn isArmOrThumb(self: Target) bool { - return switch (self.getArch()) { - .arm, - .armeb, - .aarch64, - .aarch64_be, - .thumb, - .thumbeb, - => true, - else => false, - }; -} - -pub fn getFloatAbi(self: Target) FloatAbi { - return switch (self.getAbi()) { - .gnueabihf, - .eabihf, - .musleabihf, - => .Hard, - else => .Soft, - }; -} - -pub fn getDynamicLinkerPath(self: Target) ?[]const u8 { - const env = self.getAbi(); - const arch = self.getArch(); - const os = self.getOs(); - switch (os) { - .freebsd => { - return "/libexec/ld-elf.so.1"; - }, - .linux => { - switch (env) { - .android => { - if (self.getArchPtrBitWidth() == 64) { - return "/system/bin/linker64"; - } else { - return "/system/bin/linker"; - } - }, - .gnux32 => { - if (arch == .x86_64) { - return "/libx32/ld-linux-x32.so.2"; - } - }, - .musl, - .musleabi, - .musleabihf, - => { - if (arch == .x86_64) { - return "/lib/ld-musl-x86_64.so.1"; - } - }, - else => {}, - } - switch (arch) { - .i386, - .sparc, - .sparcel, - => return "/lib/ld-linux.so.2", - - .aarch64 => return "/lib/ld-linux-aarch64.so.1", - - .aarch64_be => return "/lib/ld-linux-aarch64_be.so.1", - - .arm, - .thumb, - => return switch (getFloatAbi(self)) { - .Hard => return "/lib/ld-linux-armhf.so.3", - else => return "/lib/ld-linux.so.3", - }, - - .armeb, - .thumbeb, - => return switch (getFloatAbi(self)) { - .Hard => return "/lib/ld-linux-armhf.so.3", - else => return "/lib/ld-linux.so.3", - }, - - .mips, - .mipsel, - .mips64, - .mips64el, - => return null, - - .powerpc => return "/lib/ld.so.1", - .powerpc64 => return "/lib64/ld64.so.2", - .powerpc64le => return "/lib64/ld64.so.2", - .s390x => return "/lib64/ld64.so.1", - .sparcv9 => return "/lib64/ld-linux.so.2", - .x86_64 => return "/lib64/ld-linux-x86-64.so.2", - - .arc, - .avr, - .bpfel, - .bpfeb, - .hexagon, - .msp430, - .r600, - .amdgcn, - .riscv32, - .riscv64, - .tce, - .tcele, - .xcore, - .nvptx, - .nvptx64, - .le32, - .le64, - .amdil, - .amdil64, - .hsail, - .hsail64, - .spir, - .spir64, - .kalimba, - .shave, - .lanai, - .wasm32, - .wasm64, - .renderscript32, - .renderscript64, - .aarch64_32, - => return null, - } - }, - else => return null, - } -} - pub fn getDarwinArchString(self: Target) [:0]const u8 { const arch = self.getArch(); switch (arch) { diff --git a/src-self-hosted/windows_sdk.zig b/src-self-hosted/windows_sdk.zig new file mode 100644 index 0000000000000000000000000000000000000000..6dfdeb99fd0c2d0ba5f6e7934562a3a7b047bf32 --- /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 95f291b34315189622f8d47c675737dc83f4e955..c5804a3dc4a35d062315b2209fa8a9f9c4c72347 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/cache_hash.cpp b/src/cache_hash.cpp index 714b9b4c1861b2645e53adeabf5c686f20cdac8e..35b05a1ddf8be39afe0b4d24fad7b5c60925c766 100644 --- a/src/cache_hash.cpp +++ b/src/cache_hash.cpp @@ -5,7 +5,7 @@ * See http://opensource.org/licenses/MIT */ -#include "userland.h" +#include "stage2.h" #include "cache_hash.hpp" #include "all_types.hpp" #include "buffer.hpp" diff --git a/src/codegen.cpp b/src/codegen.cpp index a0bc56f093c4522038a92d56e27c49b4c18c3f12..41a353ef14586f3c74f123d2bac011a996f2c706 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -18,7 +18,7 @@ #include "target.hpp" #include "util.hpp" #include "zig_llvm.h" -#include "userland.h" +#include "stage2.h" #include "dump_analysis.hpp" #include "softfloat.hpp" #include "mem_profile.hpp" @@ -8375,9 +8375,11 @@ static bool detect_dynamic_link(CodeGen *g) { return true; if (g->zig_target->os == OsFreestanding) return false; - if (target_requires_pic(g->zig_target, g->libc_link_lib != nullptr)) + if (target_os_requires_libc(g->zig_target->os)) return true; - // If there are no dynamic libraries then we can disable PIC + if (g->libc_link_lib != nullptr && target_is_glibc(g->zig_target)) + return true; + // If there are no dynamic libraries then we can disable dynamic linking. for (size_t i = 0; i < g->link_libs_list.length; i += 1) { LinkLib *link_lib = g->link_libs_list.at(i); if (target_is_libc_lib_name(g->zig_target, buf_ptr(link_lib->name))) @@ -8624,7 +8626,7 @@ Buf *codegen_generate_builtin_source(CodeGen *g) { break; } buf_appendf(contents, "pub const output_mode = OutputMode.%s;\n", out_type); - const char *link_type = g->is_dynamic ? "Dynamic" : "Static"; + const char *link_type = g->have_dynamic_link ? "Dynamic" : "Static"; buf_appendf(contents, "pub const link_mode = LinkMode.%s;\n", link_type); buf_appendf(contents, "pub const is_test = %s;\n", bool_to_str(g->is_test_build)); buf_appendf(contents, "pub const single_threaded = %s;\n", bool_to_str(g->is_single_threaded)); @@ -8731,7 +8733,7 @@ static Error define_builtin_compile_vars(CodeGen *g) { cache_int(&cache_hash, g->build_mode); cache_bool(&cache_hash, g->strip_debug_symbols); cache_int(&cache_hash, g->out_type); - cache_bool(&cache_hash, g->is_dynamic); + cache_bool(&cache_hash, detect_dynamic_link(g)); cache_bool(&cache_hash, g->is_test_build); cache_bool(&cache_hash, g->is_single_threaded); cache_bool(&cache_hash, g->test_is_evented); @@ -8957,6 +8959,8 @@ static void init(CodeGen *g) { } static void detect_dynamic_linker(CodeGen *g) { + Error err; + if (g->dynamic_linker_path != nullptr) return; if (!g->have_dynamic_link) @@ -8964,42 +8968,16 @@ static void detect_dynamic_linker(CodeGen *g) { if (g->out_type == OutTypeObj || (g->out_type == OutTypeLib && !g->is_dynamic)) return; - const char *standard_ld_path = target_dynamic_linker(g->zig_target); - if (standard_ld_path == nullptr) - return; - - if (g->zig_target->is_native) { - // target_dynamic_linker is usually correct. However on some systems, such as NixOS - // it will be incorrect. See if we can do better by looking at what zig's own - // dynamic linker path is. - g->dynamic_linker_path = get_self_dynamic_linker_path(); - if (g->dynamic_linker_path != nullptr) - return; - - // If Zig is statically linked, such as via distributed binary static builds, the above - // trick won't work. What are we left with? Try to run the system C compiler and get - // it to tell us the dynamic linker path -#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))) { - 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; - return; - } - } -#endif + char *dynamic_linker_ptr; + size_t dynamic_linker_len; + if ((err = stage2_detect_dynamic_linker(g->zig_target, &dynamic_linker_ptr, &dynamic_linker_len))) { + if (err == ErrorTargetHasNoDynamicLinker) return; + fprintf(stderr, "Unable to detect dynamic linker: %s\n", err_str(err)); + exit(1); } - - g->dynamic_linker_path = buf_create_from_str(standard_ld_path); + g->dynamic_linker_path = buf_create_from_mem(dynamic_linker_ptr, dynamic_linker_len); + // Skips heap::c_allocator because the memory is allocated by stage2 library. + free(dynamic_linker_ptr); } static void detect_libc(CodeGen *g) { @@ -9028,16 +9006,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 +9060,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 +9081,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 +9091,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 +9187,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 +9645,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 +10461,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 +10744,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 +10783,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 b9bcf05d282d4a3605c0c7c98a9570eeffab374e..f8be29494a8cab83905d12ed6ef3dc05ceaacfac 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 "stage2.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/compiler.cpp b/src/compiler.cpp index 484a4ca08945622f286f5f668e887a373fa1d47d..31bac4ee24f7f5f215c19cbad56f467be9f1f075 100644 --- a/src/compiler.cpp +++ b/src/compiler.cpp @@ -4,20 +4,6 @@ #include -static Buf saved_dynamic_linker_path = BUF_INIT; -static bool searched_for_dyn_linker = false; - -static void detect_dynamic_linker(Buf *lib_path) { -#if defined(ZIG_OS_LINUX) - for (size_t i = 0; possible_ld_names[i] != NULL; i += 1) { - if (buf_ends_with_str(lib_path, possible_ld_names[i])) { - buf_init_from_buf(&saved_dynamic_linker_path, lib_path); - break; - } - } -#endif -} - Buf *get_self_libc_path(void) { static Buf saved_libc_path = BUF_INIT; static bool searched_for_libc = false; @@ -43,25 +29,6 @@ Buf *get_self_libc_path(void) { } } -Buf *get_self_dynamic_linker_path(void) { - for (;;) { - if (saved_dynamic_linker_path.list.length != 0) { - return &saved_dynamic_linker_path; - } - if (searched_for_dyn_linker) - return nullptr; - ZigList lib_paths = {}; - Error err; - if ((err = os_self_exe_shared_libs(lib_paths))) - return nullptr; - for (size_t i = 0; i < lib_paths.length; i += 1) { - Buf *lib_path = lib_paths.at(i); - detect_dynamic_linker(lib_path); - } - searched_for_dyn_linker = true; - } -} - Error get_compiler_id(Buf **result) { static Buf saved_compiler_id = BUF_INIT; @@ -98,7 +65,6 @@ Error get_compiler_id(Buf **result) { return err; for (size_t i = 0; i < lib_paths.length; i += 1) { Buf *lib_path = lib_paths.at(i); - detect_dynamic_linker(lib_path); if ((err = cache_add_file(ch, lib_path))) return err; } diff --git a/src/compiler.hpp b/src/compiler.hpp index ee9c4ab4fc58c024d24babfbd0f970c69bd26d13..4a1699b7821b64c34398ef23e0075d2b86fd0ec0 100644 --- a/src/compiler.hpp +++ b/src/compiler.hpp @@ -12,7 +12,6 @@ #include "error.hpp" Error get_compiler_id(Buf **result); -Buf *get_self_dynamic_linker_path(void); Buf *get_self_libc_path(void); Buf *get_zig_lib_dir(void); diff --git a/src/error.cpp b/src/error.cpp index 871b6b9e35d1838b98da75c5bae8a3fd42fc132e..c633e2fe672a2f4a7c3b2ef28308c7f5c7908bf6 100644 --- a/src/error.cpp +++ b/src/error.cpp @@ -65,6 +65,23 @@ 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"; + case ErrorWindowsSdkNotFound: return "Windows SDK not found"; + case ErrorUnknownDynamicLinkerPath: return "unknown dynamic linker path"; + case ErrorTargetHasNoDynamicLinker: return "target has no dynamic linker"; } return "(invalid error)"; } diff --git a/src/error.hpp b/src/error.hpp index 3ff36e1a5f224fa797db37a9686b597d984f702e..90772df10814b6f8ce452675803e9847658eb5cf 100644 --- a/src/error.hpp +++ b/src/error.hpp @@ -8,7 +8,7 @@ #ifndef ERROR_HPP #define ERROR_HPP -#include "userland.h" +#include "stage2.h" const char *err_str(Error err); diff --git a/src/libc_installation.cpp b/src/libc_installation.cpp deleted file mode 100644 index 2adc1cb69d07081db7c8112217c7b1d9b606f792..0000000000000000000000000000000000000000 --- 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 8ecad7ce612519135d452d0881c671ed2198f970..0000000000000000000000000000000000000000 --- 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 2771b694c1fb932b0737e60308a17eea6a446441..8b31b2519e45eca4243ee598cc070a7c4ae74387 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 2ff0ab73520e72e0cec7de9356aa3de3a414e161..cbaf6aeb7054a0e544b27b53a0e9dbf79606b5bc 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -14,8 +14,7 @@ #include "heap.hpp" #include "os.hpp" #include "target.hpp" -#include "libc_installation.hpp" -#include "userland.h" +#include "stage2.h" #include "glibc.hpp" #include "dump_analysis.hpp" #include "mem_profile.hpp" @@ -1004,9 +1003,22 @@ static int main0(int argc, char **argv) { return main_exit(root_progress_node, EXIT_FAILURE); } + // If both output_dir and enable_cache are provided, and doing build-lib, we + // will just do a file copy at the end. This helps when bootstrapping zig from zig0 + // because we want to pass something like this: + // zig0 build-lib --cache on --output-dir ${CMAKE_BINARY_DIR} + // And we don't have access to `zig0 build` because that would require detecting native libc + // on systems where we are not able to build a libc from source for them. + // But that's the only reason this works, so otherwise we give an error here. + Buf *final_output_dir_step = nullptr; if (output_dir != nullptr && enable_cache == CacheOptOn) { - fprintf(stderr, "`--output-dir` is incompatible with --cache on.\n"); - return print_error_usage(arg0); + if (cmd == CmdBuild && out_type == OutTypeLib) { + final_output_dir_step = output_dir; + output_dir = nullptr; + } else { + fprintf(stderr, "`--output-dir` is incompatible with --cache on.\n"); + return print_error_usage(arg0); + } } if (target_requires_pic(&target, have_libc) && want_pic == WantPICDisabled) { @@ -1027,15 +1039,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 +1144,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); } @@ -1284,8 +1303,21 @@ static int main0(int argc, char **argv) { #if defined(ZIG_OS_WINDOWS) buf_replace(&g->output_file_path, '/', '\\'); #endif - if (printf("%s\n", buf_ptr(&g->output_file_path)) < 0) - return main_exit(root_progress_node, EXIT_FAILURE); + if (final_output_dir_step != nullptr) { + Buf *dest_basename = buf_alloc(); + os_path_split(&g->output_file_path, nullptr, dest_basename); + Buf *dest_path = buf_alloc(); + os_path_join(final_output_dir_step, dest_basename, dest_path); + + if ((err = os_update_file(&g->output_file_path, dest_path))) { + fprintf(stderr, "unable to copy %s to %s: %s\n", buf_ptr(&g->output_file_path), + buf_ptr(dest_path), err_str(err)); + return main_exit(root_progress_node, EXIT_FAILURE); + } + } else { + if (printf("%s\n", buf_ptr(&g->output_file_path)) < 0) + return main_exit(root_progress_node, EXIT_FAILURE); + } } return main_exit(root_progress_node, EXIT_SUCCESS); } else { diff --git a/src/os.cpp b/src/os.cpp index 3eeab2b755f1dab877c7293c03de524279306d6f..268e8120502d10a67d119ec91e17f26a4944acdf 100644 --- a/src/os.cpp +++ b/src/os.cpp @@ -826,7 +826,9 @@ static Error os_exec_process_posix(ZigList &args, if (errno == ENOENT) { report_err = ErrorFileNotFound; } - write(err_pipe[1], &report_err, sizeof(Error)); + if (write(err_pipe[1], &report_err, sizeof(Error)) == -1) { + zig_panic("write failed"); + } exit(1); } else { // parent @@ -851,9 +853,13 @@ static Error os_exec_process_posix(ZigList &args, if (err2) return err2; Error child_err = ErrorNone; - write(err_pipe[1], &child_err, sizeof(Error)); + if (write(err_pipe[1], &child_err, sizeof(Error)) == -1) { + zig_panic("write failed"); + } close(err_pipe[1]); - read(err_pipe[0], &child_err, sizeof(Error)); + if (read(err_pipe[0], &child_err, sizeof(Error)) == -1) { + zig_panic("write failed"); + } close(err_pipe[0]); return child_err; } @@ -1029,58 +1035,156 @@ Error os_write_file(Buf *full_path, Buf *contents) { return ErrorNone; } -Error os_copy_file(Buf *src_path, Buf *dest_path) { - FILE *src_f = fopen(buf_ptr(src_path), "rb"); - if (!src_f) { - int err = errno; - if (err == ENOENT) { - return ErrorFileNotFound; - } else if (err == EACCES || err == EPERM) { - return ErrorAccess; - } else { - return ErrorFileSystem; - } - } - FILE *dest_f = fopen(buf_ptr(dest_path), "wb"); - if (!dest_f) { - int err = errno; - if (err == ENOENT) { - fclose(src_f); - return ErrorFileNotFound; - } else if (err == EACCES || err == EPERM) { - fclose(src_f); - return ErrorAccess; - } else { - fclose(src_f); - return ErrorFileSystem; - } - } - +static Error copy_open_files(FILE *src_f, FILE *dest_f) { static const size_t buf_size = 2048; char buf[buf_size]; for (;;) { size_t amt_read = fread(buf, 1, buf_size, src_f); if (amt_read != buf_size) { if (ferror(src_f)) { - fclose(src_f); - fclose(dest_f); return ErrorFileSystem; } } size_t amt_written = fwrite(buf, 1, amt_read, dest_f); if (amt_written != amt_read) { - fclose(src_f); - fclose(dest_f); return ErrorFileSystem; } if (feof(src_f)) { - fclose(src_f); - fclose(dest_f); return ErrorNone; } } } +#if defined(ZIG_OS_WINDOWS) +static void windows_filetime_to_os_timestamp(FILETIME *ft, OsTimeStamp *mtime) { + mtime->sec = (((ULONGLONG) ft->dwHighDateTime) << 32) + ft->dwLowDateTime; + mtime->nsec = 0; +} +static FILETIME windows_os_timestamp_to_filetime(OsTimeStamp mtime) { + FILETIME result; + result.dwHighDateTime = mtime.sec >> 32; + result.dwLowDateTime = mtime.sec; + return result; +} +#endif + +static Error set_file_times(OsFile file, OsTimeStamp ts) { +#if defined(ZIG_OS_WINDOWS) + FILETIME ft = windows_os_timestamp_to_filetime(ts); + if (SetFileTime(file, nullptr, &ft, &ft) == 0) { + return ErrorUnexpected; + } + return ErrorNone; +#else + struct timespec times[2] = { + { ts.sec, ts.nsec }, + { ts.sec, ts.nsec }, + }; + if (futimens(file, times) == -1) { + switch (errno) { + case EBADF: + zig_panic("futimens EBADF"); + default: + return ErrorUnexpected; + } + } + return ErrorNone; +#endif +} + +Error os_update_file(Buf *src_path, Buf *dst_path) { + Error err; + + OsFile src_file; + OsFileAttr src_attr; + if ((err = os_file_open_r(src_path, &src_file, &src_attr))) { + return err; + } + + OsFile dst_file; + OsFileAttr dst_attr; + if ((err = os_file_open_w(dst_path, &dst_file, &dst_attr, src_attr.mode))) { + os_file_close(&src_file); + return err; + } + + if (src_attr.size == dst_attr.size && + src_attr.mode == dst_attr.mode && + src_attr.mtime.sec == dst_attr.mtime.sec && + src_attr.mtime.nsec == dst_attr.mtime.nsec) + { + os_file_close(&src_file); + os_file_close(&dst_file); + return ErrorNone; + } +#if defined(ZIG_OS_WINDOWS) + if (SetEndOfFile(dst_file) == 0) { + return ErrorUnexpected; + } +#else + if (ftruncate(dst_file, 0) == -1) { + return ErrorUnexpected; + } +#endif +#if defined(ZIG_OS_WINDOWS) + FILE *src_libc_file = _fdopen(_open_osfhandle((intptr_t)src_file, _O_RDONLY), "rb"); + FILE *dst_libc_file = _fdopen(_open_osfhandle((intptr_t)dst_file, 0), "wb"); +#else + FILE *src_libc_file = fdopen(src_file, "rb"); + FILE *dst_libc_file = fdopen(dst_file, "wb"); +#endif + assert(src_libc_file); + assert(dst_libc_file); + + if ((err = copy_open_files(src_libc_file, dst_libc_file))) { + fclose(src_libc_file); + fclose(dst_libc_file); + return err; + } + if (fflush(src_libc_file) == -1) { + return ErrorUnexpected; + } + if (fflush(dst_libc_file) == -1) { + return ErrorUnexpected; + } + err = set_file_times(dst_file, src_attr.mtime); + fclose(src_libc_file); + fclose(dst_libc_file); + return err; +} + +Error os_copy_file(Buf *src_path, Buf *dest_path) { + FILE *src_f = fopen(buf_ptr(src_path), "rb"); + if (!src_f) { + int err = errno; + if (err == ENOENT) { + return ErrorFileNotFound; + } else if (err == EACCES || err == EPERM) { + return ErrorAccess; + } else { + return ErrorFileSystem; + } + } + FILE *dest_f = fopen(buf_ptr(dest_path), "wb"); + if (!dest_f) { + int err = errno; + if (err == ENOENT) { + fclose(src_f); + return ErrorFileNotFound; + } else if (err == EACCES || err == EPERM) { + fclose(src_f); + return ErrorAccess; + } else { + fclose(src_f); + return ErrorFileSystem; + } + } + Error err = copy_open_files(src_f, dest_f); + fclose(src_f); + fclose(dest_f); + return err; +} + Error os_fetch_file_path(Buf *full_path, Buf *out_contents) { FILE *f = fopen(buf_ptr(full_path), "rb"); if (!f) { @@ -1218,13 +1322,6 @@ Error os_rename(Buf *src_path, Buf *dest_path) { return ErrorNone; } -#if defined(ZIG_OS_WINDOWS) -static void windows_filetime_to_os_timestamp(FILETIME *ft, OsTimeStamp *mtime) { - mtime->sec = (((ULONGLONG) ft->dwHighDateTime) << 32) + ft->dwLowDateTime; - mtime->nsec = 0; -} -#endif - OsTimeStamp os_timestamp_calendar(void) { OsTimeStamp result; #if defined(ZIG_OS_WINDOWS) @@ -1551,108 +1648,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 { @@ -1835,10 +1830,15 @@ Error os_self_exe_shared_libs(ZigList &paths) { #endif } -Error os_file_open_r(Buf *full_path, OsFile *out_file, OsFileAttr *attr) { +Error os_file_open_rw(Buf *full_path, OsFile *out_file, OsFileAttr *attr, bool need_write, uint32_t mode) { #if defined(ZIG_OS_WINDOWS) // TODO use CreateFileW - HANDLE result = CreateFileA(buf_ptr(full_path), GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr); + HANDLE result = CreateFileA(buf_ptr(full_path), + need_write ? (GENERIC_READ|GENERIC_WRITE) : GENERIC_READ, + need_write ? 0 : FILE_SHARE_READ, + nullptr, + need_write ? OPEN_ALWAYS : OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, nullptr); if (result == INVALID_HANDLE_VALUE) { DWORD err = GetLastError(); @@ -1871,12 +1871,15 @@ Error os_file_open_r(Buf *full_path, OsFile *out_file, OsFileAttr *attr) { } windows_filetime_to_os_timestamp(&file_info.ftLastWriteTime, &attr->mtime); attr->inode = (((uint64_t)file_info.nFileIndexHigh) << 32) | file_info.nFileIndexLow; + attr->mode = 0; + attr->size = (((uint64_t)file_info.nFileSizeHigh) << 32) | file_info.nFileSizeLow; } return ErrorNone; #else for (;;) { - int fd = open(buf_ptr(full_path), O_RDONLY|O_CLOEXEC); + int fd = open(buf_ptr(full_path), + need_write ? (O_RDWR|O_CLOEXEC|O_CREAT) : (O_RDONLY|O_CLOEXEC), mode); if (fd == -1) { switch (errno) { case EINTR: @@ -1886,6 +1889,7 @@ Error os_file_open_r(Buf *full_path, OsFile *out_file, OsFileAttr *attr) { case EFAULT: zig_unreachable(); case EACCES: + case EPERM: return ErrorAccess; case EISDIR: return ErrorIsDir; @@ -1915,12 +1919,22 @@ Error os_file_open_r(Buf *full_path, OsFile *out_file, OsFileAttr *attr) { attr->mtime.sec = statbuf.st_mtim.tv_sec; attr->mtime.nsec = statbuf.st_mtim.tv_nsec; #endif + attr->mode = statbuf.st_mode; + attr->size = statbuf.st_size; } return ErrorNone; } #endif } +Error os_file_open_r(Buf *full_path, OsFile *out_file, OsFileAttr *attr) { + return os_file_open_rw(full_path, out_file, attr, false, 0); +} + +Error os_file_open_w(Buf *full_path, OsFile *out_file, OsFileAttr *attr, uint32_t mode) { + return os_file_open_rw(full_path, out_file, attr, true, mode); +} + Error os_file_open_lock_rw(Buf *full_path, OsFile *out_file) { #if defined(ZIG_OS_WINDOWS) for (;;) { @@ -1966,6 +1980,7 @@ Error os_file_open_lock_rw(Buf *full_path, OsFile *out_file) { case EFAULT: zig_unreachable(); case EACCES: + case EPERM: return ErrorAccess; case EISDIR: return ErrorIsDir; @@ -2114,21 +2129,3 @@ void os_file_close(OsFile *file) { *file = -1; #endif } - -#ifdef ZIG_OS_LINUX -const char *possible_ld_names[] = { -#if defined(ZIG_ARCH_X86_64) - "ld-linux-x86-64.so.2", - "ld-musl-x86_64.so.1", -#elif defined(ZIG_ARCH_ARM64) - "ld-linux-aarch64.so.1", - "ld-musl-aarch64.so.1", -#elif defined(ZIG_ARCH_ARM) - "ld-linux-armhf.so.3", - "ld-musl-armhf.so.1", - "ld-linux.so.3", - "ld-musl-arm.so.1", -#endif - NULL, -}; -#endif diff --git a/src/os.hpp b/src/os.hpp index 7da846a509ad19c51284bb93e35ca831f1202499..116861e8b575f7b271d31ddbdbb0a34ee57dba2b 100644 --- a/src/os.hpp +++ b/src/os.hpp @@ -43,10 +43,6 @@ #define ZIG_ARCH_UNKNOWN #endif -#ifdef ZIG_OS_LINUX -extern const char *possible_ld_names[]; -#endif - #if defined(ZIG_OS_WINDOWS) #define ZIG_PRI_usize "I64u" #define ZIG_PRI_i64 "I64d" @@ -93,13 +89,15 @@ struct Termination { #endif struct OsTimeStamp { - uint64_t sec; - uint64_t nsec; + int64_t sec; + int64_t nsec; }; struct OsFileAttr { OsTimeStamp mtime; + uint64_t size; uint64_t inode; + uint32_t mode; }; int os_init(void); @@ -121,6 +119,7 @@ Error ATTRIBUTE_MUST_USE os_make_path(Buf *path); Error ATTRIBUTE_MUST_USE os_make_dir(Buf *path); Error ATTRIBUTE_MUST_USE os_file_open_r(Buf *full_path, OsFile *out_file, OsFileAttr *attr); +Error ATTRIBUTE_MUST_USE os_file_open_w(Buf *full_path, OsFile *out_file, OsFileAttr *attr, uint32_t mode); Error ATTRIBUTE_MUST_USE os_file_open_lock_rw(Buf *full_path, OsFile *out_file); Error ATTRIBUTE_MUST_USE os_file_read(OsFile file, void *ptr, size_t *len); Error ATTRIBUTE_MUST_USE os_file_read_all(OsFile file, Buf *contents); @@ -129,6 +128,7 @@ void os_file_close(OsFile *file); Error ATTRIBUTE_MUST_USE os_write_file(Buf *full_path, Buf *contents); Error ATTRIBUTE_MUST_USE os_copy_file(Buf *src_path, Buf *dest_path); +Error ATTRIBUTE_MUST_USE os_update_file(Buf *src_path, Buf *dest_path); Error ATTRIBUTE_MUST_USE os_fetch_file(FILE *file, Buf *out_contents); Error ATTRIBUTE_MUST_USE os_fetch_file_path(Buf *full_path, Buf *out_contents); @@ -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/stage2.cpp b/src/stage2.cpp new file mode 100644 index 0000000000000000000000000000000000000000..7f68321752abece1e955f50a6ad386d1e433268f --- /dev/null +++ b/src/stage2.cpp @@ -0,0 +1,177 @@ +// This file is a shim for zig1. The real implementations of these are in +// src-self-hosted/stage1.zig + +#include "stage2.h" +#include "util.hpp" +#include "zig_llvm.h" +#include +#include +#include + +Error stage2_translate_c(struct Stage2Ast **out_ast, + struct Stage2ErrorMsg **out_errors_ptr, size_t *out_errors_len, + const char **args_begin, const char **args_end, const char *resources_path) +{ + const char *msg = "stage0 called stage2_translate_c"; + stage2_panic(msg, strlen(msg)); +} + +void stage2_free_clang_errors(struct Stage2ErrorMsg *ptr, size_t len) { + const char *msg = "stage0 called stage2_free_clang_errors"; + stage2_panic(msg, strlen(msg)); +} + +void stage2_zen(const char **ptr, size_t *len) { + const char *msg = "stage0 called stage2_zen"; + stage2_panic(msg, strlen(msg)); +} + +void stage2_attach_segfault_handler(void) { } + +void stage2_panic(const char *ptr, size_t len) { + fwrite(ptr, 1, len, stderr); + fprintf(stderr, "\n"); + fflush(stderr); + abort(); +} + +void stage2_render_ast(struct Stage2Ast *ast, FILE *output_file) { + const char *msg = "stage0 called stage2_render_ast"; + stage2_panic(msg, strlen(msg)); +} + +int stage2_fmt(int argc, char **argv) { + const char *msg = "stage0 called stage2_fmt"; + stage2_panic(msg, strlen(msg)); +} + +stage2_DepTokenizer stage2_DepTokenizer_init(const char *input, size_t len) { + const char *msg = "stage0 called stage2_DepTokenizer_init"; + stage2_panic(msg, strlen(msg)); +} + +void stage2_DepTokenizer_deinit(stage2_DepTokenizer *self) { + const char *msg = "stage0 called stage2_DepTokenizer_deinit"; + stage2_panic(msg, strlen(msg)); +} + +stage2_DepNextResult stage2_DepTokenizer_next(stage2_DepTokenizer *self) { + const char *msg = "stage0 called stage2_DepTokenizer_next"; + stage2_panic(msg, strlen(msg)); +} + + +struct Stage2Progress { + int trash; +}; + +struct Stage2ProgressNode { + int trash; +}; + +Stage2Progress *stage2_progress_create(void) { + return nullptr; +} + +void stage2_progress_destroy(Stage2Progress *progress) {} + +Stage2ProgressNode *stage2_progress_start_root(Stage2Progress *progress, + const char *name_ptr, size_t name_len, size_t estimated_total_items) +{ + return nullptr; +} +Stage2ProgressNode *stage2_progress_start(Stage2ProgressNode *node, + const char *name_ptr, size_t name_len, size_t estimated_total_items) +{ + return nullptr; +} +void stage2_progress_end(Stage2ProgressNode *node) {} +void stage2_progress_complete_one(Stage2ProgressNode *node) {} +void stage2_progress_disable_tty(Stage2Progress *progress) {} +void stage2_progress_update_node(Stage2ProgressNode *node, size_t completed_count, size_t estimated_total_items){} + +struct Stage2CpuFeatures { + const char *llvm_cpu_name; + const char *llvm_cpu_features; + const char *builtin_str; + const char *cache_hash; +}; + +Error stage2_cpu_features_parse(struct Stage2CpuFeatures **out, const char *zig_triple, + const char *cpu_name, const char *cpu_features) +{ + if (zig_triple == nullptr) { + Stage2CpuFeatures *result = heap::c_allocator.create(); + result->llvm_cpu_name = ZigLLVMGetHostCPUName(); + result->llvm_cpu_features = ZigLLVMGetNativeFeatures(); + result->builtin_str = "arch.getBaselineCpuFeatures();\n"; + result->cache_hash = "native\n\n"; + *out = result; + return ErrorNone; + } + if (cpu_name == nullptr && cpu_features == nullptr) { + Stage2CpuFeatures *result = heap::c_allocator.create(); + result->builtin_str = "arch.getBaselineCpuFeatures();\n"; + result->cache_hash = "\n\n"; + *out = result; + return ErrorNone; + } + + const char *msg = "stage0 called stage2_cpu_features_parse with non-null cpu name or features"; + stage2_panic(msg, strlen(msg)); +} + +void stage2_cpu_features_get_cache_hash(const Stage2CpuFeatures *cpu_features, + const char **ptr, size_t *len) +{ + *ptr = cpu_features->cache_hash; + *len = strlen(cpu_features->cache_hash); +} +const char *stage2_cpu_features_get_llvm_cpu(const Stage2CpuFeatures *cpu_features) { + return cpu_features->llvm_cpu_name; +} +const char *stage2_cpu_features_get_llvm_features(const Stage2CpuFeatures *cpu_features) { + return cpu_features->llvm_cpu_features; +} +void stage2_cpu_features_get_builtin_str(const Stage2CpuFeatures *cpu_features, + const char **ptr, size_t *len) +{ + *ptr = cpu_features->builtin_str; + *len = strlen(cpu_features->builtin_str); +} + +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) { + libc->include_dir = "/dummy/include"; + libc->include_dir_len = strlen(libc->include_dir); + libc->sys_include_dir = "/dummy/sys/include"; + libc->sys_include_dir_len = strlen(libc->sys_include_dir); + libc->crt_dir = ""; + libc->crt_dir_len = strlen(libc->crt_dir); + libc->static_crt_dir = ""; + libc->static_crt_dir_len = strlen(libc->static_crt_dir); + libc->msvc_lib_dir = ""; + libc->msvc_lib_dir_len = strlen(libc->msvc_lib_dir); + libc->kernel32_lib_dir = ""; + libc->kernel32_lib_dir_len = strlen(libc->kernel32_lib_dir); + return ErrorNone; +} + +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_detect_dynamic_linker(const struct ZigTarget *target, char **out_ptr, size_t *out_len) { + const char *msg = "stage0 called stage2_detect_dynamic_linker"; + stage2_panic(msg, strlen(msg)); +} diff --git a/src/stage2.h b/src/stage2.h new file mode 100644 index 0000000000000000000000000000000000000000..b6e4cebbaf2abc6d8d4b552ce9afa36342dd74eb --- /dev/null +++ b/src/stage2.h @@ -0,0 +1,315 @@ +/* + * Copyright (c) 2019 Andrew Kelley + * + * This file is part of zig, which is MIT licensed. + * See http://opensource.org/licenses/MIT + */ + +#ifndef ZIG_STAGE2_H +#define ZIG_STAGE2_H + +#include +#include +#include + +#include "zig_llvm.h" + +#ifdef __cplusplus +#define ZIG_EXTERN_C extern "C" +#else +#define ZIG_EXTERN_C +#endif + +#if defined(_MSC_VER) +#define ZIG_ATTRIBUTE_NORETURN __declspec(noreturn) +#else +#define ZIG_ATTRIBUTE_NORETURN __attribute__((noreturn)) +#endif + +// ABI warning: the types and declarations in this file must match both those in +// stage2.cpp and src-self-hosted/stage2.zig. + +// ABI warning +enum Error { + ErrorNone, + ErrorNoMem, + ErrorInvalidFormat, + ErrorSemanticAnalyzeFail, + ErrorAccess, + ErrorInterrupted, + ErrorSystemResources, + ErrorFileNotFound, + ErrorFileSystem, + ErrorFileTooBig, + ErrorDivByZero, + ErrorOverflow, + ErrorPathAlreadyExists, + ErrorUnexpected, + ErrorExactDivRemainder, + ErrorNegativeDenominator, + ErrorShiftedOutOneBits, + ErrorCCompileErrors, + ErrorEndOfFile, + ErrorIsDir, + ErrorNotDir, + ErrorUnsupportedOperatingSystem, + ErrorSharingViolation, + ErrorPipeBusy, + ErrorPrimitiveTypeNotFound, + ErrorCacheUnavailable, + ErrorPathTooLong, + ErrorCCompilerCannotFindFile, + ErrorNoCCompilerInstalled, + ErrorReadingDepFile, + ErrorInvalidDepFile, + ErrorMissingArchitecture, + ErrorMissingOperatingSystem, + ErrorUnknownArchitecture, + ErrorUnknownOperatingSystem, + ErrorUnknownABI, + ErrorInvalidFilename, + ErrorDiskQuota, + ErrorDiskSpace, + ErrorUnexpectedWriteFailure, + ErrorUnexpectedSeekFailure, + ErrorUnexpectedFileTruncationFailure, + ErrorUnimplemented, + ErrorOperationAborted, + ErrorBrokenPipe, + ErrorNoSpaceLeft, + ErrorNotLazy, + ErrorIsAsync, + ErrorImportOutsidePkgPath, + ErrorUnknownCpu, + ErrorUnknownSubArchitecture, + ErrorUnknownCpuFeature, + ErrorInvalidCpuFeatures, + ErrorInvalidLlvmCpuFeaturesFormat, + ErrorUnknownApplicationBinaryInterface, + ErrorASTUnitFailure, + ErrorBadPathName, + ErrorSymLinkLoop, + ErrorProcessFdQuotaExceeded, + ErrorSystemFdQuotaExceeded, + ErrorNoDevice, + ErrorDeviceBusy, + ErrorUnableToSpawnCCompiler, + ErrorCCompilerExitCode, + ErrorCCompilerCrashed, + ErrorCCompilerCannotFindHeaders, + ErrorLibCRuntimeNotFound, + ErrorLibCStdLibHeaderNotFound, + ErrorLibCKernel32LibNotFound, + ErrorUnsupportedArchitecture, + ErrorWindowsSdkNotFound, + ErrorUnknownDynamicLinkerPath, + ErrorTargetHasNoDynamicLinker, +}; + +// ABI warning +struct Stage2ErrorMsg { + const char *filename_ptr; // can be null + size_t filename_len; + const char *msg_ptr; + size_t msg_len; + const char *source; // valid until the ASTUnit is freed. can be null + unsigned line; // 0 based + unsigned column; // 0 based + unsigned offset; // byte offset into source +}; + +// ABI warning +struct Stage2Ast; + +// ABI warning +ZIG_EXTERN_C enum Error stage2_translate_c(struct Stage2Ast **out_ast, + struct Stage2ErrorMsg **out_errors_ptr, size_t *out_errors_len, + const char **args_begin, const char **args_end, const char *resources_path); + +// ABI warning +ZIG_EXTERN_C void stage2_free_clang_errors(struct Stage2ErrorMsg *ptr, size_t len); + +// ABI warning +ZIG_EXTERN_C void stage2_render_ast(struct Stage2Ast *ast, FILE *output_file); + +// ABI warning +ZIG_EXTERN_C void stage2_zen(const char **ptr, size_t *len); + +// ABI warning +ZIG_EXTERN_C void stage2_attach_segfault_handler(void); + +// ABI warning +ZIG_EXTERN_C ZIG_ATTRIBUTE_NORETURN void stage2_panic(const char *ptr, size_t len); + +// ABI warning +ZIG_EXTERN_C int stage2_fmt(int argc, char **argv); + +// ABI warning +struct stage2_DepTokenizer { + void *handle; +}; + +// ABI warning +struct stage2_DepNextResult { + enum TypeId { + error, + null, + target, + prereq, + }; + + TypeId type_id; + + // when ent == error --> error text + // when ent == null --> undefined + // when ent == target --> target pathname + // when ent == prereq --> prereq pathname + const char *textz; +}; + +// ABI warning +ZIG_EXTERN_C stage2_DepTokenizer stage2_DepTokenizer_init(const char *input, size_t len); + +// ABI warning +ZIG_EXTERN_C void stage2_DepTokenizer_deinit(stage2_DepTokenizer *self); + +// ABI warning +ZIG_EXTERN_C stage2_DepNextResult stage2_DepTokenizer_next(stage2_DepTokenizer *self); + +// ABI warning +struct Stage2Progress; +// ABI warning +struct Stage2ProgressNode; +// ABI warning +ZIG_EXTERN_C Stage2Progress *stage2_progress_create(void); +// ABI warning +ZIG_EXTERN_C void stage2_progress_disable_tty(Stage2Progress *progress); +// ABI warning +ZIG_EXTERN_C void stage2_progress_destroy(Stage2Progress *progress); +// ABI warning +ZIG_EXTERN_C Stage2ProgressNode *stage2_progress_start_root(Stage2Progress *progress, + const char *name_ptr, size_t name_len, size_t estimated_total_items); +// ABI warning +ZIG_EXTERN_C Stage2ProgressNode *stage2_progress_start(Stage2ProgressNode *node, + const char *name_ptr, size_t name_len, size_t estimated_total_items); +// ABI warning +ZIG_EXTERN_C void stage2_progress_end(Stage2ProgressNode *node); +// ABI warning +ZIG_EXTERN_C void stage2_progress_complete_one(Stage2ProgressNode *node); +// ABI warning +ZIG_EXTERN_C void stage2_progress_update_node(Stage2ProgressNode *node, + size_t completed_count, size_t estimated_total_items); + +// ABI warning +struct Stage2CpuFeatures; + +// ABI warning +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 +ZIG_EXTERN_C const char *stage2_cpu_features_get_llvm_cpu(const struct Stage2CpuFeatures *cpu_features); + +// ABI warning +ZIG_EXTERN_C const char *stage2_cpu_features_get_llvm_features(const struct Stage2CpuFeatures *cpu_features); + +// ABI warning +ZIG_EXTERN_C void stage2_cpu_features_get_builtin_str(const struct Stage2CpuFeatures *cpu_features, + const char **ptr, size_t *len); + +// ABI warning +ZIG_EXTERN_C void stage2_cpu_features_get_cache_hash(const struct Stage2CpuFeatures *cpu_features, + const char **ptr, size_t *len); + +// 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 +// Synchronize with target.cpp::os_list +enum Os { + OsFreestanding, + OsAnanas, + OsCloudABI, + OsDragonFly, + OsFreeBSD, + OsFuchsia, + OsIOS, + OsKFreeBSD, + OsLinux, + OsLv2, // PS3 + OsMacOSX, + OsNetBSD, + OsOpenBSD, + OsSolaris, + OsWindows, + OsHaiku, + OsMinix, + OsRTEMS, + OsNaCl, // Native Client + OsCNK, // BG/P Compute-Node Kernel + OsAIX, + OsCUDA, // NVIDIA CUDA + OsNVCL, // NVIDIA OpenCL + OsAMDHSA, // AMD HSA Runtime + OsPS4, + OsELFIAMCU, + OsTvOS, // Apple tvOS + OsWatchOS, // Apple watchOS + OsMesa3D, + OsContiki, + OsAMDPAL, + OsHermitCore, + OsHurd, + OsWASI, + OsEmscripten, + OsUefi, + OsOther, +}; + +// ABI warning +struct ZigGLibCVersion { + uint32_t major; // always 2 + uint32_t minor; + uint32_t patch; +}; + +// ABI warning +struct ZigTarget { + enum ZigLLVM_ArchType arch; + enum ZigLLVM_SubArchType sub_arch; + enum ZigLLVM_VendorType vendor; + Os os; + enum ZigLLVM_EnvironmentType abi; + struct ZigGLibCVersion *glibc_version; // null means default + struct Stage2CpuFeatures *cpu_features; + bool is_native; +}; + +// ABI warning +ZIG_EXTERN_C enum Error stage2_detect_dynamic_linker(const struct ZigTarget *target, + char **out_ptr, size_t *out_len); + +#endif diff --git a/src/target.cpp b/src/target.cpp index b8979d0214e87ddb6552af7733cbe0d55d65563e..209b42f3c9e1c7d6cff68a8fa2d86afeb99d9005 100644 --- a/src/target.cpp +++ b/src/target.cpp @@ -1204,213 +1204,10 @@ const char *target_lib_file_ext(const ZigTarget *target, bool is_static, } } -enum FloatAbi { - FloatAbiHard, - FloatAbiSoft, - FloatAbiSoftFp, -}; - -static FloatAbi get_float_abi(const ZigTarget *target) { - const ZigLLVM_EnvironmentType env = target->abi; - if (env == ZigLLVM_GNUEABIHF || - env == ZigLLVM_EABIHF || - env == ZigLLVM_MuslEABIHF) - { - return FloatAbiHard; - } else { - return FloatAbiSoft; - } -} - -static bool is_64_bit(ZigLLVM_ArchType arch) { - return target_arch_pointer_bit_width(arch) == 64; -} - bool target_is_android(const ZigTarget *target) { return target->abi == ZigLLVM_Android; } -const char *target_dynamic_linker(const ZigTarget *target) { - if (target_is_android(target)) { - return is_64_bit(target->arch) ? "/system/bin/linker64" : "/system/bin/linker"; - } - - if (target_is_musl(target)) { - Buf buf = BUF_INIT; - buf_init_from_str(&buf, "/lib/ld-musl-"); - bool is_arm = false; - switch (target->arch) { - case ZigLLVM_arm: - case ZigLLVM_thumb: - buf_append_str(&buf, "arm"); - is_arm = true; - break; - case ZigLLVM_armeb: - case ZigLLVM_thumbeb: - buf_append_str(&buf, "armeb"); - is_arm = true; - break; - default: - buf_append_str(&buf, target_arch_name(target->arch)); - } - if (is_arm && get_float_abi(target) == FloatAbiHard) { - buf_append_str(&buf, "hf"); - } - buf_append_str(&buf, ".so.1"); - return buf_ptr(&buf); - } - - switch (target->os) { - case OsFreeBSD: - return "/libexec/ld-elf.so.1"; - case OsNetBSD: - return "/libexec/ld.elf_so"; - case OsDragonFly: - return "/libexec/ld-elf.so.2"; - case OsLinux: { - const ZigLLVM_EnvironmentType abi = target->abi; - switch (target->arch) { - case ZigLLVM_UnknownArch: - zig_unreachable(); - case ZigLLVM_x86: - case ZigLLVM_sparc: - case ZigLLVM_sparcel: - return "/lib/ld-linux.so.2"; - - case ZigLLVM_aarch64: - return "/lib/ld-linux-aarch64.so.1"; - - case ZigLLVM_aarch64_be: - return "/lib/ld-linux-aarch64_be.so.1"; - - case ZigLLVM_aarch64_32: - return "/lib/ld-linux-aarch64_32.so.1"; - - case ZigLLVM_arm: - case ZigLLVM_thumb: - if (get_float_abi(target) == FloatAbiHard) { - return "/lib/ld-linux-armhf.so.3"; - } else { - return "/lib/ld-linux.so.3"; - } - - case ZigLLVM_armeb: - case ZigLLVM_thumbeb: - if (get_float_abi(target) == FloatAbiHard) { - return "/lib/ld-linux-armhf.so.3"; - } else { - return "/lib/ld-linux.so.3"; - } - - case ZigLLVM_mips: - case ZigLLVM_mipsel: - case ZigLLVM_mips64: - case ZigLLVM_mips64el: - zig_panic("TODO implement target_dynamic_linker for mips"); - - case ZigLLVM_ppc: - return "/lib/ld.so.1"; - - case ZigLLVM_ppc64: - return "/lib64/ld64.so.2"; - - case ZigLLVM_ppc64le: - return "/lib64/ld64.so.2"; - - case ZigLLVM_systemz: - return "/lib64/ld64.so.1"; - - case ZigLLVM_sparcv9: - return "/lib64/ld-linux.so.2"; - - case ZigLLVM_x86_64: - if (abi == ZigLLVM_GNUX32) { - return "/libx32/ld-linux-x32.so.2"; - } - if (abi == ZigLLVM_Musl || abi == ZigLLVM_MuslEABI || abi == ZigLLVM_MuslEABIHF) { - return "/lib/ld-musl-x86_64.so.1"; - } - return "/lib64/ld-linux-x86-64.so.2"; - - case ZigLLVM_wasm32: - case ZigLLVM_wasm64: - return nullptr; - - case ZigLLVM_riscv32: - return "/lib/ld-linux-riscv32-ilp32.so.1"; - case ZigLLVM_riscv64: - return "/lib/ld-linux-riscv64-lp64.so.1"; - - case ZigLLVM_arc: - case ZigLLVM_avr: - case ZigLLVM_bpfel: - case ZigLLVM_bpfeb: - case ZigLLVM_hexagon: - case ZigLLVM_msp430: - case ZigLLVM_r600: - case ZigLLVM_amdgcn: - case ZigLLVM_tce: - case ZigLLVM_tcele: - case ZigLLVM_xcore: - case ZigLLVM_nvptx: - case ZigLLVM_nvptx64: - case ZigLLVM_le32: - case ZigLLVM_le64: - case ZigLLVM_amdil: - case ZigLLVM_amdil64: - case ZigLLVM_hsail: - case ZigLLVM_hsail64: - case ZigLLVM_spir: - case ZigLLVM_spir64: - case ZigLLVM_kalimba: - case ZigLLVM_shave: - case ZigLLVM_lanai: - case ZigLLVM_renderscript32: - case ZigLLVM_renderscript64: - zig_panic("TODO implement target_dynamic_linker for this arch"); - } - zig_unreachable(); - } - case OsFreestanding: - case OsIOS: - case OsTvOS: - case OsWatchOS: - case OsMacOSX: - case OsUefi: - case OsWindows: - case OsEmscripten: - case OsOther: - return nullptr; - - case OsAnanas: - case OsCloudABI: - case OsFuchsia: - case OsKFreeBSD: - case OsLv2: - case OsOpenBSD: - case OsSolaris: - case OsHaiku: - case OsMinix: - case OsRTEMS: - case OsNaCl: - case OsCNK: - case OsAIX: - case OsCUDA: - case OsNVCL: - case OsAMDHSA: - case OsPS4: - case OsELFIAMCU: - case OsMesa3D: - case OsContiki: - case OsAMDPAL: - case OsHermitCore: - case OsHurd: - case OsWASI: - zig_panic("TODO implement target_dynamic_linker for this OS"); - } - zig_unreachable(); -} - bool target_can_exec(const ZigTarget *host_target, const ZigTarget *guest_target) { assert(host_target != nullptr); diff --git a/src/target.hpp b/src/target.hpp index 3e984e1269cb867773ff9125e28ebe05675a3294..43447497198cb22ff6eae45966168cbb373b37a0 100644 --- a/src/target.hpp +++ b/src/target.hpp @@ -8,51 +8,10 @@ #ifndef ZIG_TARGET_HPP #define ZIG_TARGET_HPP -#include +#include "stage2.h" struct Buf; -// Synchronize with target.cpp::os_list -enum Os { - OsFreestanding, - OsAnanas, - OsCloudABI, - OsDragonFly, - OsFreeBSD, - OsFuchsia, - OsIOS, - OsKFreeBSD, - OsLinux, - OsLv2, // PS3 - OsMacOSX, - OsNetBSD, - OsOpenBSD, - OsSolaris, - OsWindows, - OsHaiku, - OsMinix, - OsRTEMS, - OsNaCl, // Native Client - OsCNK, // BG/P Compute-Node Kernel - OsAIX, - OsCUDA, // NVIDIA CUDA - OsNVCL, // NVIDIA OpenCL - OsAMDHSA, // AMD HSA Runtime - OsPS4, - OsELFIAMCU, - OsTvOS, // Apple tvOS - OsWatchOS, // Apple watchOS - OsMesa3D, - OsContiki, - OsAMDPAL, - OsHermitCore, - OsHurd, - OsWASI, - OsEmscripten, - OsUefi, - OsOther, -}; - // Synchronize with target.cpp::subarch_list_list enum SubArchList { SubArchListNone, @@ -78,23 +37,6 @@ enum TargetSubsystem { TargetSubsystemAuto }; -struct ZigGLibCVersion { - uint32_t major; // always 2 - uint32_t minor; - uint32_t patch; -}; - -struct ZigTarget { - ZigLLVM_ArchType arch; - ZigLLVM_SubArchType sub_arch; - ZigLLVM_VendorType vendor; - Os os; - ZigLLVM_EnvironmentType abi; - ZigGLibCVersion *glibc_version; // null means default - Stage2CpuFeatures *cpu_features; - bool is_native; -}; - enum CIntType { CIntTypeShort, CIntTypeUShort, @@ -168,8 +110,6 @@ const char *target_lib_file_prefix(const ZigTarget *target); const char *target_lib_file_ext(const ZigTarget *target, bool is_static, size_t version_major, size_t version_minor, size_t version_patch); -const char *target_dynamic_linker(const ZigTarget *target); - bool target_can_exec(const ZigTarget *host_target, const ZigTarget *guest_target); ZigLLVM_OSType get_llvm_os_type(Os os_type); diff --git a/src/userland.cpp b/src/userland.cpp deleted file mode 100644 index ba0e906cf94c1650148b39a19a0f78ef9a1968e1..0000000000000000000000000000000000000000 --- a/src/userland.cpp +++ /dev/null @@ -1,146 +0,0 @@ -// This file is a shim for zig1. The real implementations of these are in -// src-self-hosted/stage1.zig - -#include "userland.h" -#include "util.hpp" -#include "zig_llvm.h" -#include -#include -#include - -Error stage2_translate_c(struct Stage2Ast **out_ast, - struct Stage2ErrorMsg **out_errors_ptr, size_t *out_errors_len, - const char **args_begin, const char **args_end, const char *resources_path) -{ - const char *msg = "stage0 called stage2_translate_c"; - stage2_panic(msg, strlen(msg)); -} - -void stage2_free_clang_errors(struct Stage2ErrorMsg *ptr, size_t len) { - const char *msg = "stage0 called stage2_free_clang_errors"; - stage2_panic(msg, strlen(msg)); -} - -void stage2_zen(const char **ptr, size_t *len) { - const char *msg = "stage0 called stage2_zen"; - stage2_panic(msg, strlen(msg)); -} - -void stage2_attach_segfault_handler(void) { } - -void stage2_panic(const char *ptr, size_t len) { - fwrite(ptr, 1, len, stderr); - fprintf(stderr, "\n"); - fflush(stderr); - abort(); -} - -void stage2_render_ast(struct Stage2Ast *ast, FILE *output_file) { - const char *msg = "stage0 called stage2_render_ast"; - stage2_panic(msg, strlen(msg)); -} - -int stage2_fmt(int argc, char **argv) { - const char *msg = "stage0 called stage2_fmt"; - stage2_panic(msg, strlen(msg)); -} - -stage2_DepTokenizer stage2_DepTokenizer_init(const char *input, size_t len) { - const char *msg = "stage0 called stage2_DepTokenizer_init"; - stage2_panic(msg, strlen(msg)); -} - -void stage2_DepTokenizer_deinit(stage2_DepTokenizer *self) { - const char *msg = "stage0 called stage2_DepTokenizer_deinit"; - stage2_panic(msg, strlen(msg)); -} - -stage2_DepNextResult stage2_DepTokenizer_next(stage2_DepTokenizer *self) { - const char *msg = "stage0 called stage2_DepTokenizer_next"; - stage2_panic(msg, strlen(msg)); -} - - -struct Stage2Progress { - int trash; -}; - -struct Stage2ProgressNode { - int trash; -}; - -Stage2Progress *stage2_progress_create(void) { - return nullptr; -} - -void stage2_progress_destroy(Stage2Progress *progress) {} - -Stage2ProgressNode *stage2_progress_start_root(Stage2Progress *progress, - const char *name_ptr, size_t name_len, size_t estimated_total_items) -{ - return nullptr; -} -Stage2ProgressNode *stage2_progress_start(Stage2ProgressNode *node, - const char *name_ptr, size_t name_len, size_t estimated_total_items) -{ - return nullptr; -} -void stage2_progress_end(Stage2ProgressNode *node) {} -void stage2_progress_complete_one(Stage2ProgressNode *node) {} -void stage2_progress_disable_tty(Stage2Progress *progress) {} -void stage2_progress_update_node(Stage2ProgressNode *node, size_t completed_count, size_t estimated_total_items){} - -struct Stage2CpuFeatures { - const char *llvm_cpu_name; - const char *llvm_cpu_features; - const char *builtin_str; - const char *cache_hash; -}; - -Error stage2_cpu_features_parse(struct Stage2CpuFeatures **out, const char *zig_triple, - const char *cpu_name, const char *cpu_features) -{ - if (zig_triple == nullptr) { - Stage2CpuFeatures *result = heap::c_allocator.create(); - result->llvm_cpu_name = ZigLLVMGetHostCPUName(); - result->llvm_cpu_features = ZigLLVMGetNativeFeatures(); - result->builtin_str = "arch.getBaselineCpuFeatures();\n"; - result->cache_hash = "native\n\n"; - *out = result; - return ErrorNone; - } - if (cpu_name == nullptr && cpu_features == nullptr) { - Stage2CpuFeatures *result = heap::c_allocator.create(); - result->builtin_str = "arch.getBaselineCpuFeatures();\n"; - result->cache_hash = "\n\n"; - *out = result; - return ErrorNone; - } - - const char *msg = "stage0 called stage2_cpu_features_parse with non-null cpu name or features"; - stage2_panic(msg, strlen(msg)); -} - -void stage2_cpu_features_get_cache_hash(const Stage2CpuFeatures *cpu_features, - const char **ptr, size_t *len) -{ - *ptr = cpu_features->cache_hash; - *len = strlen(cpu_features->cache_hash); -} -const char *stage2_cpu_features_get_llvm_cpu(const Stage2CpuFeatures *cpu_features) { - return cpu_features->llvm_cpu_name; -} -const char *stage2_cpu_features_get_llvm_features(const Stage2CpuFeatures *cpu_features) { - return cpu_features->llvm_cpu_features; -} -void stage2_cpu_features_get_builtin_str(const Stage2CpuFeatures *cpu_features, - const char **ptr, size_t *len) -{ - *ptr = cpu_features->builtin_str; - *len = strlen(cpu_features->builtin_str); -} - -int stage2_cmd_targets(const char *zig_triple) { - const char *msg = "stage0 called stage2_cmd_targets"; - stage2_panic(msg, strlen(msg)); -} diff --git a/src/userland.h b/src/userland.h deleted file mode 100644 index a59f6b7da81cf40a7027141feb5817849e846d38..0000000000000000000000000000000000000000 --- a/src/userland.h +++ /dev/null @@ -1,209 +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_USERLAND_H -#define ZIG_USERLAND_H - -#include -#include -#include - -#ifdef __cplusplus -#define ZIG_EXTERN_C extern "C" -#else -#define ZIG_EXTERN_C -#endif - -#if defined(_MSC_VER) -#define ZIG_ATTRIBUTE_NORETURN __declspec(noreturn) -#else -#define ZIG_ATTRIBUTE_NORETURN __attribute__((noreturn)) -#endif - -// ABI warning: the types and declarations in this file must match both those in -// userland.cpp and src-self-hosted/stage1.zig. - -// ABI warning -enum Error { - ErrorNone, - ErrorNoMem, - ErrorInvalidFormat, - ErrorSemanticAnalyzeFail, - ErrorAccess, - ErrorInterrupted, - ErrorSystemResources, - ErrorFileNotFound, - ErrorFileSystem, - ErrorFileTooBig, - ErrorDivByZero, - ErrorOverflow, - ErrorPathAlreadyExists, - ErrorUnexpected, - ErrorExactDivRemainder, - ErrorNegativeDenominator, - ErrorShiftedOutOneBits, - ErrorCCompileErrors, - ErrorEndOfFile, - ErrorIsDir, - ErrorNotDir, - ErrorUnsupportedOperatingSystem, - ErrorSharingViolation, - ErrorPipeBusy, - ErrorPrimitiveTypeNotFound, - ErrorCacheUnavailable, - ErrorPathTooLong, - ErrorCCompilerCannotFindFile, - ErrorNoCCompilerInstalled, - ErrorReadingDepFile, - ErrorInvalidDepFile, - ErrorMissingArchitecture, - ErrorMissingOperatingSystem, - ErrorUnknownArchitecture, - ErrorUnknownOperatingSystem, - ErrorUnknownABI, - ErrorInvalidFilename, - ErrorDiskQuota, - ErrorDiskSpace, - ErrorUnexpectedWriteFailure, - ErrorUnexpectedSeekFailure, - ErrorUnexpectedFileTruncationFailure, - ErrorUnimplemented, - ErrorOperationAborted, - ErrorBrokenPipe, - ErrorNoSpaceLeft, - ErrorNotLazy, - ErrorIsAsync, - ErrorImportOutsidePkgPath, - ErrorUnknownCpu, - ErrorUnknownSubArchitecture, - ErrorUnknownCpuFeature, - ErrorInvalidCpuFeatures, - ErrorInvalidLlvmCpuFeaturesFormat, - ErrorUnknownApplicationBinaryInterface, - ErrorASTUnitFailure, -}; - -// ABI warning -struct Stage2ErrorMsg { - const char *filename_ptr; // can be null - size_t filename_len; - const char *msg_ptr; - size_t msg_len; - const char *source; // valid until the ASTUnit is freed. can be null - unsigned line; // 0 based - unsigned column; // 0 based - unsigned offset; // byte offset into source -}; - -// ABI warning -struct Stage2Ast; - -// ABI warning -ZIG_EXTERN_C enum Error stage2_translate_c(struct Stage2Ast **out_ast, - struct Stage2ErrorMsg **out_errors_ptr, size_t *out_errors_len, - const char **args_begin, const char **args_end, const char *resources_path); - -// ABI warning -ZIG_EXTERN_C void stage2_free_clang_errors(struct Stage2ErrorMsg *ptr, size_t len); - -// ABI warning -ZIG_EXTERN_C void stage2_render_ast(struct Stage2Ast *ast, FILE *output_file); - -// ABI warning -ZIG_EXTERN_C void stage2_zen(const char **ptr, size_t *len); - -// ABI warning -ZIG_EXTERN_C void stage2_attach_segfault_handler(void); - -// ABI warning -ZIG_EXTERN_C ZIG_ATTRIBUTE_NORETURN void stage2_panic(const char *ptr, size_t len); - -// ABI warning -ZIG_EXTERN_C int stage2_fmt(int argc, char **argv); - -// ABI warning -struct stage2_DepTokenizer { - void *handle; -}; - -// ABI warning -struct stage2_DepNextResult { - enum TypeId { - error, - null, - target, - prereq, - }; - - TypeId type_id; - - // when ent == error --> error text - // when ent == null --> undefined - // when ent == target --> target pathname - // when ent == prereq --> prereq pathname - const char *textz; -}; - -// ABI warning -ZIG_EXTERN_C stage2_DepTokenizer stage2_DepTokenizer_init(const char *input, size_t len); - -// ABI warning -ZIG_EXTERN_C void stage2_DepTokenizer_deinit(stage2_DepTokenizer *self); - -// ABI warning -ZIG_EXTERN_C stage2_DepNextResult stage2_DepTokenizer_next(stage2_DepTokenizer *self); - -// ABI warning -struct Stage2Progress; -// ABI warning -struct Stage2ProgressNode; -// ABI warning -ZIG_EXTERN_C Stage2Progress *stage2_progress_create(void); -// ABI warning -ZIG_EXTERN_C void stage2_progress_disable_tty(Stage2Progress *progress); -// ABI warning -ZIG_EXTERN_C void stage2_progress_destroy(Stage2Progress *progress); -// ABI warning -ZIG_EXTERN_C Stage2ProgressNode *stage2_progress_start_root(Stage2Progress *progress, - const char *name_ptr, size_t name_len, size_t estimated_total_items); -// ABI warning -ZIG_EXTERN_C Stage2ProgressNode *stage2_progress_start(Stage2ProgressNode *node, - const char *name_ptr, size_t name_len, size_t estimated_total_items); -// ABI warning -ZIG_EXTERN_C void stage2_progress_end(Stage2ProgressNode *node); -// ABI warning -ZIG_EXTERN_C void stage2_progress_complete_one(Stage2ProgressNode *node); -// ABI warning -ZIG_EXTERN_C void stage2_progress_update_node(Stage2ProgressNode *node, - size_t completed_count, size_t estimated_total_items); - -// ABI warning -struct Stage2CpuFeatures; - -// ABI warning -ZIG_EXTERN_C Error stage2_cpu_features_parse(struct Stage2CpuFeatures **result, - const char *zig_triple, const char *cpu_name, const char *cpu_features); - -// ABI warning -ZIG_EXTERN_C const char *stage2_cpu_features_get_llvm_cpu(const struct Stage2CpuFeatures *cpu_features); - -// ABI warning -ZIG_EXTERN_C const char *stage2_cpu_features_get_llvm_features(const struct Stage2CpuFeatures *cpu_features); - -// ABI warning -ZIG_EXTERN_C void stage2_cpu_features_get_builtin_str(const struct Stage2CpuFeatures *cpu_features, - const char **ptr, size_t *len); - -// ABI warning -ZIG_EXTERN_C void stage2_cpu_features_get_cache_hash(const struct Stage2CpuFeatures *cpu_features, - const char **ptr, size_t *len); - -// ABI warning -ZIG_EXTERN_C int stage2_cmd_targets(const char *zig_triple); - - -#endif diff --git a/src/util.cpp b/src/util.cpp index 56f1de9839db8246b9f1e8fc2ea806c306bcc9cd..2de09df8087be2d8a012328117e1898560f0dc0f 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -6,7 +6,7 @@ */ #include "util.hpp" -#include "userland.h" +#include "stage2.h" #include #include diff --git a/src/windows_sdk.h b/src/windows_sdk.h index 2d531ad372083fee78f0a942caf69670bc4eb709..e8872095597a02c0c538ef23d78df74574353122 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 diff --git a/src/zig_clang.h b/src/zig_clang.h index f7de9c2cee37173d121903cc5d1343afd3b97b26..2d9485ae9b8230df2a1fcf5c9be29c2d3002c75a 100644 --- a/src/zig_clang.h +++ b/src/zig_clang.h @@ -8,7 +8,7 @@ #ifndef ZIG_ZIG_CLANG_H #define ZIG_ZIG_CLANG_H -#include "userland.h" +#include "stage2.h" #include #include