mirror of
https://github.com/ziglang/zig.git
synced 2026-02-13 04:48:20 +00:00
Merge pull request #4478 from ziglang/self-host-libc-detection
self-hosted libc and dynamic linker detection
This commit is contained in:
commit
e8a84927ab
@ -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()
|
||||
|
||||
29
build.zig
29
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);
|
||||
}
|
||||
|
||||
@ -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("::")
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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");
|
||||
|
||||
139
lib/std/event/batch.zig
Normal file
139
lib/std/event/batch.zig
Normal file
@ -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;
|
||||
}
|
||||
@ -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,
|
||||
|
||||
@ -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.
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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;
|
||||
|
||||
206
lib/std/os.zig
206
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;
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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"),
|
||||
}
|
||||
}
|
||||
|
||||
@ -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" {
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -4,5 +4,4 @@ pub usingnamespace @cImport({
|
||||
@cInclude("inttypes.h");
|
||||
@cInclude("config.h");
|
||||
@cInclude("zig_llvm.h");
|
||||
@cInclude("windows_sdk.h");
|
||||
});
|
||||
|
||||
@ -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" });
|
||||
|
||||
@ -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 group.wait();
|
||||
return self;
|
||||
}
|
||||
|
||||
async fn findNativeIncludeDirLinux(self: *LibCInstallation, allocator: *Allocator) FindError!void {
|
||||
const cc_exe = std.os.getenv("CC") orelse "cc";
|
||||
/// Must be the same allocator passed to `parse` or `findNative`.
|
||||
pub fn deinit(self: *LibCInstallation, allocator: *Allocator) void {
|
||||
const fields = std.meta.fields(LibCInstallation);
|
||||
inline for (fields) |field| {
|
||||
if (@field(self, field.name)) |payload| {
|
||||
allocator.free(payload);
|
||||
}
|
||||
}
|
||||
self.* = undefined;
|
||||
}
|
||||
|
||||
fn findNativeIncludeDirPosix(self: *LibCInstallation, allocator: *Allocator) FindError!void {
|
||||
const dev_null = if (is_windows) "nul" else "/dev/null";
|
||||
const cc_exe = std.os.getenvZ("CC") orelse default_cc_exe;
|
||||
const argv = [_][]const u8{
|
||||
cc_exe,
|
||||
"-E",
|
||||
"-Wp,-v",
|
||||
"-xc",
|
||||
"/dev/null",
|
||||
dev_null,
|
||||
};
|
||||
// TODO make this use event loop
|
||||
const errorable_result = std.ChildProcess.exec(allocator, &argv, null, null, 1024 * 1024);
|
||||
const exec_result = if (std.debug.runtime_safety) blk: {
|
||||
break :blk errorable_result catch unreachable;
|
||||
} else blk: {
|
||||
break :blk errorable_result catch |err| switch (err) {
|
||||
error.OutOfMemory => return error.OutOfMemory,
|
||||
else => return error.UnableToSpawnCCompiler,
|
||||
};
|
||||
const 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_res.term) {
|
||||
.Exited => |code| if (code != 0) return error.CCompilerExitCode,
|
||||
else => return error.CCompilerCrashed,
|
||||
}
|
||||
|
||||
switch (exec_result.term) {
|
||||
.Exited => |code| {
|
||||
if (code != 0) return error.CCompilerExitCode;
|
||||
},
|
||||
else => {
|
||||
return error.CCompilerCrashed;
|
||||
},
|
||||
}
|
||||
|
||||
var it = std.mem.tokenize(exec_result.stderr, "\n\r");
|
||||
var it = std.mem.tokenize(exec_res.stderr, "\n\r");
|
||||
var search_paths = std.ArrayList([]const u8).init(allocator);
|
||||
defer search_paths.deinit();
|
||||
while (it.next()) |line| {
|
||||
@ -226,16 +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,215 +336,327 @@ 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");
|
||||
const line = it.next() orelse return error.LibCRuntimeNotFound;
|
||||
const dirname = fs.path.dirname(line) orelse return error.LibCRuntimeNotFound;
|
||||
|
||||
if (want_dirname) {
|
||||
return std.mem.dupe(allocator, u8, dirname);
|
||||
} else {
|
||||
return std.mem.dupe(allocator, u8, line);
|
||||
var it = std.mem.tokenize(exec_res.stdout, "\n\r");
|
||||
const line = it.next() orelse return error.LibCRuntimeNotFound;
|
||||
// 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`.
|
||||
|
||||
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 {
|
||||
path: []const u8,
|
||||
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,
|
||||
}
|
||||
}
|
||||
|
||||
@ -14,6 +14,7 @@ const self_hosted_main = @import("main.zig");
|
||||
const errmsg = @import("errmsg.zig");
|
||||
const DepTokenizer = @import("dep_tokenizer.zig").Tokenizer;
|
||||
const assert = std.debug.assert;
|
||||
const LibCInstallation = @import("libc_installation.zig").LibCInstallation;
|
||||
|
||||
var stderr_file: fs.File = undefined;
|
||||
var stderr: *io.OutStream(fs.File.WriteError) = undefined;
|
||||
@ -93,6 +94,23 @@ const Error = extern enum {
|
||||
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;
|
||||
@ -113,12 +131,12 @@ export fn stage2_translate_c(
|
||||
error.SemanticAnalyzeFail => {
|
||||
out_errors_ptr.* = errors.ptr;
|
||||
out_errors_len.* = errors.len;
|
||||
return Error.CCompileErrors;
|
||||
return .CCompileErrors;
|
||||
},
|
||||
error.ASTUnitFailure => return Error.ASTUnitFailure,
|
||||
error.OutOfMemory => return Error.OutOfMemory,
|
||||
error.ASTUnitFailure => return .ASTUnitFailure,
|
||||
error.OutOfMemory => return .OutOfMemory,
|
||||
};
|
||||
return Error.None;
|
||||
return .None;
|
||||
}
|
||||
|
||||
export fn stage2_free_clang_errors(errors_ptr: [*]translate_c.ClangErrMsg, errors_len: usize) void {
|
||||
@ -129,18 +147,18 @@ export fn stage2_render_ast(tree: *ast.Tree, output_file: *FILE) Error {
|
||||
const c_out_stream = &std.io.COutStream.init(output_file).stream;
|
||||
_ = std.zig.render(std.heap.c_allocator, c_out_stream, tree) catch |e| switch (e) {
|
||||
error.WouldBlock => unreachable, // stage1 opens stuff in exclusively blocking mode
|
||||
error.SystemResources => return Error.SystemResources,
|
||||
error.OperationAborted => return Error.OperationAborted,
|
||||
error.BrokenPipe => return Error.BrokenPipe,
|
||||
error.DiskQuota => return Error.DiskQuota,
|
||||
error.FileTooBig => return Error.FileTooBig,
|
||||
error.NoSpaceLeft => return Error.NoSpaceLeft,
|
||||
error.AccessDenied => return Error.AccessDenied,
|
||||
error.OutOfMemory => return Error.OutOfMemory,
|
||||
error.Unexpected => return Error.Unexpected,
|
||||
error.InputOutput => return Error.FileSystem,
|
||||
error.SystemResources => return .SystemResources,
|
||||
error.OperationAborted => return .OperationAborted,
|
||||
error.BrokenPipe => return .BrokenPipe,
|
||||
error.DiskQuota => return .DiskQuota,
|
||||
error.FileTooBig => return .FileTooBig,
|
||||
error.NoSpaceLeft => return .NoSpaceLeft,
|
||||
error.AccessDenied => return .AccessDenied,
|
||||
error.OutOfMemory => return .OutOfMemory,
|
||||
error.Unexpected => return .Unexpected,
|
||||
error.InputOutput => return .FileSystem,
|
||||
};
|
||||
return Error.None;
|
||||
return .None;
|
||||
}
|
||||
|
||||
// TODO: just use the actual self-hosted zig fmt. Until https://github.com/ziglang/zig/issues/2377,
|
||||
@ -832,3 +850,269 @@ export fn stage2_cpu_features_get_llvm_cpu(cpu_features: *const Stage2CpuFeature
|
||||
export fn stage2_cpu_features_get_llvm_features(cpu_features: *const Stage2CpuFeatures) ?[*:0]const u8 {
|
||||
return cpu_features.llvm_features_str;
|
||||
}
|
||||
|
||||
// ABI warning
|
||||
const Stage2LibCInstallation = extern struct {
|
||||
include_dir: [*:0]const u8,
|
||||
include_dir_len: usize,
|
||||
sys_include_dir: [*:0]const u8,
|
||||
sys_include_dir_len: usize,
|
||||
crt_dir: [*:0]const u8,
|
||||
crt_dir_len: usize,
|
||||
static_crt_dir: [*:0]const u8,
|
||||
static_crt_dir_len: usize,
|
||||
msvc_lib_dir: [*:0]const u8,
|
||||
msvc_lib_dir_len: usize,
|
||||
kernel32_lib_dir: [*:0]const u8,
|
||||
kernel32_lib_dir_len: usize,
|
||||
|
||||
fn initFromStage2(self: *Stage2LibCInstallation, libc: LibCInstallation) void {
|
||||
if (libc.include_dir) |s| {
|
||||
self.include_dir = s.ptr;
|
||||
self.include_dir_len = s.len;
|
||||
} else {
|
||||
self.include_dir = "";
|
||||
self.include_dir_len = 0;
|
||||
}
|
||||
if (libc.sys_include_dir) |s| {
|
||||
self.sys_include_dir = s.ptr;
|
||||
self.sys_include_dir_len = s.len;
|
||||
} else {
|
||||
self.sys_include_dir = "";
|
||||
self.sys_include_dir_len = 0;
|
||||
}
|
||||
if (libc.crt_dir) |s| {
|
||||
self.crt_dir = s.ptr;
|
||||
self.crt_dir_len = s.len;
|
||||
} else {
|
||||
self.crt_dir = "";
|
||||
self.crt_dir_len = 0;
|
||||
}
|
||||
if (libc.static_crt_dir) |s| {
|
||||
self.static_crt_dir = s.ptr;
|
||||
self.static_crt_dir_len = s.len;
|
||||
} else {
|
||||
self.static_crt_dir = "";
|
||||
self.static_crt_dir_len = 0;
|
||||
}
|
||||
if (libc.msvc_lib_dir) |s| {
|
||||
self.msvc_lib_dir = s.ptr;
|
||||
self.msvc_lib_dir_len = s.len;
|
||||
} else {
|
||||
self.msvc_lib_dir = "";
|
||||
self.msvc_lib_dir_len = 0;
|
||||
}
|
||||
if (libc.kernel32_lib_dir) |s| {
|
||||
self.kernel32_lib_dir = s.ptr;
|
||||
self.kernel32_lib_dir_len = s.len;
|
||||
} else {
|
||||
self.kernel32_lib_dir = "";
|
||||
self.kernel32_lib_dir_len = 0;
|
||||
}
|
||||
}
|
||||
|
||||
fn toStage2(self: Stage2LibCInstallation) LibCInstallation {
|
||||
var libc: LibCInstallation = .{};
|
||||
if (self.include_dir_len != 0) {
|
||||
libc.include_dir = self.include_dir[0..self.include_dir_len :0];
|
||||
}
|
||||
if (self.sys_include_dir_len != 0) {
|
||||
libc.sys_include_dir = self.sys_include_dir[0..self.sys_include_dir_len :0];
|
||||
}
|
||||
if (self.crt_dir_len != 0) {
|
||||
libc.crt_dir = self.crt_dir[0..self.crt_dir_len :0];
|
||||
}
|
||||
if (self.static_crt_dir_len != 0) {
|
||||
libc.static_crt_dir = self.static_crt_dir[0..self.static_crt_dir_len :0];
|
||||
}
|
||||
if (self.msvc_lib_dir_len != 0) {
|
||||
libc.msvc_lib_dir = self.msvc_lib_dir[0..self.msvc_lib_dir_len :0];
|
||||
}
|
||||
if (self.kernel32_lib_dir_len != 0) {
|
||||
libc.kernel32_lib_dir = self.kernel32_lib_dir[0..self.kernel32_lib_dir_len :0];
|
||||
}
|
||||
return libc;
|
||||
}
|
||||
};
|
||||
|
||||
// ABI warning
|
||||
export fn stage2_libc_parse(stage1_libc: *Stage2LibCInstallation, libc_file_z: [*:0]const u8) Error {
|
||||
stderr_file = std.io.getStdErr();
|
||||
stderr = &stderr_file.outStream().stream;
|
||||
const libc_file = mem.toSliceConst(u8, libc_file_z);
|
||||
var libc = LibCInstallation.parse(std.heap.c_allocator, libc_file, stderr) catch |err| switch (err) {
|
||||
error.ParseError => return .SemanticAnalyzeFail,
|
||||
error.DiskQuota => return .DiskQuota,
|
||||
error.FileTooBig => return .FileTooBig,
|
||||
error.InputOutput => return .FileSystem,
|
||||
error.NoSpaceLeft => return .NoSpaceLeft,
|
||||
error.AccessDenied => return .AccessDenied,
|
||||
error.BrokenPipe => return .BrokenPipe,
|
||||
error.SystemResources => return .SystemResources,
|
||||
error.OperationAborted => return .OperationAborted,
|
||||
error.WouldBlock => unreachable,
|
||||
error.Unexpected => return .Unexpected,
|
||||
error.EndOfStream => return .EndOfFile,
|
||||
error.IsDir => return .IsDir,
|
||||
error.ConnectionResetByPeer => unreachable,
|
||||
error.OutOfMemory => return .OutOfMemory,
|
||||
error.Unseekable => unreachable,
|
||||
error.SharingViolation => return .SharingViolation,
|
||||
error.PathAlreadyExists => unreachable,
|
||||
error.FileNotFound => return .FileNotFound,
|
||||
error.PipeBusy => return .PipeBusy,
|
||||
error.NameTooLong => return .PathTooLong,
|
||||
error.InvalidUtf8 => return .BadPathName,
|
||||
error.BadPathName => return .BadPathName,
|
||||
error.SymLinkLoop => return .SymLinkLoop,
|
||||
error.ProcessFdQuotaExceeded => return .ProcessFdQuotaExceeded,
|
||||
error.SystemFdQuotaExceeded => return .SystemFdQuotaExceeded,
|
||||
error.NoDevice => return .NoDevice,
|
||||
error.NotDir => return .NotDir,
|
||||
error.DeviceBusy => return .DeviceBusy,
|
||||
};
|
||||
stage1_libc.initFromStage2(libc);
|
||||
return .None;
|
||||
}
|
||||
|
||||
// ABI warning
|
||||
export fn stage2_libc_find_native(stage1_libc: *Stage2LibCInstallation) Error {
|
||||
var libc = LibCInstallation.findNative(std.heap.c_allocator) catch |err| switch (err) {
|
||||
error.OutOfMemory => return .OutOfMemory,
|
||||
error.FileSystem => return .FileSystem,
|
||||
error.UnableToSpawnCCompiler => return .UnableToSpawnCCompiler,
|
||||
error.CCompilerExitCode => return .CCompilerExitCode,
|
||||
error.CCompilerCrashed => return .CCompilerCrashed,
|
||||
error.CCompilerCannotFindHeaders => return .CCompilerCannotFindHeaders,
|
||||
error.LibCRuntimeNotFound => return .LibCRuntimeNotFound,
|
||||
error.LibCStdLibHeaderNotFound => return .LibCStdLibHeaderNotFound,
|
||||
error.LibCKernel32LibNotFound => return .LibCKernel32LibNotFound,
|
||||
error.UnsupportedArchitecture => return .UnsupportedArchitecture,
|
||||
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));
|
||||
}
|
||||
@ -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) {
|
||||
|
||||
22
src-self-hosted/windows_sdk.zig
Normal file
22
src-self-hosted/windows_sdk.zig
Normal file
@ -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;
|
||||
@ -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<const char *> lib_dirs;
|
||||
ZigList<const char *> framework_dirs;
|
||||
|
||||
ZigLibCInstallation *libc;
|
||||
Stage2LibCInstallation *libc;
|
||||
|
||||
size_t version_major;
|
||||
size_t version_minor;
|
||||
|
||||
@ -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"
|
||||
|
||||
113
src/codegen.cpp
113
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<Buf*>(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<const char*>(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<ZigLibCInstallation>();
|
||||
g->libc = heap::c_allocator.create<Stage2LibCInstallation>();
|
||||
|
||||
// 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<Buf*>(dir_count);
|
||||
g->libc_include_dir_list = heap::c_allocator.allocate<const char *>(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<const char *> &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<CodeGen>();
|
||||
g->pass1_arena = heap::ArenaAllocator::construct(&heap::c_allocator, &heap::c_allocator, "pass1");
|
||||
|
||||
@ -11,17 +11,16 @@
|
||||
#include "parser.hpp"
|
||||
#include "errmsg.hpp"
|
||||
#include "target.hpp"
|
||||
#include "libc_installation.hpp"
|
||||
#include "userland.h"
|
||||
#include "stage2.h"
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
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);
|
||||
|
||||
@ -4,20 +4,6 @@
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
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<Buf *> 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;
|
||||
}
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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)";
|
||||
}
|
||||
|
||||
@ -8,7 +8,7 @@
|
||||
#ifndef ERROR_HPP
|
||||
#define ERROR_HPP
|
||||
|
||||
#include "userland.h"
|
||||
#include "stage2.h"
|
||||
|
||||
const char *err_str(Error err);
|
||||
|
||||
|
||||
@ -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<uint8_t> name, Slice<uint8_t> 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<Slice<uint8_t>> 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<uint8_t> name;
|
||||
if (!SplitIterator_next(&line_it).unwrap(&name)) {
|
||||
if (verbose) {
|
||||
fprintf(stderr, "missing equal sign after field name\n");
|
||||
}
|
||||
return ErrorSemanticAnalyzeFail;
|
||||
}
|
||||
Slice<uint8_t> 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<const char *> 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<const char *> 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<const char *> 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
|
||||
}
|
||||
@ -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 <stdio.h>
|
||||
|
||||
#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
|
||||
14
src/link.cpp
14
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)));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
60
src/main.cpp
60
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<ZigLibCInstallation>();
|
||||
if ((err = zig_libc_parse(libc, buf_create_from_str(libc_txt), &target, true))) {
|
||||
libc = heap::c_allocator.create<Stage2LibCInstallation>();
|
||||
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 {
|
||||
|
||||
311
src/os.cpp
311
src/os.cpp
@ -826,7 +826,9 @@ static Error os_exec_process_posix(ZigList<const char *> &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<const char *> &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,6 +1035,124 @@ Error os_write_file(Buf *full_path, Buf *contents) {
|
||||
return ErrorNone;
|
||||
}
|
||||
|
||||
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)) {
|
||||
return ErrorFileSystem;
|
||||
}
|
||||
}
|
||||
size_t amt_written = fwrite(buf, 1, amt_read, dest_f);
|
||||
if (amt_written != amt_read) {
|
||||
return ErrorFileSystem;
|
||||
}
|
||||
if (feof(src_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) {
|
||||
@ -1055,30 +1179,10 @@ Error os_copy_file(Buf *src_path, Buf *dest_path) {
|
||||
return ErrorFileSystem;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
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) {
|
||||
@ -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<Buf *> &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
|
||||
|
||||
16
src/os.hpp
16
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<Buf *> &paths);
|
||||
|
||||
#endif
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
// This file is a shim for zig1. The real implementations of these are in
|
||||
// src-self-hosted/stage1.zig
|
||||
|
||||
#include "userland.h"
|
||||
#include "stage2.h"
|
||||
#include "util.hpp"
|
||||
#include "zig_llvm.h"
|
||||
#include <stdio.h>
|
||||
@ -144,3 +144,34 @@ 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));
|
||||
}
|
||||
@ -5,13 +5,15 @@
|
||||
* See http://opensource.org/licenses/MIT
|
||||
*/
|
||||
|
||||
#ifndef ZIG_USERLAND_H
|
||||
#define ZIG_USERLAND_H
|
||||
#ifndef ZIG_STAGE2_H
|
||||
#define ZIG_STAGE2_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "zig_llvm.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
#define ZIG_EXTERN_C extern "C"
|
||||
#else
|
||||
@ -25,7 +27,7 @@
|
||||
#endif
|
||||
|
||||
// ABI warning: the types and declarations in this file must match both those in
|
||||
// userland.cpp and src-self-hosted/stage1.zig.
|
||||
// stage2.cpp and src-self-hosted/stage2.zig.
|
||||
|
||||
// ABI warning
|
||||
enum Error {
|
||||
@ -85,6 +87,23 @@ enum Error {
|
||||
ErrorInvalidLlvmCpuFeaturesFormat,
|
||||
ErrorUnknownApplicationBinaryInterface,
|
||||
ErrorASTUnitFailure,
|
||||
ErrorBadPathName,
|
||||
ErrorSymLinkLoop,
|
||||
ErrorProcessFdQuotaExceeded,
|
||||
ErrorSystemFdQuotaExceeded,
|
||||
ErrorNoDevice,
|
||||
ErrorDeviceBusy,
|
||||
ErrorUnableToSpawnCCompiler,
|
||||
ErrorCCompilerExitCode,
|
||||
ErrorCCompilerCrashed,
|
||||
ErrorCCompilerCannotFindHeaders,
|
||||
ErrorLibCRuntimeNotFound,
|
||||
ErrorLibCStdLibHeaderNotFound,
|
||||
ErrorLibCKernel32LibNotFound,
|
||||
ErrorUnsupportedArchitecture,
|
||||
ErrorWindowsSdkNotFound,
|
||||
ErrorUnknownDynamicLinkerPath,
|
||||
ErrorTargetHasNoDynamicLinker,
|
||||
};
|
||||
|
||||
// ABI warning
|
||||
@ -185,7 +204,7 @@ ZIG_EXTERN_C void stage2_progress_update_node(Stage2ProgressNode *node,
|
||||
struct Stage2CpuFeatures;
|
||||
|
||||
// ABI warning
|
||||
ZIG_EXTERN_C Error stage2_cpu_features_parse(struct Stage2CpuFeatures **result,
|
||||
ZIG_EXTERN_C enum Error stage2_cpu_features_parse(struct Stage2CpuFeatures **result,
|
||||
const char *zig_triple, const char *cpu_name, const char *cpu_features);
|
||||
|
||||
// ABI warning
|
||||
@ -205,5 +224,92 @@ ZIG_EXTERN_C void stage2_cpu_features_get_cache_hash(const struct Stage2CpuFeatu
|
||||
// ABI warning
|
||||
ZIG_EXTERN_C int stage2_cmd_targets(const char *zig_triple);
|
||||
|
||||
// ABI warning
|
||||
struct Stage2LibCInstallation {
|
||||
const char *include_dir;
|
||||
size_t include_dir_len;
|
||||
const char *sys_include_dir;
|
||||
size_t sys_include_dir_len;
|
||||
const char *crt_dir;
|
||||
size_t crt_dir_len;
|
||||
const char *static_crt_dir;
|
||||
size_t static_crt_dir_len;
|
||||
const char *msvc_lib_dir;
|
||||
size_t msvc_lib_dir_len;
|
||||
const char *kernel32_lib_dir;
|
||||
size_t kernel32_lib_dir_len;
|
||||
};
|
||||
|
||||
// ABI warning
|
||||
ZIG_EXTERN_C enum Error stage2_libc_parse(struct Stage2LibCInstallation *libc, const char *libc_file);
|
||||
// ABI warning
|
||||
ZIG_EXTERN_C enum Error stage2_libc_render(struct Stage2LibCInstallation *self, FILE *file);
|
||||
// ABI warning
|
||||
ZIG_EXTERN_C enum Error stage2_libc_find_native(struct Stage2LibCInstallation *libc);
|
||||
|
||||
// ABI warning
|
||||
// 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
|
||||
203
src/target.cpp
203
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);
|
||||
|
||||
|
||||
@ -8,51 +8,10 @@
|
||||
#ifndef ZIG_TARGET_HPP
|
||||
#define ZIG_TARGET_HPP
|
||||
|
||||
#include <zig_llvm.h>
|
||||
#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);
|
||||
|
||||
|
||||
@ -6,7 +6,7 @@
|
||||
*/
|
||||
|
||||
#include "util.hpp"
|
||||
#include "userland.h"
|
||||
#include "stage2.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
@ -16,6 +16,7 @@
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
// 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
|
||||
|
||||
@ -8,7 +8,7 @@
|
||||
#ifndef ZIG_ZIG_CLANG_H
|
||||
#define ZIG_ZIG_CLANG_H
|
||||
|
||||
#include "userland.h"
|
||||
#include "stage2.h"
|
||||
#include <inttypes.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user