mirror of
https://github.com/ziglang/zig.git
synced 2025-12-06 06:13:07 +00:00
Merge pull request #19114 from ziglang/lazy-resinator
move `zig libc` command to be lazily built
This commit is contained in:
commit
9410b11ca6
@ -507,16 +507,18 @@ set(ZIG_STAGE2_SOURCES
|
||||
"${CMAKE_SOURCE_DIR}/lib/std/zig/Ast.zig"
|
||||
"${CMAKE_SOURCE_DIR}/lib/std/zig/AstGen.zig"
|
||||
"${CMAKE_SOURCE_DIR}/lib/std/zig/AstRlAnnotate.zig"
|
||||
"${CMAKE_SOURCE_DIR}/lib/std/zig/c_builtins.zig"
|
||||
"${CMAKE_SOURCE_DIR}/lib/std/zig/LibCInstallation.zig"
|
||||
"${CMAKE_SOURCE_DIR}/lib/std/zig/Parse.zig"
|
||||
"${CMAKE_SOURCE_DIR}/lib/std/zig/render.zig"
|
||||
"${CMAKE_SOURCE_DIR}/lib/std/zig/Server.zig"
|
||||
"${CMAKE_SOURCE_DIR}/lib/std/zig/WindowsSdk.zig"
|
||||
"${CMAKE_SOURCE_DIR}/lib/std/zig/Zir.zig"
|
||||
"${CMAKE_SOURCE_DIR}/lib/std/zig/c_builtins.zig"
|
||||
"${CMAKE_SOURCE_DIR}/lib/std/zig/render.zig"
|
||||
"${CMAKE_SOURCE_DIR}/lib/std/zig/string_literal.zig"
|
||||
"${CMAKE_SOURCE_DIR}/lib/std/zig/system.zig"
|
||||
"${CMAKE_SOURCE_DIR}/lib/std/zig/system/NativePaths.zig"
|
||||
"${CMAKE_SOURCE_DIR}/lib/std/zig/system/x86.zig"
|
||||
"${CMAKE_SOURCE_DIR}/lib/std/zig/tokenizer.zig"
|
||||
"${CMAKE_SOURCE_DIR}/lib/std/zig/Zir.zig"
|
||||
"${CMAKE_SOURCE_DIR}/src/Air.zig"
|
||||
"${CMAKE_SOURCE_DIR}/src/Compilation.zig"
|
||||
"${CMAKE_SOURCE_DIR}/src/Compilation/Config.zig"
|
||||
@ -570,7 +572,6 @@ set(ZIG_STAGE2_SOURCES
|
||||
"${CMAKE_SOURCE_DIR}/src/codegen/llvm/bindings.zig"
|
||||
"${CMAKE_SOURCE_DIR}/src/glibc.zig"
|
||||
"${CMAKE_SOURCE_DIR}/src/introspect.zig"
|
||||
"${CMAKE_SOURCE_DIR}/src/libc_installation.zig"
|
||||
"${CMAKE_SOURCE_DIR}/src/libcxx.zig"
|
||||
"${CMAKE_SOURCE_DIR}/src/libtsan.zig"
|
||||
"${CMAKE_SOURCE_DIR}/src/libunwind.zig"
|
||||
@ -645,7 +646,6 @@ set(ZIG_STAGE2_SOURCES
|
||||
"${CMAKE_SOURCE_DIR}/src/translate_c/ast.zig"
|
||||
"${CMAKE_SOURCE_DIR}/src/type.zig"
|
||||
"${CMAKE_SOURCE_DIR}/src/wasi_libc.zig"
|
||||
"${CMAKE_SOURCE_DIR}/src/windows_sdk.zig"
|
||||
"${CMAKE_SOURCE_DIR}/src/stubs/aro_builtins.zig"
|
||||
"${CMAKE_SOURCE_DIR}/src/stubs/aro_names.zig"
|
||||
)
|
||||
|
||||
137
lib/compiler/libc.zig
Normal file
137
lib/compiler/libc.zig
Normal file
@ -0,0 +1,137 @@
|
||||
const std = @import("std");
|
||||
const mem = std.mem;
|
||||
const io = std.io;
|
||||
const LibCInstallation = std.zig.LibCInstallation;
|
||||
|
||||
const usage_libc =
|
||||
\\Usage: zig libc
|
||||
\\
|
||||
\\ Detect the native libc installation and print the resulting
|
||||
\\ paths to stdout. You can save this into a file and then edit
|
||||
\\ the paths to create a cross compilation libc kit. Then you
|
||||
\\ can pass `--libc [file]` for Zig to use it.
|
||||
\\
|
||||
\\Usage: zig libc [paths_file]
|
||||
\\
|
||||
\\ Parse a libc installation text file and validate it.
|
||||
\\
|
||||
\\Options:
|
||||
\\ -h, --help Print this help and exit
|
||||
\\ -target [name] <arch><sub>-<os>-<abi> see the targets command
|
||||
\\ -includes Print the libc include directories for the target
|
||||
\\
|
||||
;
|
||||
|
||||
pub fn main() !void {
|
||||
var arena_instance = std.heap.ArenaAllocator.init(std.heap.page_allocator);
|
||||
defer arena_instance.deinit();
|
||||
const arena = arena_instance.allocator();
|
||||
const gpa = arena;
|
||||
|
||||
const args = try std.process.argsAlloc(arena);
|
||||
const zig_lib_directory = args[1];
|
||||
|
||||
var input_file: ?[]const u8 = null;
|
||||
var target_arch_os_abi: []const u8 = "native";
|
||||
var print_includes: bool = false;
|
||||
{
|
||||
var i: usize = 2;
|
||||
while (i < args.len) : (i += 1) {
|
||||
const arg = args[i];
|
||||
if (mem.startsWith(u8, arg, "-")) {
|
||||
if (mem.eql(u8, arg, "-h") or mem.eql(u8, arg, "--help")) {
|
||||
const stdout = std.io.getStdOut().writer();
|
||||
try stdout.writeAll(usage_libc);
|
||||
return std.process.cleanExit();
|
||||
} else if (mem.eql(u8, arg, "-target")) {
|
||||
if (i + 1 >= args.len) fatal("expected parameter after {s}", .{arg});
|
||||
i += 1;
|
||||
target_arch_os_abi = args[i];
|
||||
} else if (mem.eql(u8, arg, "-includes")) {
|
||||
print_includes = true;
|
||||
} else {
|
||||
fatal("unrecognized parameter: '{s}'", .{arg});
|
||||
}
|
||||
} else if (input_file != null) {
|
||||
fatal("unexpected extra parameter: '{s}'", .{arg});
|
||||
} else {
|
||||
input_file = arg;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const target_query = std.zig.parseTargetQueryOrReportFatalError(gpa, .{
|
||||
.arch_os_abi = target_arch_os_abi,
|
||||
});
|
||||
const target = std.zig.resolveTargetQueryOrFatal(target_query);
|
||||
|
||||
if (print_includes) {
|
||||
const libc_installation: ?*LibCInstallation = libc: {
|
||||
if (input_file) |libc_file| {
|
||||
const libc = try arena.create(LibCInstallation);
|
||||
libc.* = LibCInstallation.parse(arena, libc_file, target) catch |err| {
|
||||
fatal("unable to parse libc file at path {s}: {s}", .{ libc_file, @errorName(err) });
|
||||
};
|
||||
break :libc libc;
|
||||
} else {
|
||||
break :libc null;
|
||||
}
|
||||
};
|
||||
|
||||
const is_native_abi = target_query.isNativeAbi();
|
||||
|
||||
const libc_dirs = std.zig.LibCDirs.detect(
|
||||
arena,
|
||||
zig_lib_directory,
|
||||
target,
|
||||
is_native_abi,
|
||||
true,
|
||||
libc_installation,
|
||||
) catch |err| {
|
||||
const zig_target = try target.zigTriple(arena);
|
||||
fatal("unable to detect libc for target {s}: {s}", .{ zig_target, @errorName(err) });
|
||||
};
|
||||
|
||||
if (libc_dirs.libc_include_dir_list.len == 0) {
|
||||
const zig_target = try target.zigTriple(arena);
|
||||
fatal("no include dirs detected for target {s}", .{zig_target});
|
||||
}
|
||||
|
||||
var bw = std.io.bufferedWriter(std.io.getStdOut().writer());
|
||||
var writer = bw.writer();
|
||||
for (libc_dirs.libc_include_dir_list) |include_dir| {
|
||||
try writer.writeAll(include_dir);
|
||||
try writer.writeByte('\n');
|
||||
}
|
||||
try bw.flush();
|
||||
return std.process.cleanExit();
|
||||
}
|
||||
|
||||
if (input_file) |libc_file| {
|
||||
var libc = LibCInstallation.parse(gpa, libc_file, target) catch |err| {
|
||||
fatal("unable to parse libc file at path {s}: {s}", .{ libc_file, @errorName(err) });
|
||||
};
|
||||
defer libc.deinit(gpa);
|
||||
} else {
|
||||
if (!target_query.isNative()) {
|
||||
fatal("unable to detect libc for non-native target", .{});
|
||||
}
|
||||
var libc = LibCInstallation.findNative(.{
|
||||
.allocator = gpa,
|
||||
.verbose = true,
|
||||
.target = target,
|
||||
}) catch |err| {
|
||||
fatal("unable to detect native libc: {s}", .{@errorName(err)});
|
||||
};
|
||||
defer libc.deinit(gpa);
|
||||
|
||||
var bw = std.io.bufferedWriter(std.io.getStdOut().writer());
|
||||
try libc.render(bw.writer());
|
||||
try bw.flush();
|
||||
}
|
||||
}
|
||||
|
||||
fn fatal(comptime format: []const u8, args: anytype) noreturn {
|
||||
std.log.err(format, args);
|
||||
std.process.exit(1);
|
||||
}
|
||||
@ -2758,6 +2758,22 @@ fn eqlIgnoreCase(ignore_case: bool, a: []const u8, b: []const u8) bool {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn osArchName(target: std.Target) [:0]const u8 {
|
||||
return switch (target.os.tag) {
|
||||
.linux => switch (target.cpu.arch) {
|
||||
.arm, .armeb, .thumb, .thumbeb => "arm",
|
||||
.aarch64, .aarch64_be, .aarch64_32 => "aarch64",
|
||||
.mips, .mipsel, .mips64, .mips64el => "mips",
|
||||
.powerpc, .powerpcle, .powerpc64, .powerpc64le => "powerpc",
|
||||
.riscv32, .riscv64 => "riscv",
|
||||
.sparc, .sparcel, .sparc64 => "sparc",
|
||||
.x86, .x86_64 => "x86",
|
||||
else => @tagName(target.cpu.arch),
|
||||
},
|
||||
else => @tagName(target.cpu.arch),
|
||||
};
|
||||
}
|
||||
|
||||
const Target = @This();
|
||||
const std = @import("std.zig");
|
||||
const builtin = @import("builtin");
|
||||
|
||||
132
lib/std/zig.zig
132
lib/std/zig.zig
@ -14,6 +14,10 @@ pub const system = @import("zig/system.zig");
|
||||
pub const CrossTarget = std.Target.Query;
|
||||
pub const BuiltinFn = @import("zig/BuiltinFn.zig");
|
||||
pub const AstRlAnnotate = @import("zig/AstRlAnnotate.zig");
|
||||
pub const LibCInstallation = @import("zig/LibCInstallation.zig");
|
||||
pub const WindowsSdk = @import("zig/WindowsSdk.zig");
|
||||
pub const LibCDirs = @import("zig/LibCDirs.zig");
|
||||
pub const target = @import("zig/target.zig");
|
||||
|
||||
// Character literal parsing
|
||||
pub const ParsedCharLiteral = string_literal.ParsedCharLiteral;
|
||||
@ -142,10 +146,10 @@ pub const BinNameOptions = struct {
|
||||
/// Returns the standard file system basename of a binary generated by the Zig compiler.
|
||||
pub fn binNameAlloc(allocator: Allocator, options: BinNameOptions) error{OutOfMemory}![]u8 {
|
||||
const root_name = options.root_name;
|
||||
const target = options.target;
|
||||
switch (target.ofmt) {
|
||||
const t = options.target;
|
||||
switch (t.ofmt) {
|
||||
.coff => switch (options.output_mode) {
|
||||
.Exe => return std.fmt.allocPrint(allocator, "{s}{s}", .{ root_name, target.exeFileExt() }),
|
||||
.Exe => return std.fmt.allocPrint(allocator, "{s}{s}", .{ root_name, t.exeFileExt() }),
|
||||
.Lib => {
|
||||
const suffix = switch (options.link_mode orelse .Static) {
|
||||
.Static => ".lib",
|
||||
@ -160,16 +164,16 @@ pub fn binNameAlloc(allocator: Allocator, options: BinNameOptions) error{OutOfMe
|
||||
.Lib => {
|
||||
switch (options.link_mode orelse .Static) {
|
||||
.Static => return std.fmt.allocPrint(allocator, "{s}{s}.a", .{
|
||||
target.libPrefix(), root_name,
|
||||
t.libPrefix(), root_name,
|
||||
}),
|
||||
.Dynamic => {
|
||||
if (options.version) |ver| {
|
||||
return std.fmt.allocPrint(allocator, "{s}{s}.so.{d}.{d}.{d}", .{
|
||||
target.libPrefix(), root_name, ver.major, ver.minor, ver.patch,
|
||||
t.libPrefix(), root_name, ver.major, ver.minor, ver.patch,
|
||||
});
|
||||
} else {
|
||||
return std.fmt.allocPrint(allocator, "{s}{s}.so", .{
|
||||
target.libPrefix(), root_name,
|
||||
t.libPrefix(), root_name,
|
||||
});
|
||||
}
|
||||
},
|
||||
@ -182,16 +186,16 @@ pub fn binNameAlloc(allocator: Allocator, options: BinNameOptions) error{OutOfMe
|
||||
.Lib => {
|
||||
switch (options.link_mode orelse .Static) {
|
||||
.Static => return std.fmt.allocPrint(allocator, "{s}{s}.a", .{
|
||||
target.libPrefix(), root_name,
|
||||
t.libPrefix(), root_name,
|
||||
}),
|
||||
.Dynamic => {
|
||||
if (options.version) |ver| {
|
||||
return std.fmt.allocPrint(allocator, "{s}{s}.{d}.{d}.{d}.dylib", .{
|
||||
target.libPrefix(), root_name, ver.major, ver.minor, ver.patch,
|
||||
t.libPrefix(), root_name, ver.major, ver.minor, ver.patch,
|
||||
});
|
||||
} else {
|
||||
return std.fmt.allocPrint(allocator, "{s}{s}.dylib", .{
|
||||
target.libPrefix(), root_name,
|
||||
t.libPrefix(), root_name,
|
||||
});
|
||||
}
|
||||
},
|
||||
@ -200,11 +204,11 @@ pub fn binNameAlloc(allocator: Allocator, options: BinNameOptions) error{OutOfMe
|
||||
.Obj => return std.fmt.allocPrint(allocator, "{s}.o", .{root_name}),
|
||||
},
|
||||
.wasm => switch (options.output_mode) {
|
||||
.Exe => return std.fmt.allocPrint(allocator, "{s}{s}", .{ root_name, target.exeFileExt() }),
|
||||
.Exe => return std.fmt.allocPrint(allocator, "{s}{s}", .{ root_name, t.exeFileExt() }),
|
||||
.Lib => {
|
||||
switch (options.link_mode orelse .Static) {
|
||||
.Static => return std.fmt.allocPrint(allocator, "{s}{s}.a", .{
|
||||
target.libPrefix(), root_name,
|
||||
t.libPrefix(), root_name,
|
||||
}),
|
||||
.Dynamic => return std.fmt.allocPrint(allocator, "{s}.wasm", .{root_name}),
|
||||
}
|
||||
@ -218,10 +222,10 @@ pub fn binNameAlloc(allocator: Allocator, options: BinNameOptions) error{OutOfMe
|
||||
.plan9 => switch (options.output_mode) {
|
||||
.Exe => return allocator.dupe(u8, root_name),
|
||||
.Obj => return std.fmt.allocPrint(allocator, "{s}{s}", .{
|
||||
root_name, target.ofmt.fileExt(target.cpu.arch),
|
||||
root_name, t.ofmt.fileExt(t.cpu.arch),
|
||||
}),
|
||||
.Lib => return std.fmt.allocPrint(allocator, "{s}{s}.a", .{
|
||||
target.libPrefix(), root_name,
|
||||
t.libPrefix(), root_name,
|
||||
}),
|
||||
},
|
||||
.nvptx => return std.fmt.allocPrint(allocator, "{s}.ptx", .{root_name}),
|
||||
@ -900,15 +904,117 @@ pub fn putAstErrorsIntoBundle(
|
||||
try wip_errors.addZirErrorMessages(zir, tree, tree.source, path);
|
||||
}
|
||||
|
||||
pub fn resolveTargetQueryOrFatal(target_query: std.Target.Query) std.Target {
|
||||
return std.zig.system.resolveTargetQuery(target_query) catch |err|
|
||||
fatal("unable to resolve target: {s}", .{@errorName(err)});
|
||||
}
|
||||
|
||||
pub fn parseTargetQueryOrReportFatalError(
|
||||
allocator: Allocator,
|
||||
opts: std.Target.Query.ParseOptions,
|
||||
) std.Target.Query {
|
||||
var opts_with_diags = opts;
|
||||
var diags: std.Target.Query.ParseOptions.Diagnostics = .{};
|
||||
if (opts_with_diags.diagnostics == null) {
|
||||
opts_with_diags.diagnostics = &diags;
|
||||
}
|
||||
return std.Target.Query.parse(opts_with_diags) catch |err| switch (err) {
|
||||
error.UnknownCpuModel => {
|
||||
help: {
|
||||
var help_text = std.ArrayList(u8).init(allocator);
|
||||
defer help_text.deinit();
|
||||
for (diags.arch.?.allCpuModels()) |cpu| {
|
||||
help_text.writer().print(" {s}\n", .{cpu.name}) catch break :help;
|
||||
}
|
||||
std.log.info("available CPUs for architecture '{s}':\n{s}", .{
|
||||
@tagName(diags.arch.?), help_text.items,
|
||||
});
|
||||
}
|
||||
fatal("unknown CPU: '{s}'", .{diags.cpu_name.?});
|
||||
},
|
||||
error.UnknownCpuFeature => {
|
||||
help: {
|
||||
var help_text = std.ArrayList(u8).init(allocator);
|
||||
defer help_text.deinit();
|
||||
for (diags.arch.?.allFeaturesList()) |feature| {
|
||||
help_text.writer().print(" {s}: {s}\n", .{ feature.name, feature.description }) catch break :help;
|
||||
}
|
||||
std.log.info("available CPU features for architecture '{s}':\n{s}", .{
|
||||
@tagName(diags.arch.?), help_text.items,
|
||||
});
|
||||
}
|
||||
fatal("unknown CPU feature: '{s}'", .{diags.unknown_feature_name.?});
|
||||
},
|
||||
error.UnknownObjectFormat => {
|
||||
help: {
|
||||
var help_text = std.ArrayList(u8).init(allocator);
|
||||
defer help_text.deinit();
|
||||
inline for (@typeInfo(std.Target.ObjectFormat).Enum.fields) |field| {
|
||||
help_text.writer().print(" {s}\n", .{field.name}) catch break :help;
|
||||
}
|
||||
std.log.info("available object formats:\n{s}", .{help_text.items});
|
||||
}
|
||||
fatal("unknown object format: '{s}'", .{opts.object_format.?});
|
||||
},
|
||||
else => |e| fatal("unable to parse target query '{s}': {s}", .{
|
||||
opts.arch_os_abi, @errorName(e),
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
pub fn fatal(comptime format: []const u8, args: anytype) noreturn {
|
||||
std.log.err(format, args);
|
||||
std.process.exit(1);
|
||||
}
|
||||
|
||||
/// Collects all the environment variables that Zig could possibly inspect, so
|
||||
/// that we can do reflection on this and print them with `zig env`.
|
||||
pub const EnvVar = enum {
|
||||
ZIG_GLOBAL_CACHE_DIR,
|
||||
ZIG_LOCAL_CACHE_DIR,
|
||||
ZIG_LIB_DIR,
|
||||
ZIG_LIBC,
|
||||
ZIG_BUILD_RUNNER,
|
||||
ZIG_VERBOSE_LINK,
|
||||
ZIG_VERBOSE_CC,
|
||||
ZIG_BTRFS_WORKAROUND,
|
||||
ZIG_DEBUG_CMD,
|
||||
CC,
|
||||
NO_COLOR,
|
||||
XDG_CACHE_HOME,
|
||||
HOME,
|
||||
|
||||
pub fn isSet(comptime ev: EnvVar) bool {
|
||||
return std.process.hasEnvVarConstant(@tagName(ev));
|
||||
}
|
||||
|
||||
pub fn get(ev: EnvVar, arena: std.mem.Allocator) !?[]u8 {
|
||||
if (std.process.getEnvVarOwned(arena, @tagName(ev))) |value| {
|
||||
return value;
|
||||
} else |err| switch (err) {
|
||||
error.EnvironmentVariableNotFound => return null,
|
||||
else => |e| return e,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn getPosix(comptime ev: EnvVar) ?[:0]const u8 {
|
||||
return std.os.getenvZ(@tagName(ev));
|
||||
}
|
||||
};
|
||||
|
||||
test {
|
||||
_ = Ast;
|
||||
_ = AstRlAnnotate;
|
||||
_ = BuiltinFn;
|
||||
_ = Client;
|
||||
_ = ErrorBundle;
|
||||
_ = LibCDirs;
|
||||
_ = LibCInstallation;
|
||||
_ = Server;
|
||||
_ = WindowsSdk;
|
||||
_ = number_literal;
|
||||
_ = primitives;
|
||||
_ = string_literal;
|
||||
_ = system;
|
||||
_ = target;
|
||||
}
|
||||
|
||||
281
lib/std/zig/LibCDirs.zig
Normal file
281
lib/std/zig/LibCDirs.zig
Normal file
@ -0,0 +1,281 @@
|
||||
libc_include_dir_list: []const []const u8,
|
||||
libc_installation: ?*const LibCInstallation,
|
||||
libc_framework_dir_list: []const []const u8,
|
||||
sysroot: ?[]const u8,
|
||||
darwin_sdk_layout: ?DarwinSdkLayout,
|
||||
|
||||
/// The filesystem layout of darwin SDK elements.
|
||||
pub const DarwinSdkLayout = enum {
|
||||
/// macOS SDK layout: TOP { /usr/include, /usr/lib, /System/Library/Frameworks }.
|
||||
sdk,
|
||||
/// Shipped libc layout: TOP { /lib/libc/include, /lib/libc/darwin, <NONE> }.
|
||||
vendored,
|
||||
};
|
||||
|
||||
pub fn detect(
|
||||
arena: Allocator,
|
||||
zig_lib_dir: []const u8,
|
||||
target: std.Target,
|
||||
is_native_abi: bool,
|
||||
link_libc: bool,
|
||||
libc_installation: ?*const LibCInstallation,
|
||||
) !LibCDirs {
|
||||
if (!link_libc) {
|
||||
return .{
|
||||
.libc_include_dir_list = &[0][]u8{},
|
||||
.libc_installation = null,
|
||||
.libc_framework_dir_list = &.{},
|
||||
.sysroot = null,
|
||||
.darwin_sdk_layout = null,
|
||||
};
|
||||
}
|
||||
|
||||
if (libc_installation) |lci| {
|
||||
return detectFromInstallation(arena, target, lci);
|
||||
}
|
||||
|
||||
// If linking system libraries and targeting the native abi, default to
|
||||
// using the system libc installation.
|
||||
if (is_native_abi and !target.isMinGW()) {
|
||||
const libc = try arena.create(LibCInstallation);
|
||||
libc.* = LibCInstallation.findNative(.{ .allocator = arena, .target = target }) catch |err| switch (err) {
|
||||
error.CCompilerExitCode,
|
||||
error.CCompilerCrashed,
|
||||
error.CCompilerCannotFindHeaders,
|
||||
error.UnableToSpawnCCompiler,
|
||||
error.DarwinSdkNotFound,
|
||||
=> |e| {
|
||||
// We tried to integrate with the native system C compiler,
|
||||
// however, it is not installed. So we must rely on our bundled
|
||||
// libc files.
|
||||
if (std.zig.target.canBuildLibC(target)) {
|
||||
return detectFromBuilding(arena, zig_lib_dir, target);
|
||||
}
|
||||
return e;
|
||||
},
|
||||
else => |e| return e,
|
||||
};
|
||||
return detectFromInstallation(arena, target, libc);
|
||||
}
|
||||
|
||||
// If not linking system libraries, build and provide our own libc by
|
||||
// default if possible.
|
||||
if (std.zig.target.canBuildLibC(target)) {
|
||||
return detectFromBuilding(arena, zig_lib_dir, target);
|
||||
}
|
||||
|
||||
// If zig can't build the libc for the target and we are targeting the
|
||||
// native abi, fall back to using the system libc installation.
|
||||
// On windows, instead of the native (mingw) abi, we want to check
|
||||
// for the MSVC abi as a fallback.
|
||||
const use_system_abi = if (builtin.os.tag == .windows)
|
||||
target.abi == .msvc
|
||||
else
|
||||
is_native_abi;
|
||||
|
||||
if (use_system_abi) {
|
||||
const libc = try arena.create(LibCInstallation);
|
||||
libc.* = try LibCInstallation.findNative(.{ .allocator = arena, .verbose = true, .target = target });
|
||||
return detectFromInstallation(arena, target, libc);
|
||||
}
|
||||
|
||||
return .{
|
||||
.libc_include_dir_list = &[0][]u8{},
|
||||
.libc_installation = null,
|
||||
.libc_framework_dir_list = &.{},
|
||||
.sysroot = null,
|
||||
.darwin_sdk_layout = null,
|
||||
};
|
||||
}
|
||||
|
||||
fn detectFromInstallation(arena: Allocator, target: std.Target, lci: *const LibCInstallation) !LibCDirs {
|
||||
var list = try std.ArrayList([]const u8).initCapacity(arena, 5);
|
||||
var framework_list = std.ArrayList([]const u8).init(arena);
|
||||
|
||||
list.appendAssumeCapacity(lci.include_dir.?);
|
||||
|
||||
const is_redundant = std.mem.eql(u8, lci.sys_include_dir.?, lci.include_dir.?);
|
||||
if (!is_redundant) list.appendAssumeCapacity(lci.sys_include_dir.?);
|
||||
|
||||
if (target.os.tag == .windows) {
|
||||
if (std.fs.path.dirname(lci.sys_include_dir.?)) |sys_include_dir_parent| {
|
||||
// This include path will only exist when the optional "Desktop development with C++"
|
||||
// is installed. It contains headers, .rc files, and resources. It is especially
|
||||
// necessary when working with Windows resources.
|
||||
const atlmfc_dir = try std.fs.path.join(arena, &[_][]const u8{ sys_include_dir_parent, "atlmfc", "include" });
|
||||
list.appendAssumeCapacity(atlmfc_dir);
|
||||
}
|
||||
if (std.fs.path.dirname(lci.include_dir.?)) |include_dir_parent| {
|
||||
const um_dir = try std.fs.path.join(arena, &[_][]const u8{ include_dir_parent, "um" });
|
||||
list.appendAssumeCapacity(um_dir);
|
||||
|
||||
const shared_dir = try std.fs.path.join(arena, &[_][]const u8{ include_dir_parent, "shared" });
|
||||
list.appendAssumeCapacity(shared_dir);
|
||||
}
|
||||
}
|
||||
if (target.os.tag == .haiku) {
|
||||
const include_dir_path = lci.include_dir orelse return error.LibCInstallationNotAvailable;
|
||||
const os_dir = try std.fs.path.join(arena, &[_][]const u8{ include_dir_path, "os" });
|
||||
list.appendAssumeCapacity(os_dir);
|
||||
// Errors.h
|
||||
const os_support_dir = try std.fs.path.join(arena, &[_][]const u8{ include_dir_path, "os/support" });
|
||||
list.appendAssumeCapacity(os_support_dir);
|
||||
|
||||
const config_dir = try std.fs.path.join(arena, &[_][]const u8{ include_dir_path, "config" });
|
||||
list.appendAssumeCapacity(config_dir);
|
||||
}
|
||||
|
||||
var sysroot: ?[]const u8 = null;
|
||||
|
||||
if (target.isDarwin()) d: {
|
||||
const down1 = std.fs.path.dirname(lci.sys_include_dir.?) orelse break :d;
|
||||
const down2 = std.fs.path.dirname(down1) orelse break :d;
|
||||
try framework_list.append(try std.fs.path.join(arena, &.{ down2, "System", "Library", "Frameworks" }));
|
||||
sysroot = down2;
|
||||
}
|
||||
|
||||
return .{
|
||||
.libc_include_dir_list = list.items,
|
||||
.libc_installation = lci,
|
||||
.libc_framework_dir_list = framework_list.items,
|
||||
.sysroot = sysroot,
|
||||
.darwin_sdk_layout = if (sysroot == null) null else .sdk,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn detectFromBuilding(
|
||||
arena: Allocator,
|
||||
zig_lib_dir: []const u8,
|
||||
target: std.Target,
|
||||
) !LibCDirs {
|
||||
const s = std.fs.path.sep_str;
|
||||
|
||||
if (target.isDarwin()) {
|
||||
const list = try arena.alloc([]const u8, 1);
|
||||
list[0] = try std.fmt.allocPrint(
|
||||
arena,
|
||||
"{s}" ++ s ++ "libc" ++ s ++ "include" ++ s ++ "any-macos-any",
|
||||
.{zig_lib_dir},
|
||||
);
|
||||
return .{
|
||||
.libc_include_dir_list = list,
|
||||
.libc_installation = null,
|
||||
.libc_framework_dir_list = &.{},
|
||||
.sysroot = null,
|
||||
.darwin_sdk_layout = .vendored,
|
||||
};
|
||||
}
|
||||
|
||||
const generic_name = libCGenericName(target);
|
||||
// Some architectures are handled by the same set of headers.
|
||||
const arch_name = if (target.abi.isMusl())
|
||||
std.zig.target.muslArchNameHeaders(target.cpu.arch)
|
||||
else if (target.cpu.arch.isThumb())
|
||||
// ARM headers are valid for Thumb too.
|
||||
switch (target.cpu.arch) {
|
||||
.thumb => "arm",
|
||||
.thumbeb => "armeb",
|
||||
else => unreachable,
|
||||
}
|
||||
else
|
||||
@tagName(target.cpu.arch);
|
||||
const os_name = @tagName(target.os.tag);
|
||||
// Musl's headers are ABI-agnostic and so they all have the "musl" ABI name.
|
||||
const abi_name = if (target.abi.isMusl()) "musl" else @tagName(target.abi);
|
||||
const arch_include_dir = try std.fmt.allocPrint(
|
||||
arena,
|
||||
"{s}" ++ s ++ "libc" ++ s ++ "include" ++ s ++ "{s}-{s}-{s}",
|
||||
.{ zig_lib_dir, arch_name, os_name, abi_name },
|
||||
);
|
||||
const generic_include_dir = try std.fmt.allocPrint(
|
||||
arena,
|
||||
"{s}" ++ s ++ "libc" ++ s ++ "include" ++ s ++ "generic-{s}",
|
||||
.{ zig_lib_dir, generic_name },
|
||||
);
|
||||
const generic_arch_name = target.osArchName();
|
||||
const arch_os_include_dir = try std.fmt.allocPrint(
|
||||
arena,
|
||||
"{s}" ++ s ++ "libc" ++ s ++ "include" ++ s ++ "{s}-{s}-any",
|
||||
.{ zig_lib_dir, generic_arch_name, os_name },
|
||||
);
|
||||
const generic_os_include_dir = try std.fmt.allocPrint(
|
||||
arena,
|
||||
"{s}" ++ s ++ "libc" ++ s ++ "include" ++ s ++ "any-{s}-any",
|
||||
.{ zig_lib_dir, os_name },
|
||||
);
|
||||
|
||||
const list = try arena.alloc([]const u8, 4);
|
||||
list[0] = arch_include_dir;
|
||||
list[1] = generic_include_dir;
|
||||
list[2] = arch_os_include_dir;
|
||||
list[3] = generic_os_include_dir;
|
||||
|
||||
return .{
|
||||
.libc_include_dir_list = list,
|
||||
.libc_installation = null,
|
||||
.libc_framework_dir_list = &.{},
|
||||
.sysroot = null,
|
||||
.darwin_sdk_layout = .vendored,
|
||||
};
|
||||
}
|
||||
|
||||
fn libCGenericName(target: std.Target) [:0]const u8 {
|
||||
switch (target.os.tag) {
|
||||
.windows => return "mingw",
|
||||
.macos, .ios, .tvos, .watchos => return "darwin",
|
||||
else => {},
|
||||
}
|
||||
switch (target.abi) {
|
||||
.gnu,
|
||||
.gnuabin32,
|
||||
.gnuabi64,
|
||||
.gnueabi,
|
||||
.gnueabihf,
|
||||
.gnuf32,
|
||||
.gnuf64,
|
||||
.gnusf,
|
||||
.gnux32,
|
||||
.gnuilp32,
|
||||
=> return "glibc",
|
||||
.musl,
|
||||
.musleabi,
|
||||
.musleabihf,
|
||||
.muslx32,
|
||||
.none,
|
||||
=> return "musl",
|
||||
.code16,
|
||||
.eabi,
|
||||
.eabihf,
|
||||
.android,
|
||||
.msvc,
|
||||
.itanium,
|
||||
.cygnus,
|
||||
.coreclr,
|
||||
.simulator,
|
||||
.macabi,
|
||||
=> unreachable,
|
||||
|
||||
.pixel,
|
||||
.vertex,
|
||||
.geometry,
|
||||
.hull,
|
||||
.domain,
|
||||
.compute,
|
||||
.library,
|
||||
.raygeneration,
|
||||
.intersection,
|
||||
.anyhit,
|
||||
.closesthit,
|
||||
.miss,
|
||||
.callable,
|
||||
.mesh,
|
||||
.amplification,
|
||||
=> unreachable,
|
||||
}
|
||||
}
|
||||
|
||||
const LibCDirs = @This();
|
||||
const builtin = @import("builtin");
|
||||
const std = @import("../std.zig");
|
||||
const LibCInstallation = std.zig.LibCInstallation;
|
||||
const Allocator = std.mem.Allocator;
|
||||
709
lib/std/zig/LibCInstallation.zig
Normal file
709
lib/std/zig/LibCInstallation.zig
Normal file
@ -0,0 +1,709 @@
|
||||
//! See the render function implementation for documentation of the fields.
|
||||
|
||||
include_dir: ?[]const u8 = null,
|
||||
sys_include_dir: ?[]const u8 = null,
|
||||
crt_dir: ?[]const u8 = null,
|
||||
msvc_lib_dir: ?[]const u8 = null,
|
||||
kernel32_lib_dir: ?[]const u8 = null,
|
||||
gcc_dir: ?[]const u8 = null,
|
||||
|
||||
pub const FindError = error{
|
||||
OutOfMemory,
|
||||
FileSystem,
|
||||
UnableToSpawnCCompiler,
|
||||
CCompilerExitCode,
|
||||
CCompilerCrashed,
|
||||
CCompilerCannotFindHeaders,
|
||||
LibCRuntimeNotFound,
|
||||
LibCStdLibHeaderNotFound,
|
||||
LibCKernel32LibNotFound,
|
||||
UnsupportedArchitecture,
|
||||
WindowsSdkNotFound,
|
||||
DarwinSdkNotFound,
|
||||
ZigIsTheCCompiler,
|
||||
};
|
||||
|
||||
pub fn parse(
|
||||
allocator: Allocator,
|
||||
libc_file: []const u8,
|
||||
target: std.Target,
|
||||
) !LibCInstallation {
|
||||
var self: LibCInstallation = .{};
|
||||
|
||||
const fields = std.meta.fields(LibCInstallation);
|
||||
const FoundKey = struct {
|
||||
found: bool,
|
||||
allocated: ?[:0]u8,
|
||||
};
|
||||
var found_keys = [1]FoundKey{FoundKey{ .found = false, .allocated = null }} ** fields.len;
|
||||
errdefer {
|
||||
self = .{};
|
||||
for (found_keys) |found_key| {
|
||||
if (found_key.allocated) |s| allocator.free(s);
|
||||
}
|
||||
}
|
||||
|
||||
const contents = try std.fs.cwd().readFileAlloc(allocator, libc_file, std.math.maxInt(usize));
|
||||
defer allocator.free(contents);
|
||||
|
||||
var it = std.mem.tokenizeScalar(u8, contents, '\n');
|
||||
while (it.next()) |line| {
|
||||
if (line.len == 0 or line[0] == '#') continue;
|
||||
var line_it = std.mem.splitScalar(u8, line, '=');
|
||||
const name = line_it.first();
|
||||
const value = line_it.rest();
|
||||
inline for (fields, 0..) |field, i| {
|
||||
if (std.mem.eql(u8, name, field.name)) {
|
||||
found_keys[i].found = true;
|
||||
if (value.len == 0) {
|
||||
@field(self, field.name) = null;
|
||||
} else {
|
||||
found_keys[i].allocated = try allocator.dupeZ(u8, value);
|
||||
@field(self, field.name) = found_keys[i].allocated;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
inline for (fields, 0..) |field, i| {
|
||||
if (!found_keys[i].found) {
|
||||
log.err("missing field: {s}\n", .{field.name});
|
||||
return error.ParseError;
|
||||
}
|
||||
}
|
||||
if (self.include_dir == null) {
|
||||
log.err("include_dir may not be empty\n", .{});
|
||||
return error.ParseError;
|
||||
}
|
||||
if (self.sys_include_dir == null) {
|
||||
log.err("sys_include_dir may not be empty\n", .{});
|
||||
return error.ParseError;
|
||||
}
|
||||
|
||||
const os_tag = target.os.tag;
|
||||
if (self.crt_dir == null and !target.isDarwin()) {
|
||||
log.err("crt_dir may not be empty for {s}\n", .{@tagName(os_tag)});
|
||||
return error.ParseError;
|
||||
}
|
||||
|
||||
if (self.msvc_lib_dir == null and os_tag == .windows and target.abi == .msvc) {
|
||||
log.err("msvc_lib_dir may not be empty for {s}-{s}\n", .{
|
||||
@tagName(os_tag),
|
||||
@tagName(target.abi),
|
||||
});
|
||||
return error.ParseError;
|
||||
}
|
||||
if (self.kernel32_lib_dir == null and os_tag == .windows and target.abi == .msvc) {
|
||||
log.err("kernel32_lib_dir may not be empty for {s}-{s}\n", .{
|
||||
@tagName(os_tag),
|
||||
@tagName(target.abi),
|
||||
});
|
||||
return error.ParseError;
|
||||
}
|
||||
|
||||
if (self.gcc_dir == null and os_tag == .haiku) {
|
||||
log.err("gcc_dir may not be empty for {s}\n", .{@tagName(os_tag)});
|
||||
return error.ParseError;
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
pub fn render(self: LibCInstallation, out: anytype) !void {
|
||||
@setEvalBranchQuota(4000);
|
||||
const include_dir = self.include_dir orelse "";
|
||||
const sys_include_dir = self.sys_include_dir orelse "";
|
||||
const crt_dir = self.crt_dir orelse "";
|
||||
const msvc_lib_dir = self.msvc_lib_dir orelse "";
|
||||
const kernel32_lib_dir = self.kernel32_lib_dir orelse "";
|
||||
const gcc_dir = self.gcc_dir orelse "";
|
||||
|
||||
try out.print(
|
||||
\\# The directory that contains `stdlib.h`.
|
||||
\\# On POSIX-like systems, include directories be found with: `cc -E -Wp,-v -xc /dev/null`
|
||||
\\include_dir={s}
|
||||
\\
|
||||
\\# 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={s}
|
||||
\\
|
||||
\\# 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.
|
||||
\\crt_dir={s}
|
||||
\\
|
||||
\\# The directory that contains `vcruntime.lib`.
|
||||
\\# Only needed when targeting MSVC on Windows.
|
||||
\\msvc_lib_dir={s}
|
||||
\\
|
||||
\\# The directory that contains `kernel32.lib`.
|
||||
\\# Only needed when targeting MSVC on Windows.
|
||||
\\kernel32_lib_dir={s}
|
||||
\\
|
||||
\\# The directory that contains `crtbeginS.o` and `crtendS.o`
|
||||
\\# Only needed when targeting Haiku.
|
||||
\\gcc_dir={s}
|
||||
\\
|
||||
, .{
|
||||
include_dir,
|
||||
sys_include_dir,
|
||||
crt_dir,
|
||||
msvc_lib_dir,
|
||||
kernel32_lib_dir,
|
||||
gcc_dir,
|
||||
});
|
||||
}
|
||||
|
||||
pub const FindNativeOptions = struct {
|
||||
allocator: Allocator,
|
||||
target: std.Target,
|
||||
|
||||
/// If enabled, will print human-friendly errors to stderr.
|
||||
verbose: bool = false,
|
||||
};
|
||||
|
||||
/// Finds the default, native libc.
|
||||
pub fn findNative(args: FindNativeOptions) FindError!LibCInstallation {
|
||||
var self: LibCInstallation = .{};
|
||||
|
||||
if (is_darwin) {
|
||||
if (!std.zig.system.darwin.isSdkInstalled(args.allocator))
|
||||
return error.DarwinSdkNotFound;
|
||||
const sdk = std.zig.system.darwin.getSdk(args.allocator, args.target) orelse
|
||||
return error.DarwinSdkNotFound;
|
||||
defer args.allocator.free(sdk);
|
||||
|
||||
self.include_dir = try fs.path.join(args.allocator, &.{
|
||||
sdk, "usr/include",
|
||||
});
|
||||
self.sys_include_dir = try fs.path.join(args.allocator, &.{
|
||||
sdk, "usr/include",
|
||||
});
|
||||
return self;
|
||||
} else if (is_windows) {
|
||||
var sdk = std.zig.WindowsSdk.find(args.allocator) catch |err| switch (err) {
|
||||
error.NotFound => return error.WindowsSdkNotFound,
|
||||
error.PathTooLong => return error.WindowsSdkNotFound,
|
||||
error.OutOfMemory => return error.OutOfMemory,
|
||||
};
|
||||
defer sdk.free(args.allocator);
|
||||
|
||||
try self.findNativeMsvcIncludeDir(args, &sdk);
|
||||
try self.findNativeMsvcLibDir(args, &sdk);
|
||||
try self.findNativeKernel32LibDir(args, &sdk);
|
||||
try self.findNativeIncludeDirWindows(args, &sdk);
|
||||
try self.findNativeCrtDirWindows(args, &sdk);
|
||||
} else if (is_haiku) {
|
||||
try self.findNativeIncludeDirPosix(args);
|
||||
try self.findNativeCrtBeginDirHaiku(args);
|
||||
self.crt_dir = try args.allocator.dupeZ(u8, "/system/develop/lib");
|
||||
} else if (builtin.target.os.tag.isSolarish()) {
|
||||
// There is only one libc, and its headers/libraries are always in the same spot.
|
||||
self.include_dir = try args.allocator.dupeZ(u8, "/usr/include");
|
||||
self.sys_include_dir = try args.allocator.dupeZ(u8, "/usr/include");
|
||||
self.crt_dir = try args.allocator.dupeZ(u8, "/usr/lib/64");
|
||||
} else if (std.process.can_spawn) {
|
||||
try self.findNativeIncludeDirPosix(args);
|
||||
switch (builtin.target.os.tag) {
|
||||
.freebsd, .netbsd, .openbsd, .dragonfly => self.crt_dir = try args.allocator.dupeZ(u8, "/usr/lib"),
|
||||
.linux => try self.findNativeCrtDirPosix(args),
|
||||
else => {},
|
||||
}
|
||||
} else {
|
||||
return error.LibCRuntimeNotFound;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
/// Must be the same allocator passed to `parse` or `findNative`.
|
||||
pub fn deinit(self: *LibCInstallation, allocator: Allocator) void {
|
||||
const fields = std.meta.fields(LibCInstallation);
|
||||
inline for (fields) |field| {
|
||||
if (@field(self, field.name)) |payload| {
|
||||
allocator.free(payload);
|
||||
}
|
||||
}
|
||||
self.* = undefined;
|
||||
}
|
||||
|
||||
fn findNativeIncludeDirPosix(self: *LibCInstallation, args: FindNativeOptions) FindError!void {
|
||||
const allocator = args.allocator;
|
||||
|
||||
// Detect infinite loops.
|
||||
var env_map = std.process.getEnvMap(allocator) catch |err| switch (err) {
|
||||
error.Unexpected => unreachable, // WASI-only
|
||||
else => |e| return e,
|
||||
};
|
||||
defer env_map.deinit();
|
||||
const skip_cc_env_var = if (env_map.get(inf_loop_env_key)) |phase| blk: {
|
||||
if (std.mem.eql(u8, phase, "1")) {
|
||||
try env_map.put(inf_loop_env_key, "2");
|
||||
break :blk true;
|
||||
} else {
|
||||
return error.ZigIsTheCCompiler;
|
||||
}
|
||||
} else blk: {
|
||||
try env_map.put(inf_loop_env_key, "1");
|
||||
break :blk false;
|
||||
};
|
||||
|
||||
const dev_null = if (is_windows) "nul" else "/dev/null";
|
||||
|
||||
var argv = std.ArrayList([]const u8).init(allocator);
|
||||
defer argv.deinit();
|
||||
|
||||
try appendCcExe(&argv, skip_cc_env_var);
|
||||
try argv.appendSlice(&.{
|
||||
"-E",
|
||||
"-Wp,-v",
|
||||
"-xc",
|
||||
dev_null,
|
||||
});
|
||||
|
||||
const run_res = std.ChildProcess.run(.{
|
||||
.allocator = allocator,
|
||||
.argv = argv.items,
|
||||
.max_output_bytes = 1024 * 1024,
|
||||
.env_map = &env_map,
|
||||
// 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 => {
|
||||
printVerboseInvocation(argv.items, null, args.verbose, null);
|
||||
return error.UnableToSpawnCCompiler;
|
||||
},
|
||||
};
|
||||
defer {
|
||||
allocator.free(run_res.stdout);
|
||||
allocator.free(run_res.stderr);
|
||||
}
|
||||
switch (run_res.term) {
|
||||
.Exited => |code| if (code != 0) {
|
||||
printVerboseInvocation(argv.items, null, args.verbose, run_res.stderr);
|
||||
return error.CCompilerExitCode;
|
||||
},
|
||||
else => {
|
||||
printVerboseInvocation(argv.items, null, args.verbose, run_res.stderr);
|
||||
return error.CCompilerCrashed;
|
||||
},
|
||||
}
|
||||
|
||||
var it = std.mem.tokenizeAny(u8, run_res.stderr, "\n\r");
|
||||
var search_paths = std.ArrayList([]const u8).init(allocator);
|
||||
defer search_paths.deinit();
|
||||
while (it.next()) |line| {
|
||||
if (line.len != 0 and line[0] == ' ') {
|
||||
try search_paths.append(line);
|
||||
}
|
||||
}
|
||||
if (search_paths.items.len == 0) {
|
||||
return error.CCompilerCannotFindHeaders;
|
||||
}
|
||||
|
||||
const include_dir_example_file = if (is_haiku) "posix/stdlib.h" else "stdlib.h";
|
||||
const sys_include_dir_example_file = if (is_windows)
|
||||
"sys\\types.h"
|
||||
else if (is_haiku)
|
||||
"errno.h"
|
||||
else
|
||||
"sys/errno.h";
|
||||
|
||||
var path_i: usize = 0;
|
||||
while (path_i < search_paths.items.len) : (path_i += 1) {
|
||||
// search in reverse order
|
||||
const search_path_untrimmed = search_paths.items[search_paths.items.len - path_i - 1];
|
||||
const search_path = std.mem.trimLeft(u8, search_path_untrimmed, " ");
|
||||
var search_dir = fs.cwd().openDir(search_path, .{}) catch |err| switch (err) {
|
||||
error.FileNotFound,
|
||||
error.NotDir,
|
||||
error.NoDevice,
|
||||
=> continue,
|
||||
|
||||
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 allocator.dupeZ(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 allocator.dupeZ(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;
|
||||
}
|
||||
}
|
||||
|
||||
return error.LibCStdLibHeaderNotFound;
|
||||
}
|
||||
|
||||
fn findNativeIncludeDirWindows(
|
||||
self: *LibCInstallation,
|
||||
args: FindNativeOptions,
|
||||
sdk: *std.zig.WindowsSdk,
|
||||
) FindError!void {
|
||||
const allocator = args.allocator;
|
||||
|
||||
var search_buf: [2]Search = undefined;
|
||||
const searches = fillSearch(&search_buf, sdk);
|
||||
|
||||
var result_buf = std.ArrayList(u8).init(allocator);
|
||||
defer result_buf.deinit();
|
||||
|
||||
for (searches) |search| {
|
||||
result_buf.shrinkAndFree(0);
|
||||
try result_buf.writer().print("{s}\\Include\\{s}\\ucrt", .{ search.path, search.version });
|
||||
|
||||
var dir = fs.cwd().openDir(result_buf.items, .{}) catch |err| switch (err) {
|
||||
error.FileNotFound,
|
||||
error.NotDir,
|
||||
error.NoDevice,
|
||||
=> continue,
|
||||
|
||||
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 = try result_buf.toOwnedSlice();
|
||||
return;
|
||||
}
|
||||
|
||||
return error.LibCStdLibHeaderNotFound;
|
||||
}
|
||||
|
||||
fn findNativeCrtDirWindows(
|
||||
self: *LibCInstallation,
|
||||
args: FindNativeOptions,
|
||||
sdk: *std.zig.WindowsSdk,
|
||||
) FindError!void {
|
||||
const allocator = args.allocator;
|
||||
|
||||
var search_buf: [2]Search = undefined;
|
||||
const searches = fillSearch(&search_buf, sdk);
|
||||
|
||||
var result_buf = std.ArrayList(u8).init(allocator);
|
||||
defer result_buf.deinit();
|
||||
|
||||
const arch_sub_dir = switch (builtin.target.cpu.arch) {
|
||||
.x86 => "x86",
|
||||
.x86_64 => "x64",
|
||||
.arm, .armeb => "arm",
|
||||
.aarch64 => "arm64",
|
||||
else => return error.UnsupportedArchitecture,
|
||||
};
|
||||
|
||||
for (searches) |search| {
|
||||
result_buf.shrinkAndFree(0);
|
||||
try result_buf.writer().print("{s}\\Lib\\{s}\\ucrt\\{s}", .{ search.path, search.version, arch_sub_dir });
|
||||
|
||||
var dir = fs.cwd().openDir(result_buf.items, .{}) 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 = try result_buf.toOwnedSlice();
|
||||
return;
|
||||
}
|
||||
return error.LibCRuntimeNotFound;
|
||||
}
|
||||
|
||||
fn findNativeCrtDirPosix(self: *LibCInstallation, args: FindNativeOptions) FindError!void {
|
||||
self.crt_dir = try ccPrintFileName(.{
|
||||
.allocator = args.allocator,
|
||||
.search_basename = "crt1.o",
|
||||
.want_dirname = .only_dir,
|
||||
.verbose = args.verbose,
|
||||
});
|
||||
}
|
||||
|
||||
fn findNativeCrtBeginDirHaiku(self: *LibCInstallation, args: FindNativeOptions) FindError!void {
|
||||
self.gcc_dir = try ccPrintFileName(.{
|
||||
.allocator = args.allocator,
|
||||
.search_basename = "crtbeginS.o",
|
||||
.want_dirname = .only_dir,
|
||||
.verbose = args.verbose,
|
||||
});
|
||||
}
|
||||
|
||||
fn findNativeKernel32LibDir(
|
||||
self: *LibCInstallation,
|
||||
args: FindNativeOptions,
|
||||
sdk: *std.zig.WindowsSdk,
|
||||
) FindError!void {
|
||||
const allocator = args.allocator;
|
||||
|
||||
var search_buf: [2]Search = undefined;
|
||||
const searches = fillSearch(&search_buf, sdk);
|
||||
|
||||
var result_buf = std.ArrayList(u8).init(allocator);
|
||||
defer result_buf.deinit();
|
||||
|
||||
const arch_sub_dir = switch (builtin.target.cpu.arch) {
|
||||
.x86 => "x86",
|
||||
.x86_64 => "x64",
|
||||
.arm, .armeb => "arm",
|
||||
.aarch64 => "arm64",
|
||||
else => return error.UnsupportedArchitecture,
|
||||
};
|
||||
|
||||
for (searches) |search| {
|
||||
result_buf.shrinkAndFree(0);
|
||||
const stream = result_buf.writer();
|
||||
try stream.print("{s}\\Lib\\{s}\\um\\{s}", .{ search.path, search.version, arch_sub_dir });
|
||||
|
||||
var dir = fs.cwd().openDir(result_buf.items, .{}) 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 = try result_buf.toOwnedSlice();
|
||||
return;
|
||||
}
|
||||
return error.LibCKernel32LibNotFound;
|
||||
}
|
||||
|
||||
fn findNativeMsvcIncludeDir(
|
||||
self: *LibCInstallation,
|
||||
args: FindNativeOptions,
|
||||
sdk: *std.zig.WindowsSdk,
|
||||
) FindError!void {
|
||||
const allocator = args.allocator;
|
||||
|
||||
const msvc_lib_dir = sdk.msvc_lib_dir orelse return error.LibCStdLibHeaderNotFound;
|
||||
const up1 = fs.path.dirname(msvc_lib_dir) orelse return error.LibCStdLibHeaderNotFound;
|
||||
const up2 = fs.path.dirname(up1) orelse return error.LibCStdLibHeaderNotFound;
|
||||
|
||||
const dir_path = try fs.path.join(allocator, &[_][]const u8{ up2, "include" });
|
||||
errdefer allocator.free(dir_path);
|
||||
|
||||
var dir = fs.cwd().openDir(dir_path, .{}) 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 = dir_path;
|
||||
}
|
||||
|
||||
fn findNativeMsvcLibDir(
|
||||
self: *LibCInstallation,
|
||||
args: FindNativeOptions,
|
||||
sdk: *std.zig.WindowsSdk,
|
||||
) FindError!void {
|
||||
const allocator = args.allocator;
|
||||
const msvc_lib_dir = sdk.msvc_lib_dir orelse return error.LibCRuntimeNotFound;
|
||||
self.msvc_lib_dir = try allocator.dupe(u8, msvc_lib_dir);
|
||||
}
|
||||
|
||||
pub const CCPrintFileNameOptions = struct {
|
||||
allocator: Allocator,
|
||||
search_basename: []const u8,
|
||||
want_dirname: enum { full_path, only_dir },
|
||||
verbose: bool = false,
|
||||
};
|
||||
|
||||
/// caller owns returned memory
|
||||
fn ccPrintFileName(args: CCPrintFileNameOptions) ![:0]u8 {
|
||||
const allocator = args.allocator;
|
||||
|
||||
// Detect infinite loops.
|
||||
var env_map = std.process.getEnvMap(allocator) catch |err| switch (err) {
|
||||
error.Unexpected => unreachable, // WASI-only
|
||||
else => |e| return e,
|
||||
};
|
||||
defer env_map.deinit();
|
||||
const skip_cc_env_var = if (env_map.get(inf_loop_env_key)) |phase| blk: {
|
||||
if (std.mem.eql(u8, phase, "1")) {
|
||||
try env_map.put(inf_loop_env_key, "2");
|
||||
break :blk true;
|
||||
} else {
|
||||
return error.ZigIsTheCCompiler;
|
||||
}
|
||||
} else blk: {
|
||||
try env_map.put(inf_loop_env_key, "1");
|
||||
break :blk false;
|
||||
};
|
||||
|
||||
var argv = std.ArrayList([]const u8).init(allocator);
|
||||
defer argv.deinit();
|
||||
|
||||
const arg1 = try std.fmt.allocPrint(allocator, "-print-file-name={s}", .{args.search_basename});
|
||||
defer allocator.free(arg1);
|
||||
|
||||
try appendCcExe(&argv, skip_cc_env_var);
|
||||
try argv.append(arg1);
|
||||
|
||||
const run_res = std.ChildProcess.run(.{
|
||||
.allocator = allocator,
|
||||
.argv = argv.items,
|
||||
.max_output_bytes = 1024 * 1024,
|
||||
.env_map = &env_map,
|
||||
// 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(run_res.stdout);
|
||||
allocator.free(run_res.stderr);
|
||||
}
|
||||
switch (run_res.term) {
|
||||
.Exited => |code| if (code != 0) {
|
||||
printVerboseInvocation(argv.items, args.search_basename, args.verbose, run_res.stderr);
|
||||
return error.CCompilerExitCode;
|
||||
},
|
||||
else => {
|
||||
printVerboseInvocation(argv.items, args.search_basename, args.verbose, run_res.stderr);
|
||||
return error.CCompilerCrashed;
|
||||
},
|
||||
}
|
||||
|
||||
var it = std.mem.tokenizeAny(u8, run_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, args.search_basename)) return error.LibCRuntimeNotFound;
|
||||
switch (args.want_dirname) {
|
||||
.full_path => return allocator.dupeZ(u8, line),
|
||||
.only_dir => {
|
||||
const dirname = fs.path.dirname(line) orelse return error.LibCRuntimeNotFound;
|
||||
return allocator.dupeZ(u8, dirname);
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn printVerboseInvocation(
|
||||
argv: []const []const u8,
|
||||
search_basename: ?[]const u8,
|
||||
verbose: bool,
|
||||
stderr: ?[]const u8,
|
||||
) void {
|
||||
if (!verbose) return;
|
||||
|
||||
if (search_basename) |s| {
|
||||
std.debug.print("Zig attempted to find the file '{s}' by executing this command:\n", .{s});
|
||||
} else {
|
||||
std.debug.print("Zig attempted to find the path to native system libc headers by executing this command:\n", .{});
|
||||
}
|
||||
for (argv, 0..) |arg, i| {
|
||||
if (i != 0) std.debug.print(" ", .{});
|
||||
std.debug.print("{s}", .{arg});
|
||||
}
|
||||
std.debug.print("\n", .{});
|
||||
if (stderr) |s| {
|
||||
std.debug.print("Output:\n==========\n{s}\n==========\n", .{s});
|
||||
}
|
||||
}
|
||||
|
||||
const Search = struct {
|
||||
path: []const u8,
|
||||
version: []const u8,
|
||||
};
|
||||
|
||||
fn fillSearch(search_buf: *[2]Search, sdk: *std.zig.WindowsSdk) []Search {
|
||||
var search_end: usize = 0;
|
||||
if (sdk.windows10sdk) |windows10sdk| {
|
||||
search_buf[search_end] = .{
|
||||
.path = windows10sdk.path,
|
||||
.version = windows10sdk.version,
|
||||
};
|
||||
search_end += 1;
|
||||
}
|
||||
if (sdk.windows81sdk) |windows81sdk| {
|
||||
search_buf[search_end] = .{
|
||||
.path = windows81sdk.path,
|
||||
.version = windows81sdk.version,
|
||||
};
|
||||
search_end += 1;
|
||||
}
|
||||
return search_buf[0..search_end];
|
||||
}
|
||||
|
||||
const inf_loop_env_key = "ZIG_IS_DETECTING_LIBC_PATHS";
|
||||
|
||||
fn appendCcExe(args: *std.ArrayList([]const u8), skip_cc_env_var: bool) !void {
|
||||
const default_cc_exe = if (is_windows) "cc.exe" else "cc";
|
||||
try args.ensureUnusedCapacity(1);
|
||||
if (skip_cc_env_var) {
|
||||
args.appendAssumeCapacity(default_cc_exe);
|
||||
return;
|
||||
}
|
||||
const cc_env_var = std.zig.EnvVar.CC.getPosix() orelse {
|
||||
args.appendAssumeCapacity(default_cc_exe);
|
||||
return;
|
||||
};
|
||||
// Respect space-separated flags to the C compiler.
|
||||
var it = std.mem.tokenizeScalar(u8, cc_env_var, ' ');
|
||||
while (it.next()) |arg| {
|
||||
try args.append(arg);
|
||||
}
|
||||
}
|
||||
|
||||
const LibCInstallation = @This();
|
||||
const std = @import("std");
|
||||
const builtin = @import("builtin");
|
||||
const Target = std.Target;
|
||||
const fs = std.fs;
|
||||
const Allocator = std.mem.Allocator;
|
||||
|
||||
const is_darwin = builtin.target.isDarwin();
|
||||
const is_windows = builtin.target.os.tag == .windows;
|
||||
const is_haiku = builtin.target.os.tag == .haiku;
|
||||
|
||||
const log = std.log.scoped(.libc_installation);
|
||||
@ -1,3 +1,8 @@
|
||||
windows10sdk: ?Windows10Sdk,
|
||||
windows81sdk: ?Windows81Sdk,
|
||||
msvc_lib_dir: ?[]const u8,
|
||||
|
||||
const WindowsSdk = @This();
|
||||
const std = @import("std");
|
||||
const builtin = @import("builtin");
|
||||
|
||||
@ -11,6 +16,69 @@ const version_major_minor_max_length = "255.255".len;
|
||||
// note(bratishkaerik): i think ProductVersion in registry (created by Visual Studio installer) also follows this rule
|
||||
const product_version_max_length = version_major_minor_max_length + ".65535".len;
|
||||
|
||||
/// Find path and version of Windows 10 SDK and Windows 8.1 SDK, and find path to MSVC's `lib/` directory.
|
||||
/// Caller owns the result's fields.
|
||||
/// After finishing work, call `free(allocator)`.
|
||||
pub fn find(allocator: std.mem.Allocator) error{ OutOfMemory, NotFound, PathTooLong }!WindowsSdk {
|
||||
if (builtin.os.tag != .windows) return error.NotFound;
|
||||
|
||||
//note(dimenus): If this key doesn't exist, neither the Win 8 SDK nor the Win 10 SDK is installed
|
||||
const roots_key = RegistryWtf8.openKey(windows.HKEY_LOCAL_MACHINE, WINDOWS_KIT_REG_KEY) catch |err| switch (err) {
|
||||
error.KeyNotFound => return error.NotFound,
|
||||
};
|
||||
defer roots_key.closeKey();
|
||||
|
||||
const windows10sdk: ?Windows10Sdk = blk: {
|
||||
const windows10sdk = Windows10Sdk.find(allocator) catch |err| switch (err) {
|
||||
error.Windows10SdkNotFound,
|
||||
error.PathTooLong,
|
||||
error.VersionTooLong,
|
||||
=> break :blk null,
|
||||
error.OutOfMemory => return error.OutOfMemory,
|
||||
};
|
||||
const is_valid_version = windows10sdk.isValidVersion();
|
||||
if (!is_valid_version) break :blk null;
|
||||
break :blk windows10sdk;
|
||||
};
|
||||
errdefer if (windows10sdk) |*w| w.free(allocator);
|
||||
|
||||
const windows81sdk: ?Windows81Sdk = blk: {
|
||||
const windows81sdk = Windows81Sdk.find(allocator, &roots_key) catch |err| switch (err) {
|
||||
error.Windows81SdkNotFound => break :blk null,
|
||||
error.PathTooLong => break :blk null,
|
||||
error.VersionTooLong => break :blk null,
|
||||
error.OutOfMemory => return error.OutOfMemory,
|
||||
};
|
||||
// no check
|
||||
break :blk windows81sdk;
|
||||
};
|
||||
errdefer if (windows81sdk) |*w| w.free(allocator);
|
||||
|
||||
const msvc_lib_dir: ?[]const u8 = MsvcLibDir.find(allocator) catch |err| switch (err) {
|
||||
error.MsvcLibDirNotFound => null,
|
||||
error.OutOfMemory => return error.OutOfMemory,
|
||||
};
|
||||
errdefer allocator.free(msvc_lib_dir);
|
||||
|
||||
return WindowsSdk{
|
||||
.windows10sdk = windows10sdk,
|
||||
.windows81sdk = windows81sdk,
|
||||
.msvc_lib_dir = msvc_lib_dir,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn free(self: *const WindowsSdk, allocator: std.mem.Allocator) void {
|
||||
if (self.windows10sdk) |*w10sdk| {
|
||||
w10sdk.free(allocator);
|
||||
}
|
||||
if (self.windows81sdk) |*w81sdk| {
|
||||
w81sdk.free(allocator);
|
||||
}
|
||||
if (self.msvc_lib_dir) |msvc_lib_dir| {
|
||||
allocator.free(msvc_lib_dir);
|
||||
}
|
||||
}
|
||||
|
||||
/// Iterates via `iterator` and collects all folders with names starting with `optional_prefix`
|
||||
/// and similar to SemVer. Returns slice of folder names sorted in descending order.
|
||||
/// Caller owns result.
|
||||
@ -508,75 +576,6 @@ pub const Windows81Sdk = struct {
|
||||
}
|
||||
};
|
||||
|
||||
pub const ZigWindowsSDK = struct {
|
||||
windows10sdk: ?Windows10Sdk,
|
||||
windows81sdk: ?Windows81Sdk,
|
||||
msvc_lib_dir: ?[]const u8,
|
||||
|
||||
/// Find path and version of Windows 10 SDK and Windows 8.1 SDK, and find path to MSVC's `lib/` directory.
|
||||
/// Caller owns the result's fields.
|
||||
/// After finishing work, call `free(allocator)`.
|
||||
pub fn find(allocator: std.mem.Allocator) error{ OutOfMemory, NotFound, PathTooLong }!ZigWindowsSDK {
|
||||
if (builtin.os.tag != .windows) return error.NotFound;
|
||||
|
||||
//note(dimenus): If this key doesn't exist, neither the Win 8 SDK nor the Win 10 SDK is installed
|
||||
const roots_key = RegistryWtf8.openKey(windows.HKEY_LOCAL_MACHINE, WINDOWS_KIT_REG_KEY) catch |err| switch (err) {
|
||||
error.KeyNotFound => return error.NotFound,
|
||||
};
|
||||
defer roots_key.closeKey();
|
||||
|
||||
const windows10sdk: ?Windows10Sdk = blk: {
|
||||
const windows10sdk = Windows10Sdk.find(allocator) catch |err| switch (err) {
|
||||
error.Windows10SdkNotFound,
|
||||
error.PathTooLong,
|
||||
error.VersionTooLong,
|
||||
=> break :blk null,
|
||||
error.OutOfMemory => return error.OutOfMemory,
|
||||
};
|
||||
const is_valid_version = windows10sdk.isValidVersion();
|
||||
if (!is_valid_version) break :blk null;
|
||||
break :blk windows10sdk;
|
||||
};
|
||||
errdefer if (windows10sdk) |*w| w.free(allocator);
|
||||
|
||||
const windows81sdk: ?Windows81Sdk = blk: {
|
||||
const windows81sdk = Windows81Sdk.find(allocator, &roots_key) catch |err| switch (err) {
|
||||
error.Windows81SdkNotFound => break :blk null,
|
||||
error.PathTooLong => break :blk null,
|
||||
error.VersionTooLong => break :blk null,
|
||||
error.OutOfMemory => return error.OutOfMemory,
|
||||
};
|
||||
// no check
|
||||
break :blk windows81sdk;
|
||||
};
|
||||
errdefer if (windows81sdk) |*w| w.free(allocator);
|
||||
|
||||
const msvc_lib_dir: ?[]const u8 = MsvcLibDir.find(allocator) catch |err| switch (err) {
|
||||
error.MsvcLibDirNotFound => null,
|
||||
error.OutOfMemory => return error.OutOfMemory,
|
||||
};
|
||||
errdefer allocator.free(msvc_lib_dir);
|
||||
|
||||
return ZigWindowsSDK{
|
||||
.windows10sdk = windows10sdk,
|
||||
.windows81sdk = windows81sdk,
|
||||
.msvc_lib_dir = msvc_lib_dir,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn free(self: *const ZigWindowsSDK, allocator: std.mem.Allocator) void {
|
||||
if (self.windows10sdk) |*w10sdk| {
|
||||
w10sdk.free(allocator);
|
||||
}
|
||||
if (self.windows81sdk) |*w81sdk| {
|
||||
w81sdk.free(allocator);
|
||||
}
|
||||
if (self.msvc_lib_dir) |msvc_lib_dir| {
|
||||
allocator.free(msvc_lib_dir);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const MsvcLibDir = struct {
|
||||
fn findInstancesDirViaCLSID(allocator: std.mem.Allocator) error{ OutOfMemory, PathNotFound }!std.fs.Dir {
|
||||
const setup_configuration_clsid = "{177f0c4a-1cd3-4de7-a32c-71dbbb9fa36d}";
|
||||
117
lib/std/zig/target.zig
Normal file
117
lib/std/zig/target.zig
Normal file
@ -0,0 +1,117 @@
|
||||
pub const ArchOsAbi = struct {
|
||||
arch: std.Target.Cpu.Arch,
|
||||
os: std.Target.Os.Tag,
|
||||
abi: std.Target.Abi,
|
||||
os_ver: ?std.SemanticVersion = null,
|
||||
|
||||
// Minimum glibc version that provides support for the arch/os when ABI is GNU.
|
||||
glibc_min: ?std.SemanticVersion = null,
|
||||
};
|
||||
|
||||
pub const available_libcs = [_]ArchOsAbi{
|
||||
.{ .arch = .aarch64_be, .os = .linux, .abi = .gnu, .glibc_min = .{ .major = 2, .minor = 17, .patch = 0 } },
|
||||
.{ .arch = .aarch64_be, .os = .linux, .abi = .musl },
|
||||
.{ .arch = .aarch64_be, .os = .windows, .abi = .gnu },
|
||||
.{ .arch = .aarch64, .os = .linux, .abi = .gnu },
|
||||
.{ .arch = .aarch64, .os = .linux, .abi = .musl },
|
||||
.{ .arch = .aarch64, .os = .windows, .abi = .gnu },
|
||||
.{ .arch = .aarch64, .os = .macos, .abi = .none, .os_ver = .{ .major = 11, .minor = 0, .patch = 0 } },
|
||||
.{ .arch = .armeb, .os = .linux, .abi = .gnueabi },
|
||||
.{ .arch = .armeb, .os = .linux, .abi = .gnueabihf },
|
||||
.{ .arch = .armeb, .os = .linux, .abi = .musleabi },
|
||||
.{ .arch = .armeb, .os = .linux, .abi = .musleabihf },
|
||||
.{ .arch = .armeb, .os = .windows, .abi = .gnu },
|
||||
.{ .arch = .arm, .os = .linux, .abi = .gnueabi },
|
||||
.{ .arch = .arm, .os = .linux, .abi = .gnueabihf },
|
||||
.{ .arch = .arm, .os = .linux, .abi = .musleabi },
|
||||
.{ .arch = .arm, .os = .linux, .abi = .musleabihf },
|
||||
.{ .arch = .thumb, .os = .linux, .abi = .gnueabi },
|
||||
.{ .arch = .thumb, .os = .linux, .abi = .gnueabihf },
|
||||
.{ .arch = .thumb, .os = .linux, .abi = .musleabi },
|
||||
.{ .arch = .thumb, .os = .linux, .abi = .musleabihf },
|
||||
.{ .arch = .arm, .os = .windows, .abi = .gnu },
|
||||
.{ .arch = .csky, .os = .linux, .abi = .gnueabi },
|
||||
.{ .arch = .csky, .os = .linux, .abi = .gnueabihf },
|
||||
.{ .arch = .x86, .os = .linux, .abi = .gnu },
|
||||
.{ .arch = .x86, .os = .linux, .abi = .musl },
|
||||
.{ .arch = .x86, .os = .windows, .abi = .gnu },
|
||||
.{ .arch = .m68k, .os = .linux, .abi = .gnu },
|
||||
.{ .arch = .m68k, .os = .linux, .abi = .musl },
|
||||
.{ .arch = .mips64el, .os = .linux, .abi = .gnuabi64 },
|
||||
.{ .arch = .mips64el, .os = .linux, .abi = .gnuabin32 },
|
||||
.{ .arch = .mips64el, .os = .linux, .abi = .musl },
|
||||
.{ .arch = .mips64, .os = .linux, .abi = .gnuabi64 },
|
||||
.{ .arch = .mips64, .os = .linux, .abi = .gnuabin32 },
|
||||
.{ .arch = .mips64, .os = .linux, .abi = .musl },
|
||||
.{ .arch = .mipsel, .os = .linux, .abi = .gnueabi },
|
||||
.{ .arch = .mipsel, .os = .linux, .abi = .gnueabihf },
|
||||
.{ .arch = .mipsel, .os = .linux, .abi = .musl },
|
||||
.{ .arch = .mips, .os = .linux, .abi = .gnueabi },
|
||||
.{ .arch = .mips, .os = .linux, .abi = .gnueabihf },
|
||||
.{ .arch = .mips, .os = .linux, .abi = .musl },
|
||||
.{ .arch = .powerpc64le, .os = .linux, .abi = .gnu, .glibc_min = .{ .major = 2, .minor = 19, .patch = 0 } },
|
||||
.{ .arch = .powerpc64le, .os = .linux, .abi = .musl },
|
||||
.{ .arch = .powerpc64, .os = .linux, .abi = .gnu },
|
||||
.{ .arch = .powerpc64, .os = .linux, .abi = .musl },
|
||||
.{ .arch = .powerpc, .os = .linux, .abi = .gnueabi },
|
||||
.{ .arch = .powerpc, .os = .linux, .abi = .gnueabihf },
|
||||
.{ .arch = .powerpc, .os = .linux, .abi = .musl },
|
||||
.{ .arch = .riscv64, .os = .linux, .abi = .gnu, .glibc_min = .{ .major = 2, .minor = 27, .patch = 0 } },
|
||||
.{ .arch = .riscv64, .os = .linux, .abi = .musl },
|
||||
.{ .arch = .s390x, .os = .linux, .abi = .gnu },
|
||||
.{ .arch = .s390x, .os = .linux, .abi = .musl },
|
||||
.{ .arch = .sparc, .os = .linux, .abi = .gnu },
|
||||
.{ .arch = .sparc64, .os = .linux, .abi = .gnu },
|
||||
.{ .arch = .wasm32, .os = .freestanding, .abi = .musl },
|
||||
.{ .arch = .wasm32, .os = .wasi, .abi = .musl },
|
||||
.{ .arch = .x86_64, .os = .linux, .abi = .gnu },
|
||||
.{ .arch = .x86_64, .os = .linux, .abi = .gnux32 },
|
||||
.{ .arch = .x86_64, .os = .linux, .abi = .musl },
|
||||
.{ .arch = .x86_64, .os = .windows, .abi = .gnu },
|
||||
.{ .arch = .x86_64, .os = .macos, .abi = .none, .os_ver = .{ .major = 10, .minor = 7, .patch = 0 } },
|
||||
};
|
||||
|
||||
pub fn canBuildLibC(target: std.Target) bool {
|
||||
for (available_libcs) |libc| {
|
||||
if (target.cpu.arch == libc.arch and target.os.tag == libc.os and target.abi == libc.abi) {
|
||||
if (target.os.tag == .macos) {
|
||||
const ver = target.os.version_range.semver;
|
||||
return ver.min.order(libc.os_ver.?) != .lt;
|
||||
}
|
||||
// Ensure glibc (aka *-linux-gnu) version is supported
|
||||
if (target.isGnuLibC()) {
|
||||
const min_glibc_ver = libc.glibc_min orelse return true;
|
||||
const target_glibc_ver = target.os.version_range.linux.glibc;
|
||||
return target_glibc_ver.order(min_glibc_ver) != .lt;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
pub fn muslArchNameHeaders(arch: std.Target.Cpu.Arch) [:0]const u8 {
|
||||
return switch (arch) {
|
||||
.x86 => return "x86",
|
||||
else => muslArchName(arch),
|
||||
};
|
||||
}
|
||||
|
||||
pub fn muslArchName(arch: std.Target.Cpu.Arch) [:0]const u8 {
|
||||
switch (arch) {
|
||||
.aarch64, .aarch64_be => return "aarch64",
|
||||
.arm, .armeb, .thumb, .thumbeb => return "arm",
|
||||
.x86 => return "i386",
|
||||
.mips, .mipsel => return "mips",
|
||||
.mips64el, .mips64 => return "mips64",
|
||||
.powerpc => return "powerpc",
|
||||
.powerpc64, .powerpc64le => return "powerpc64",
|
||||
.riscv64 => return "riscv64",
|
||||
.s390x => return "s390x",
|
||||
.wasm32, .wasm64 => return "wasm",
|
||||
.x86_64 => return "x86_64",
|
||||
else => unreachable,
|
||||
}
|
||||
}
|
||||
|
||||
const std = @import("std");
|
||||
@ -19,7 +19,7 @@ const link = @import("link.zig");
|
||||
const tracy = @import("tracy.zig");
|
||||
const trace = tracy.trace;
|
||||
const build_options = @import("build_options");
|
||||
const LibCInstallation = @import("libc_installation.zig").LibCInstallation;
|
||||
const LibCInstallation = std.zig.LibCInstallation;
|
||||
const glibc = @import("glibc.zig");
|
||||
const musl = @import("musl.zig");
|
||||
const mingw = @import("mingw.zig");
|
||||
@ -1232,7 +1232,7 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil
|
||||
|
||||
const link_libc = options.config.link_libc;
|
||||
|
||||
const libc_dirs = try detectLibCIncludeDirs(
|
||||
const libc_dirs = try std.zig.LibCDirs.detect(
|
||||
arena,
|
||||
options.zig_lib_directory.path.?,
|
||||
options.root_mod.resolved_target.result,
|
||||
@ -1250,7 +1250,7 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil
|
||||
// only relevant differences would be things like `#define` constants being
|
||||
// different in the MinGW headers vs the MSVC headers, but any such
|
||||
// differences would likely be a MinGW bug.
|
||||
const rc_dirs = b: {
|
||||
const rc_dirs: std.zig.LibCDirs = b: {
|
||||
// Set the includes to .none here when there are no rc files to compile
|
||||
var includes = if (options.rc_source_files.len > 0) options.rc_includes else .none;
|
||||
const target = options.root_mod.resolved_target.result;
|
||||
@ -1265,7 +1265,7 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil
|
||||
}
|
||||
}
|
||||
while (true) switch (includes) {
|
||||
.any, .msvc => break :b detectLibCIncludeDirs(
|
||||
.any, .msvc => break :b std.zig.LibCDirs.detect(
|
||||
arena,
|
||||
options.zig_lib_directory.path.?,
|
||||
.{
|
||||
@ -1287,13 +1287,13 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil
|
||||
}
|
||||
return err;
|
||||
},
|
||||
.gnu => break :b try detectLibCFromBuilding(arena, options.zig_lib_directory.path.?, .{
|
||||
.gnu => break :b try std.zig.LibCDirs.detectFromBuilding(arena, options.zig_lib_directory.path.?, .{
|
||||
.cpu = target.cpu,
|
||||
.os = target.os,
|
||||
.abi = .gnu,
|
||||
.ofmt = target.ofmt,
|
||||
}),
|
||||
.none => break :b LibCDirs{
|
||||
.none => break :b .{
|
||||
.libc_include_dir_list = &[0][]u8{},
|
||||
.libc_installation = null,
|
||||
.libc_framework_dir_list = &.{},
|
||||
@ -1772,7 +1772,7 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil
|
||||
// If we need to build glibc for the target, add work items for it.
|
||||
// We go through the work queue so that building can be done in parallel.
|
||||
if (comp.wantBuildGLibCFromSource()) {
|
||||
if (!target_util.canBuildLibC(target)) return error.LibCUnavailable;
|
||||
if (!std.zig.target.canBuildLibC(target)) return error.LibCUnavailable;
|
||||
|
||||
if (glibc.needsCrtiCrtn(target)) {
|
||||
try comp.work_queue.write(&[_]Job{
|
||||
@ -1787,7 +1787,7 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil
|
||||
});
|
||||
}
|
||||
if (comp.wantBuildMuslFromSource()) {
|
||||
if (!target_util.canBuildLibC(target)) return error.LibCUnavailable;
|
||||
if (!std.zig.target.canBuildLibC(target)) return error.LibCUnavailable;
|
||||
|
||||
try comp.work_queue.ensureUnusedCapacity(6);
|
||||
if (musl.needsCrtiCrtn(target)) {
|
||||
@ -1808,7 +1808,7 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil
|
||||
}
|
||||
|
||||
if (comp.wantBuildWasiLibcFromSource()) {
|
||||
if (!target_util.canBuildLibC(target)) return error.LibCUnavailable;
|
||||
if (!std.zig.target.canBuildLibC(target)) return error.LibCUnavailable;
|
||||
|
||||
// worst-case we need all components
|
||||
try comp.work_queue.ensureUnusedCapacity(comp.wasi_emulated_libs.len + 2);
|
||||
@ -1825,7 +1825,7 @@ pub fn create(gpa: Allocator, arena: Allocator, options: CreateOptions) !*Compil
|
||||
}
|
||||
|
||||
if (comp.wantBuildMinGWFromSource()) {
|
||||
if (!target_util.canBuildLibC(target)) return error.LibCUnavailable;
|
||||
if (!std.zig.target.canBuildLibC(target)) return error.LibCUnavailable;
|
||||
|
||||
const crt_job: Job = .{ .mingw_crt_file = if (is_dyn_lib) .dllcrt2_o else .crt2_o };
|
||||
try comp.work_queue.ensureUnusedCapacity(2);
|
||||
@ -5830,224 +5830,6 @@ test "classifyFileExt" {
|
||||
try std.testing.expectEqual(FileExt.zig, classifyFileExt("foo.zig"));
|
||||
}
|
||||
|
||||
const LibCDirs = struct {
|
||||
libc_include_dir_list: []const []const u8,
|
||||
libc_installation: ?*const LibCInstallation,
|
||||
libc_framework_dir_list: []const []const u8,
|
||||
sysroot: ?[]const u8,
|
||||
darwin_sdk_layout: ?link.File.MachO.SdkLayout,
|
||||
};
|
||||
|
||||
fn getZigShippedLibCIncludeDirsDarwin(arena: Allocator, zig_lib_dir: []const u8) !LibCDirs {
|
||||
const s = std.fs.path.sep_str;
|
||||
const list = try arena.alloc([]const u8, 1);
|
||||
list[0] = try std.fmt.allocPrint(
|
||||
arena,
|
||||
"{s}" ++ s ++ "libc" ++ s ++ "include" ++ s ++ "any-macos-any",
|
||||
.{zig_lib_dir},
|
||||
);
|
||||
return LibCDirs{
|
||||
.libc_include_dir_list = list,
|
||||
.libc_installation = null,
|
||||
.libc_framework_dir_list = &.{},
|
||||
.sysroot = null,
|
||||
.darwin_sdk_layout = .vendored,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn detectLibCIncludeDirs(
|
||||
arena: Allocator,
|
||||
zig_lib_dir: []const u8,
|
||||
target: Target,
|
||||
is_native_abi: bool,
|
||||
link_libc: bool,
|
||||
libc_installation: ?*const LibCInstallation,
|
||||
) !LibCDirs {
|
||||
if (!link_libc) {
|
||||
return LibCDirs{
|
||||
.libc_include_dir_list = &[0][]u8{},
|
||||
.libc_installation = null,
|
||||
.libc_framework_dir_list = &.{},
|
||||
.sysroot = null,
|
||||
.darwin_sdk_layout = null,
|
||||
};
|
||||
}
|
||||
|
||||
if (libc_installation) |lci| {
|
||||
return detectLibCFromLibCInstallation(arena, target, lci);
|
||||
}
|
||||
|
||||
// If linking system libraries and targeting the native abi, default to
|
||||
// using the system libc installation.
|
||||
if (is_native_abi and !target.isMinGW()) {
|
||||
const libc = try arena.create(LibCInstallation);
|
||||
libc.* = LibCInstallation.findNative(.{ .allocator = arena, .target = target }) catch |err| switch (err) {
|
||||
error.CCompilerExitCode,
|
||||
error.CCompilerCrashed,
|
||||
error.CCompilerCannotFindHeaders,
|
||||
error.UnableToSpawnCCompiler,
|
||||
error.DarwinSdkNotFound,
|
||||
=> |e| {
|
||||
// We tried to integrate with the native system C compiler,
|
||||
// however, it is not installed. So we must rely on our bundled
|
||||
// libc files.
|
||||
if (target_util.canBuildLibC(target)) {
|
||||
return detectLibCFromBuilding(arena, zig_lib_dir, target);
|
||||
}
|
||||
return e;
|
||||
},
|
||||
else => |e| return e,
|
||||
};
|
||||
return detectLibCFromLibCInstallation(arena, target, libc);
|
||||
}
|
||||
|
||||
// If not linking system libraries, build and provide our own libc by
|
||||
// default if possible.
|
||||
if (target_util.canBuildLibC(target)) {
|
||||
return detectLibCFromBuilding(arena, zig_lib_dir, target);
|
||||
}
|
||||
|
||||
// If zig can't build the libc for the target and we are targeting the
|
||||
// native abi, fall back to using the system libc installation.
|
||||
// On windows, instead of the native (mingw) abi, we want to check
|
||||
// for the MSVC abi as a fallback.
|
||||
const use_system_abi = if (builtin.target.os.tag == .windows)
|
||||
target.abi == .msvc
|
||||
else
|
||||
is_native_abi;
|
||||
|
||||
if (use_system_abi) {
|
||||
const libc = try arena.create(LibCInstallation);
|
||||
libc.* = try LibCInstallation.findNative(.{ .allocator = arena, .verbose = true, .target = target });
|
||||
return detectLibCFromLibCInstallation(arena, target, libc);
|
||||
}
|
||||
|
||||
return LibCDirs{
|
||||
.libc_include_dir_list = &[0][]u8{},
|
||||
.libc_installation = null,
|
||||
.libc_framework_dir_list = &.{},
|
||||
.sysroot = null,
|
||||
.darwin_sdk_layout = null,
|
||||
};
|
||||
}
|
||||
|
||||
fn detectLibCFromLibCInstallation(arena: Allocator, target: Target, lci: *const LibCInstallation) !LibCDirs {
|
||||
var list = try std.ArrayList([]const u8).initCapacity(arena, 5);
|
||||
var framework_list = std.ArrayList([]const u8).init(arena);
|
||||
|
||||
list.appendAssumeCapacity(lci.include_dir.?);
|
||||
|
||||
const is_redundant = mem.eql(u8, lci.sys_include_dir.?, lci.include_dir.?);
|
||||
if (!is_redundant) list.appendAssumeCapacity(lci.sys_include_dir.?);
|
||||
|
||||
if (target.os.tag == .windows) {
|
||||
if (std.fs.path.dirname(lci.sys_include_dir.?)) |sys_include_dir_parent| {
|
||||
// This include path will only exist when the optional "Desktop development with C++"
|
||||
// is installed. It contains headers, .rc files, and resources. It is especially
|
||||
// necessary when working with Windows resources.
|
||||
const atlmfc_dir = try std.fs.path.join(arena, &[_][]const u8{ sys_include_dir_parent, "atlmfc", "include" });
|
||||
list.appendAssumeCapacity(atlmfc_dir);
|
||||
}
|
||||
if (std.fs.path.dirname(lci.include_dir.?)) |include_dir_parent| {
|
||||
const um_dir = try std.fs.path.join(arena, &[_][]const u8{ include_dir_parent, "um" });
|
||||
list.appendAssumeCapacity(um_dir);
|
||||
|
||||
const shared_dir = try std.fs.path.join(arena, &[_][]const u8{ include_dir_parent, "shared" });
|
||||
list.appendAssumeCapacity(shared_dir);
|
||||
}
|
||||
}
|
||||
if (target.os.tag == .haiku) {
|
||||
const include_dir_path = lci.include_dir orelse return error.LibCInstallationNotAvailable;
|
||||
const os_dir = try std.fs.path.join(arena, &[_][]const u8{ include_dir_path, "os" });
|
||||
list.appendAssumeCapacity(os_dir);
|
||||
// Errors.h
|
||||
const os_support_dir = try std.fs.path.join(arena, &[_][]const u8{ include_dir_path, "os/support" });
|
||||
list.appendAssumeCapacity(os_support_dir);
|
||||
|
||||
const config_dir = try std.fs.path.join(arena, &[_][]const u8{ include_dir_path, "config" });
|
||||
list.appendAssumeCapacity(config_dir);
|
||||
}
|
||||
|
||||
var sysroot: ?[]const u8 = null;
|
||||
|
||||
if (target.isDarwin()) d: {
|
||||
const down1 = std.fs.path.dirname(lci.sys_include_dir.?) orelse break :d;
|
||||
const down2 = std.fs.path.dirname(down1) orelse break :d;
|
||||
try framework_list.append(try std.fs.path.join(arena, &.{ down2, "System", "Library", "Frameworks" }));
|
||||
sysroot = down2;
|
||||
}
|
||||
|
||||
return LibCDirs{
|
||||
.libc_include_dir_list = list.items,
|
||||
.libc_installation = lci,
|
||||
.libc_framework_dir_list = framework_list.items,
|
||||
.sysroot = sysroot,
|
||||
.darwin_sdk_layout = if (sysroot == null) null else .sdk,
|
||||
};
|
||||
}
|
||||
|
||||
fn detectLibCFromBuilding(
|
||||
arena: Allocator,
|
||||
zig_lib_dir: []const u8,
|
||||
target: std.Target,
|
||||
) !LibCDirs {
|
||||
if (target.isDarwin())
|
||||
return getZigShippedLibCIncludeDirsDarwin(arena, zig_lib_dir);
|
||||
|
||||
const generic_name = target_util.libCGenericName(target);
|
||||
// Some architectures are handled by the same set of headers.
|
||||
const arch_name = if (target.abi.isMusl())
|
||||
musl.archNameHeaders(target.cpu.arch)
|
||||
else if (target.cpu.arch.isThumb())
|
||||
// ARM headers are valid for Thumb too.
|
||||
switch (target.cpu.arch) {
|
||||
.thumb => "arm",
|
||||
.thumbeb => "armeb",
|
||||
else => unreachable,
|
||||
}
|
||||
else
|
||||
@tagName(target.cpu.arch);
|
||||
const os_name = @tagName(target.os.tag);
|
||||
// Musl's headers are ABI-agnostic and so they all have the "musl" ABI name.
|
||||
const abi_name = if (target.abi.isMusl()) "musl" else @tagName(target.abi);
|
||||
const s = std.fs.path.sep_str;
|
||||
const arch_include_dir = try std.fmt.allocPrint(
|
||||
arena,
|
||||
"{s}" ++ s ++ "libc" ++ s ++ "include" ++ s ++ "{s}-{s}-{s}",
|
||||
.{ zig_lib_dir, arch_name, os_name, abi_name },
|
||||
);
|
||||
const generic_include_dir = try std.fmt.allocPrint(
|
||||
arena,
|
||||
"{s}" ++ s ++ "libc" ++ s ++ "include" ++ s ++ "generic-{s}",
|
||||
.{ zig_lib_dir, generic_name },
|
||||
);
|
||||
const generic_arch_name = target_util.osArchName(target);
|
||||
const arch_os_include_dir = try std.fmt.allocPrint(
|
||||
arena,
|
||||
"{s}" ++ s ++ "libc" ++ s ++ "include" ++ s ++ "{s}-{s}-any",
|
||||
.{ zig_lib_dir, generic_arch_name, os_name },
|
||||
);
|
||||
const generic_os_include_dir = try std.fmt.allocPrint(
|
||||
arena,
|
||||
"{s}" ++ s ++ "libc" ++ s ++ "include" ++ s ++ "any-{s}-any",
|
||||
.{ zig_lib_dir, os_name },
|
||||
);
|
||||
|
||||
const list = try arena.alloc([]const u8, 4);
|
||||
list[0] = arch_include_dir;
|
||||
list[1] = generic_include_dir;
|
||||
list[2] = arch_os_include_dir;
|
||||
list[3] = generic_os_include_dir;
|
||||
|
||||
return LibCDirs{
|
||||
.libc_include_dir_list = list,
|
||||
.libc_installation = null,
|
||||
.libc_framework_dir_list = &.{},
|
||||
.sysroot = null,
|
||||
.darwin_sdk_layout = .vendored,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn get_libc_crt_file(comp: *Compilation, arena: Allocator, basename: []const u8) ![]const u8 {
|
||||
if (comp.wantBuildGLibCFromSource() or
|
||||
comp.wantBuildMuslFromSource() or
|
||||
|
||||
@ -7,7 +7,6 @@ const path = fs.path;
|
||||
const assert = std.debug.assert;
|
||||
const Version = std.SemanticVersion;
|
||||
|
||||
const target_util = @import("target.zig");
|
||||
const Compilation = @import("Compilation.zig");
|
||||
const build_options = @import("build_options");
|
||||
const trace = @import("tracy.zig").trace;
|
||||
@ -21,7 +20,7 @@ pub const Lib = struct {
|
||||
|
||||
pub const ABI = struct {
|
||||
all_versions: []const Version, // all defined versions (one abilist from v2.0.0 up to current)
|
||||
all_targets: []const target_util.ArchOsAbi,
|
||||
all_targets: []const std.zig.target.ArchOsAbi,
|
||||
/// The bytes from the file verbatim, starting from the u16 number
|
||||
/// of function inclusions.
|
||||
inclusions: []const u8,
|
||||
@ -103,7 +102,7 @@ pub fn loadMetaData(gpa: Allocator, contents: []const u8) LoadMetaDataError!*ABI
|
||||
const targets_len = contents[index];
|
||||
index += 1;
|
||||
|
||||
const targets = try arena.alloc(target_util.ArchOsAbi, targets_len);
|
||||
const targets = try arena.alloc(std.zig.target.ArchOsAbi, targets_len);
|
||||
var i: u8 = 0;
|
||||
while (i < targets.len) : (i += 1) {
|
||||
const target_name = mem.sliceTo(contents[index..], 0);
|
||||
@ -512,7 +511,7 @@ fn add_include_dirs(comp: *Compilation, arena: Allocator, args: *std.ArrayList([
|
||||
try args.append("-I");
|
||||
try args.append(try lib_path(comp, arena, lib_libc ++ "include" ++ s ++ "generic-glibc"));
|
||||
|
||||
const arch_name = target_util.osArchName(target);
|
||||
const arch_name = target.osArchName();
|
||||
try args.append("-I");
|
||||
try args.append(try std.fmt.allocPrint(arena, "{s}" ++ s ++ "libc" ++ s ++ "include" ++ s ++ "{s}-linux-any", .{
|
||||
comp.zig_lib_directory.path.?, arch_name,
|
||||
@ -726,7 +725,7 @@ pub fn buildSharedObjects(comp: *Compilation, prog_node: *std.Progress.Node) !vo
|
||||
break i;
|
||||
}
|
||||
} else {
|
||||
unreachable; // target_util.available_libcs prevents us from getting here
|
||||
unreachable; // std.zig.target.available_libcs prevents us from getting here
|
||||
};
|
||||
|
||||
const target_ver_index = for (metadata.all_versions, 0..) |ver, i| {
|
||||
|
||||
@ -84,14 +84,14 @@ pub fn resolveGlobalCacheDir(allocator: mem.Allocator) ![]u8 {
|
||||
if (builtin.os.tag == .wasi)
|
||||
@compileError("on WASI the global cache dir must be resolved with preopens");
|
||||
|
||||
if (try EnvVar.ZIG_GLOBAL_CACHE_DIR.get(allocator)) |value| return value;
|
||||
if (try std.zig.EnvVar.ZIG_GLOBAL_CACHE_DIR.get(allocator)) |value| return value;
|
||||
|
||||
const appname = "zig";
|
||||
|
||||
if (builtin.os.tag != .windows) {
|
||||
if (EnvVar.XDG_CACHE_HOME.getPosix()) |cache_root| {
|
||||
if (std.zig.EnvVar.XDG_CACHE_HOME.getPosix()) |cache_root| {
|
||||
return fs.path.join(allocator, &[_][]const u8{ cache_root, appname });
|
||||
} else if (EnvVar.HOME.getPosix()) |home| {
|
||||
} else if (std.zig.EnvVar.HOME.getPosix()) |home| {
|
||||
return fs.path.join(allocator, &[_][]const u8{ home, ".cache", appname });
|
||||
}
|
||||
}
|
||||
@ -141,41 +141,3 @@ pub fn resolvePath(
|
||||
pub fn isUpDir(p: []const u8) bool {
|
||||
return mem.startsWith(u8, p, "..") and (p.len == 2 or p[2] == fs.path.sep);
|
||||
}
|
||||
|
||||
/// Collects all the environment variables that Zig could possibly inspect, so
|
||||
/// that we can do reflection on this and print them with `zig env`.
|
||||
pub const EnvVar = enum {
|
||||
ZIG_GLOBAL_CACHE_DIR,
|
||||
ZIG_LOCAL_CACHE_DIR,
|
||||
ZIG_LIB_DIR,
|
||||
ZIG_LIBC,
|
||||
ZIG_BUILD_RUNNER,
|
||||
ZIG_VERBOSE_LINK,
|
||||
ZIG_VERBOSE_CC,
|
||||
ZIG_BTRFS_WORKAROUND,
|
||||
ZIG_DEBUG_CMD,
|
||||
CC,
|
||||
NO_COLOR,
|
||||
XDG_CACHE_HOME,
|
||||
HOME,
|
||||
|
||||
pub fn isSet(comptime ev: EnvVar) bool {
|
||||
return std.process.hasEnvVarConstant(@tagName(ev));
|
||||
}
|
||||
|
||||
pub fn get(ev: EnvVar, arena: mem.Allocator) !?[]u8 {
|
||||
// Env vars aren't used in the bootstrap stage.
|
||||
if (build_options.only_c) return null;
|
||||
|
||||
if (std.process.getEnvVarOwned(arena, @tagName(ev))) |value| {
|
||||
return value;
|
||||
} else |err| switch (err) {
|
||||
error.EnvironmentVariableNotFound => return null,
|
||||
else => |e| return e,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn getPosix(comptime ev: EnvVar) ?[:0]const u8 {
|
||||
return std.os.getenvZ(@tagName(ev));
|
||||
}
|
||||
};
|
||||
|
||||
@ -1,712 +0,0 @@
|
||||
const std = @import("std");
|
||||
const builtin = @import("builtin");
|
||||
const Target = std.Target;
|
||||
const fs = std.fs;
|
||||
const Allocator = std.mem.Allocator;
|
||||
|
||||
const is_darwin = builtin.target.isDarwin();
|
||||
const is_windows = builtin.target.os.tag == .windows;
|
||||
const is_haiku = builtin.target.os.tag == .haiku;
|
||||
|
||||
const log = std.log.scoped(.libc_installation);
|
||||
|
||||
const ZigWindowsSDK = @import("windows_sdk.zig").ZigWindowsSDK;
|
||||
const EnvVar = @import("introspect.zig").EnvVar;
|
||||
|
||||
/// See the render function implementation for documentation of the fields.
|
||||
pub const LibCInstallation = struct {
|
||||
include_dir: ?[]const u8 = null,
|
||||
sys_include_dir: ?[]const u8 = null,
|
||||
crt_dir: ?[]const u8 = null,
|
||||
msvc_lib_dir: ?[]const u8 = null,
|
||||
kernel32_lib_dir: ?[]const u8 = null,
|
||||
gcc_dir: ?[]const u8 = null,
|
||||
|
||||
pub const FindError = error{
|
||||
OutOfMemory,
|
||||
FileSystem,
|
||||
UnableToSpawnCCompiler,
|
||||
CCompilerExitCode,
|
||||
CCompilerCrashed,
|
||||
CCompilerCannotFindHeaders,
|
||||
LibCRuntimeNotFound,
|
||||
LibCStdLibHeaderNotFound,
|
||||
LibCKernel32LibNotFound,
|
||||
UnsupportedArchitecture,
|
||||
WindowsSdkNotFound,
|
||||
DarwinSdkNotFound,
|
||||
ZigIsTheCCompiler,
|
||||
};
|
||||
|
||||
pub fn parse(
|
||||
allocator: Allocator,
|
||||
libc_file: []const u8,
|
||||
target: std.Target,
|
||||
) !LibCInstallation {
|
||||
var self: LibCInstallation = .{};
|
||||
|
||||
const fields = std.meta.fields(LibCInstallation);
|
||||
const FoundKey = struct {
|
||||
found: bool,
|
||||
allocated: ?[:0]u8,
|
||||
};
|
||||
var found_keys = [1]FoundKey{FoundKey{ .found = false, .allocated = null }} ** fields.len;
|
||||
errdefer {
|
||||
self = .{};
|
||||
for (found_keys) |found_key| {
|
||||
if (found_key.allocated) |s| allocator.free(s);
|
||||
}
|
||||
}
|
||||
|
||||
const contents = try std.fs.cwd().readFileAlloc(allocator, libc_file, std.math.maxInt(usize));
|
||||
defer allocator.free(contents);
|
||||
|
||||
var it = std.mem.tokenizeScalar(u8, contents, '\n');
|
||||
while (it.next()) |line| {
|
||||
if (line.len == 0 or line[0] == '#') continue;
|
||||
var line_it = std.mem.splitScalar(u8, line, '=');
|
||||
const name = line_it.first();
|
||||
const value = line_it.rest();
|
||||
inline for (fields, 0..) |field, i| {
|
||||
if (std.mem.eql(u8, name, field.name)) {
|
||||
found_keys[i].found = true;
|
||||
if (value.len == 0) {
|
||||
@field(self, field.name) = null;
|
||||
} else {
|
||||
found_keys[i].allocated = try allocator.dupeZ(u8, value);
|
||||
@field(self, field.name) = found_keys[i].allocated;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
inline for (fields, 0..) |field, i| {
|
||||
if (!found_keys[i].found) {
|
||||
log.err("missing field: {s}\n", .{field.name});
|
||||
return error.ParseError;
|
||||
}
|
||||
}
|
||||
if (self.include_dir == null) {
|
||||
log.err("include_dir may not be empty\n", .{});
|
||||
return error.ParseError;
|
||||
}
|
||||
if (self.sys_include_dir == null) {
|
||||
log.err("sys_include_dir may not be empty\n", .{});
|
||||
return error.ParseError;
|
||||
}
|
||||
|
||||
const os_tag = target.os.tag;
|
||||
if (self.crt_dir == null and !target.isDarwin()) {
|
||||
log.err("crt_dir may not be empty for {s}\n", .{@tagName(os_tag)});
|
||||
return error.ParseError;
|
||||
}
|
||||
|
||||
if (self.msvc_lib_dir == null and os_tag == .windows and target.abi == .msvc) {
|
||||
log.err("msvc_lib_dir may not be empty for {s}-{s}\n", .{
|
||||
@tagName(os_tag),
|
||||
@tagName(target.abi),
|
||||
});
|
||||
return error.ParseError;
|
||||
}
|
||||
if (self.kernel32_lib_dir == null and os_tag == .windows and target.abi == .msvc) {
|
||||
log.err("kernel32_lib_dir may not be empty for {s}-{s}\n", .{
|
||||
@tagName(os_tag),
|
||||
@tagName(target.abi),
|
||||
});
|
||||
return error.ParseError;
|
||||
}
|
||||
|
||||
if (self.gcc_dir == null and os_tag == .haiku) {
|
||||
log.err("gcc_dir may not be empty for {s}\n", .{@tagName(os_tag)});
|
||||
return error.ParseError;
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
pub fn render(self: LibCInstallation, out: anytype) !void {
|
||||
@setEvalBranchQuota(4000);
|
||||
const include_dir = self.include_dir orelse "";
|
||||
const sys_include_dir = self.sys_include_dir orelse "";
|
||||
const crt_dir = self.crt_dir orelse "";
|
||||
const msvc_lib_dir = self.msvc_lib_dir orelse "";
|
||||
const kernel32_lib_dir = self.kernel32_lib_dir orelse "";
|
||||
const gcc_dir = self.gcc_dir orelse "";
|
||||
|
||||
try out.print(
|
||||
\\# The directory that contains `stdlib.h`.
|
||||
\\# On POSIX-like systems, include directories be found with: `cc -E -Wp,-v -xc /dev/null`
|
||||
\\include_dir={s}
|
||||
\\
|
||||
\\# 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={s}
|
||||
\\
|
||||
\\# 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.
|
||||
\\crt_dir={s}
|
||||
\\
|
||||
\\# The directory that contains `vcruntime.lib`.
|
||||
\\# Only needed when targeting MSVC on Windows.
|
||||
\\msvc_lib_dir={s}
|
||||
\\
|
||||
\\# The directory that contains `kernel32.lib`.
|
||||
\\# Only needed when targeting MSVC on Windows.
|
||||
\\kernel32_lib_dir={s}
|
||||
\\
|
||||
\\# The directory that contains `crtbeginS.o` and `crtendS.o`
|
||||
\\# Only needed when targeting Haiku.
|
||||
\\gcc_dir={s}
|
||||
\\
|
||||
, .{
|
||||
include_dir,
|
||||
sys_include_dir,
|
||||
crt_dir,
|
||||
msvc_lib_dir,
|
||||
kernel32_lib_dir,
|
||||
gcc_dir,
|
||||
});
|
||||
}
|
||||
|
||||
pub const FindNativeOptions = struct {
|
||||
allocator: Allocator,
|
||||
target: std.Target,
|
||||
|
||||
/// If enabled, will print human-friendly errors to stderr.
|
||||
verbose: bool = false,
|
||||
};
|
||||
|
||||
/// Finds the default, native libc.
|
||||
pub fn findNative(args: FindNativeOptions) FindError!LibCInstallation {
|
||||
var self: LibCInstallation = .{};
|
||||
|
||||
if (is_darwin) {
|
||||
if (!std.zig.system.darwin.isSdkInstalled(args.allocator))
|
||||
return error.DarwinSdkNotFound;
|
||||
const sdk = std.zig.system.darwin.getSdk(args.allocator, args.target) orelse
|
||||
return error.DarwinSdkNotFound;
|
||||
defer args.allocator.free(sdk);
|
||||
|
||||
self.include_dir = try fs.path.join(args.allocator, &.{
|
||||
sdk, "usr/include",
|
||||
});
|
||||
self.sys_include_dir = try fs.path.join(args.allocator, &.{
|
||||
sdk, "usr/include",
|
||||
});
|
||||
return self;
|
||||
} else if (is_windows) {
|
||||
var sdk: ZigWindowsSDK = ZigWindowsSDK.find(args.allocator) catch |err| switch (err) {
|
||||
error.NotFound => return error.WindowsSdkNotFound,
|
||||
error.PathTooLong => return error.WindowsSdkNotFound,
|
||||
error.OutOfMemory => return error.OutOfMemory,
|
||||
};
|
||||
defer sdk.free(args.allocator);
|
||||
|
||||
try self.findNativeMsvcIncludeDir(args, &sdk);
|
||||
try self.findNativeMsvcLibDir(args, &sdk);
|
||||
try self.findNativeKernel32LibDir(args, &sdk);
|
||||
try self.findNativeIncludeDirWindows(args, &sdk);
|
||||
try self.findNativeCrtDirWindows(args, &sdk);
|
||||
} else if (is_haiku) {
|
||||
try self.findNativeIncludeDirPosix(args);
|
||||
try self.findNativeCrtBeginDirHaiku(args);
|
||||
self.crt_dir = try args.allocator.dupeZ(u8, "/system/develop/lib");
|
||||
} else if (builtin.target.os.tag.isSolarish()) {
|
||||
// There is only one libc, and its headers/libraries are always in the same spot.
|
||||
self.include_dir = try args.allocator.dupeZ(u8, "/usr/include");
|
||||
self.sys_include_dir = try args.allocator.dupeZ(u8, "/usr/include");
|
||||
self.crt_dir = try args.allocator.dupeZ(u8, "/usr/lib/64");
|
||||
} else if (std.process.can_spawn) {
|
||||
try self.findNativeIncludeDirPosix(args);
|
||||
switch (builtin.target.os.tag) {
|
||||
.freebsd, .netbsd, .openbsd, .dragonfly => self.crt_dir = try args.allocator.dupeZ(u8, "/usr/lib"),
|
||||
.linux => try self.findNativeCrtDirPosix(args),
|
||||
else => {},
|
||||
}
|
||||
} else {
|
||||
return error.LibCRuntimeNotFound;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
/// Must be the same allocator passed to `parse` or `findNative`.
|
||||
pub fn deinit(self: *LibCInstallation, allocator: Allocator) void {
|
||||
const fields = std.meta.fields(LibCInstallation);
|
||||
inline for (fields) |field| {
|
||||
if (@field(self, field.name)) |payload| {
|
||||
allocator.free(payload);
|
||||
}
|
||||
}
|
||||
self.* = undefined;
|
||||
}
|
||||
|
||||
fn findNativeIncludeDirPosix(self: *LibCInstallation, args: FindNativeOptions) FindError!void {
|
||||
const allocator = args.allocator;
|
||||
|
||||
// Detect infinite loops.
|
||||
var env_map = std.process.getEnvMap(allocator) catch |err| switch (err) {
|
||||
error.Unexpected => unreachable, // WASI-only
|
||||
else => |e| return e,
|
||||
};
|
||||
defer env_map.deinit();
|
||||
const skip_cc_env_var = if (env_map.get(inf_loop_env_key)) |phase| blk: {
|
||||
if (std.mem.eql(u8, phase, "1")) {
|
||||
try env_map.put(inf_loop_env_key, "2");
|
||||
break :blk true;
|
||||
} else {
|
||||
return error.ZigIsTheCCompiler;
|
||||
}
|
||||
} else blk: {
|
||||
try env_map.put(inf_loop_env_key, "1");
|
||||
break :blk false;
|
||||
};
|
||||
|
||||
const dev_null = if (is_windows) "nul" else "/dev/null";
|
||||
|
||||
var argv = std.ArrayList([]const u8).init(allocator);
|
||||
defer argv.deinit();
|
||||
|
||||
try appendCcExe(&argv, skip_cc_env_var);
|
||||
try argv.appendSlice(&.{
|
||||
"-E",
|
||||
"-Wp,-v",
|
||||
"-xc",
|
||||
dev_null,
|
||||
});
|
||||
|
||||
const run_res = std.ChildProcess.run(.{
|
||||
.allocator = allocator,
|
||||
.argv = argv.items,
|
||||
.max_output_bytes = 1024 * 1024,
|
||||
.env_map = &env_map,
|
||||
// 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 => {
|
||||
printVerboseInvocation(argv.items, null, args.verbose, null);
|
||||
return error.UnableToSpawnCCompiler;
|
||||
},
|
||||
};
|
||||
defer {
|
||||
allocator.free(run_res.stdout);
|
||||
allocator.free(run_res.stderr);
|
||||
}
|
||||
switch (run_res.term) {
|
||||
.Exited => |code| if (code != 0) {
|
||||
printVerboseInvocation(argv.items, null, args.verbose, run_res.stderr);
|
||||
return error.CCompilerExitCode;
|
||||
},
|
||||
else => {
|
||||
printVerboseInvocation(argv.items, null, args.verbose, run_res.stderr);
|
||||
return error.CCompilerCrashed;
|
||||
},
|
||||
}
|
||||
|
||||
var it = std.mem.tokenizeAny(u8, run_res.stderr, "\n\r");
|
||||
var search_paths = std.ArrayList([]const u8).init(allocator);
|
||||
defer search_paths.deinit();
|
||||
while (it.next()) |line| {
|
||||
if (line.len != 0 and line[0] == ' ') {
|
||||
try search_paths.append(line);
|
||||
}
|
||||
}
|
||||
if (search_paths.items.len == 0) {
|
||||
return error.CCompilerCannotFindHeaders;
|
||||
}
|
||||
|
||||
const include_dir_example_file = if (is_haiku) "posix/stdlib.h" else "stdlib.h";
|
||||
const sys_include_dir_example_file = if (is_windows)
|
||||
"sys\\types.h"
|
||||
else if (is_haiku)
|
||||
"errno.h"
|
||||
else
|
||||
"sys/errno.h";
|
||||
|
||||
var path_i: usize = 0;
|
||||
while (path_i < search_paths.items.len) : (path_i += 1) {
|
||||
// search in reverse order
|
||||
const search_path_untrimmed = search_paths.items[search_paths.items.len - path_i - 1];
|
||||
const search_path = std.mem.trimLeft(u8, search_path_untrimmed, " ");
|
||||
var search_dir = fs.cwd().openDir(search_path, .{}) catch |err| switch (err) {
|
||||
error.FileNotFound,
|
||||
error.NotDir,
|
||||
error.NoDevice,
|
||||
=> continue,
|
||||
|
||||
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 allocator.dupeZ(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 allocator.dupeZ(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;
|
||||
}
|
||||
}
|
||||
|
||||
return error.LibCStdLibHeaderNotFound;
|
||||
}
|
||||
|
||||
fn findNativeIncludeDirWindows(
|
||||
self: *LibCInstallation,
|
||||
args: FindNativeOptions,
|
||||
sdk: *ZigWindowsSDK,
|
||||
) FindError!void {
|
||||
const allocator = args.allocator;
|
||||
|
||||
var search_buf: [2]Search = undefined;
|
||||
const searches = fillSearch(&search_buf, sdk);
|
||||
|
||||
var result_buf = std.ArrayList(u8).init(allocator);
|
||||
defer result_buf.deinit();
|
||||
|
||||
for (searches) |search| {
|
||||
result_buf.shrinkAndFree(0);
|
||||
try result_buf.writer().print("{s}\\Include\\{s}\\ucrt", .{ search.path, search.version });
|
||||
|
||||
var dir = fs.cwd().openDir(result_buf.items, .{}) catch |err| switch (err) {
|
||||
error.FileNotFound,
|
||||
error.NotDir,
|
||||
error.NoDevice,
|
||||
=> continue,
|
||||
|
||||
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 = try result_buf.toOwnedSlice();
|
||||
return;
|
||||
}
|
||||
|
||||
return error.LibCStdLibHeaderNotFound;
|
||||
}
|
||||
|
||||
fn findNativeCrtDirWindows(
|
||||
self: *LibCInstallation,
|
||||
args: FindNativeOptions,
|
||||
sdk: *ZigWindowsSDK,
|
||||
) FindError!void {
|
||||
const allocator = args.allocator;
|
||||
|
||||
var search_buf: [2]Search = undefined;
|
||||
const searches = fillSearch(&search_buf, sdk);
|
||||
|
||||
var result_buf = std.ArrayList(u8).init(allocator);
|
||||
defer result_buf.deinit();
|
||||
|
||||
const arch_sub_dir = switch (builtin.target.cpu.arch) {
|
||||
.x86 => "x86",
|
||||
.x86_64 => "x64",
|
||||
.arm, .armeb => "arm",
|
||||
.aarch64 => "arm64",
|
||||
else => return error.UnsupportedArchitecture,
|
||||
};
|
||||
|
||||
for (searches) |search| {
|
||||
result_buf.shrinkAndFree(0);
|
||||
try result_buf.writer().print("{s}\\Lib\\{s}\\ucrt\\{s}", .{ search.path, search.version, arch_sub_dir });
|
||||
|
||||
var dir = fs.cwd().openDir(result_buf.items, .{}) 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 = try result_buf.toOwnedSlice();
|
||||
return;
|
||||
}
|
||||
return error.LibCRuntimeNotFound;
|
||||
}
|
||||
|
||||
fn findNativeCrtDirPosix(self: *LibCInstallation, args: FindNativeOptions) FindError!void {
|
||||
self.crt_dir = try ccPrintFileName(.{
|
||||
.allocator = args.allocator,
|
||||
.search_basename = "crt1.o",
|
||||
.want_dirname = .only_dir,
|
||||
.verbose = args.verbose,
|
||||
});
|
||||
}
|
||||
|
||||
fn findNativeCrtBeginDirHaiku(self: *LibCInstallation, args: FindNativeOptions) FindError!void {
|
||||
self.gcc_dir = try ccPrintFileName(.{
|
||||
.allocator = args.allocator,
|
||||
.search_basename = "crtbeginS.o",
|
||||
.want_dirname = .only_dir,
|
||||
.verbose = args.verbose,
|
||||
});
|
||||
}
|
||||
|
||||
fn findNativeKernel32LibDir(
|
||||
self: *LibCInstallation,
|
||||
args: FindNativeOptions,
|
||||
sdk: *ZigWindowsSDK,
|
||||
) FindError!void {
|
||||
const allocator = args.allocator;
|
||||
|
||||
var search_buf: [2]Search = undefined;
|
||||
const searches = fillSearch(&search_buf, sdk);
|
||||
|
||||
var result_buf = std.ArrayList(u8).init(allocator);
|
||||
defer result_buf.deinit();
|
||||
|
||||
const arch_sub_dir = switch (builtin.target.cpu.arch) {
|
||||
.x86 => "x86",
|
||||
.x86_64 => "x64",
|
||||
.arm, .armeb => "arm",
|
||||
.aarch64 => "arm64",
|
||||
else => return error.UnsupportedArchitecture,
|
||||
};
|
||||
|
||||
for (searches) |search| {
|
||||
result_buf.shrinkAndFree(0);
|
||||
const stream = result_buf.writer();
|
||||
try stream.print("{s}\\Lib\\{s}\\um\\{s}", .{ search.path, search.version, arch_sub_dir });
|
||||
|
||||
var dir = fs.cwd().openDir(result_buf.items, .{}) 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 = try result_buf.toOwnedSlice();
|
||||
return;
|
||||
}
|
||||
return error.LibCKernel32LibNotFound;
|
||||
}
|
||||
|
||||
fn findNativeMsvcIncludeDir(
|
||||
self: *LibCInstallation,
|
||||
args: FindNativeOptions,
|
||||
sdk: *ZigWindowsSDK,
|
||||
) FindError!void {
|
||||
const allocator = args.allocator;
|
||||
|
||||
const msvc_lib_dir = sdk.msvc_lib_dir orelse return error.LibCStdLibHeaderNotFound;
|
||||
const up1 = fs.path.dirname(msvc_lib_dir) orelse return error.LibCStdLibHeaderNotFound;
|
||||
const up2 = fs.path.dirname(up1) orelse return error.LibCStdLibHeaderNotFound;
|
||||
|
||||
const dir_path = try fs.path.join(allocator, &[_][]const u8{ up2, "include" });
|
||||
errdefer allocator.free(dir_path);
|
||||
|
||||
var dir = fs.cwd().openDir(dir_path, .{}) 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 = dir_path;
|
||||
}
|
||||
|
||||
fn findNativeMsvcLibDir(
|
||||
self: *LibCInstallation,
|
||||
args: FindNativeOptions,
|
||||
sdk: *ZigWindowsSDK,
|
||||
) FindError!void {
|
||||
const allocator = args.allocator;
|
||||
const msvc_lib_dir = sdk.msvc_lib_dir orelse return error.LibCRuntimeNotFound;
|
||||
self.msvc_lib_dir = try allocator.dupe(u8, msvc_lib_dir);
|
||||
}
|
||||
};
|
||||
|
||||
pub const CCPrintFileNameOptions = struct {
|
||||
allocator: Allocator,
|
||||
search_basename: []const u8,
|
||||
want_dirname: enum { full_path, only_dir },
|
||||
verbose: bool = false,
|
||||
};
|
||||
|
||||
/// caller owns returned memory
|
||||
fn ccPrintFileName(args: CCPrintFileNameOptions) ![:0]u8 {
|
||||
const allocator = args.allocator;
|
||||
|
||||
// Detect infinite loops.
|
||||
var env_map = std.process.getEnvMap(allocator) catch |err| switch (err) {
|
||||
error.Unexpected => unreachable, // WASI-only
|
||||
else => |e| return e,
|
||||
};
|
||||
defer env_map.deinit();
|
||||
const skip_cc_env_var = if (env_map.get(inf_loop_env_key)) |phase| blk: {
|
||||
if (std.mem.eql(u8, phase, "1")) {
|
||||
try env_map.put(inf_loop_env_key, "2");
|
||||
break :blk true;
|
||||
} else {
|
||||
return error.ZigIsTheCCompiler;
|
||||
}
|
||||
} else blk: {
|
||||
try env_map.put(inf_loop_env_key, "1");
|
||||
break :blk false;
|
||||
};
|
||||
|
||||
var argv = std.ArrayList([]const u8).init(allocator);
|
||||
defer argv.deinit();
|
||||
|
||||
const arg1 = try std.fmt.allocPrint(allocator, "-print-file-name={s}", .{args.search_basename});
|
||||
defer allocator.free(arg1);
|
||||
|
||||
try appendCcExe(&argv, skip_cc_env_var);
|
||||
try argv.append(arg1);
|
||||
|
||||
const run_res = std.ChildProcess.run(.{
|
||||
.allocator = allocator,
|
||||
.argv = argv.items,
|
||||
.max_output_bytes = 1024 * 1024,
|
||||
.env_map = &env_map,
|
||||
// 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(run_res.stdout);
|
||||
allocator.free(run_res.stderr);
|
||||
}
|
||||
switch (run_res.term) {
|
||||
.Exited => |code| if (code != 0) {
|
||||
printVerboseInvocation(argv.items, args.search_basename, args.verbose, run_res.stderr);
|
||||
return error.CCompilerExitCode;
|
||||
},
|
||||
else => {
|
||||
printVerboseInvocation(argv.items, args.search_basename, args.verbose, run_res.stderr);
|
||||
return error.CCompilerCrashed;
|
||||
},
|
||||
}
|
||||
|
||||
var it = std.mem.tokenizeAny(u8, run_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, args.search_basename)) return error.LibCRuntimeNotFound;
|
||||
switch (args.want_dirname) {
|
||||
.full_path => return allocator.dupeZ(u8, line),
|
||||
.only_dir => {
|
||||
const dirname = fs.path.dirname(line) orelse return error.LibCRuntimeNotFound;
|
||||
return allocator.dupeZ(u8, dirname);
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn printVerboseInvocation(
|
||||
argv: []const []const u8,
|
||||
search_basename: ?[]const u8,
|
||||
verbose: bool,
|
||||
stderr: ?[]const u8,
|
||||
) void {
|
||||
if (!verbose) return;
|
||||
|
||||
if (search_basename) |s| {
|
||||
std.debug.print("Zig attempted to find the file '{s}' by executing this command:\n", .{s});
|
||||
} else {
|
||||
std.debug.print("Zig attempted to find the path to native system libc headers by executing this command:\n", .{});
|
||||
}
|
||||
for (argv, 0..) |arg, i| {
|
||||
if (i != 0) std.debug.print(" ", .{});
|
||||
std.debug.print("{s}", .{arg});
|
||||
}
|
||||
std.debug.print("\n", .{});
|
||||
if (stderr) |s| {
|
||||
std.debug.print("Output:\n==========\n{s}\n==========\n", .{s});
|
||||
}
|
||||
}
|
||||
|
||||
const Search = struct {
|
||||
path: []const u8,
|
||||
version: []const u8,
|
||||
};
|
||||
|
||||
fn fillSearch(search_buf: *[2]Search, sdk: *ZigWindowsSDK) []Search {
|
||||
var search_end: usize = 0;
|
||||
if (sdk.windows10sdk) |windows10sdk| {
|
||||
search_buf[search_end] = .{
|
||||
.path = windows10sdk.path,
|
||||
.version = windows10sdk.version,
|
||||
};
|
||||
search_end += 1;
|
||||
}
|
||||
if (sdk.windows81sdk) |windows81sdk| {
|
||||
search_buf[search_end] = .{
|
||||
.path = windows81sdk.path,
|
||||
.version = windows81sdk.version,
|
||||
};
|
||||
search_end += 1;
|
||||
}
|
||||
return search_buf[0..search_end];
|
||||
}
|
||||
|
||||
const inf_loop_env_key = "ZIG_IS_DETECTING_LIBC_PATHS";
|
||||
|
||||
fn appendCcExe(args: *std.ArrayList([]const u8), skip_cc_env_var: bool) !void {
|
||||
const default_cc_exe = if (is_windows) "cc.exe" else "cc";
|
||||
try args.ensureUnusedCapacity(1);
|
||||
if (skip_cc_env_var) {
|
||||
args.appendAssumeCapacity(default_cc_exe);
|
||||
return;
|
||||
}
|
||||
const cc_env_var = EnvVar.CC.getPosix() orelse {
|
||||
args.appendAssumeCapacity(default_cc_exe);
|
||||
return;
|
||||
};
|
||||
// Respect space-separated flags to the C compiler.
|
||||
var it = std.mem.tokenizeScalar(u8, cc_env_var, ' ');
|
||||
while (it.next()) |arg| {
|
||||
try args.append(arg);
|
||||
}
|
||||
}
|
||||
@ -12,7 +12,7 @@ const Air = @import("Air.zig");
|
||||
const Allocator = std.mem.Allocator;
|
||||
const Cache = std.Build.Cache;
|
||||
const Compilation = @import("Compilation.zig");
|
||||
const LibCInstallation = @import("libc_installation.zig").LibCInstallation;
|
||||
const LibCInstallation = std.zig.LibCInstallation;
|
||||
const Liveness = @import("Liveness.zig");
|
||||
const Module = @import("Module.zig");
|
||||
const InternPool = @import("InternPool.zig");
|
||||
|
||||
@ -4616,13 +4616,7 @@ const SystemLib = struct {
|
||||
must_link: bool = false,
|
||||
};
|
||||
|
||||
/// The filesystem layout of darwin SDK elements.
|
||||
pub const SdkLayout = enum {
|
||||
/// macOS SDK layout: TOP { /usr/include, /usr/lib, /System/Library/Frameworks }.
|
||||
sdk,
|
||||
/// Shipped libc layout: TOP { /lib/libc/include, /lib/libc/darwin, <NONE> }.
|
||||
vendored,
|
||||
};
|
||||
pub const SdkLayout = std.zig.LibCDirs.DarwinSdkLayout;
|
||||
|
||||
const UndefinedTreatment = enum {
|
||||
@"error",
|
||||
|
||||
231
src/main.zig
231
src/main.zig
@ -19,8 +19,8 @@ const link = @import("link.zig");
|
||||
const Package = @import("Package.zig");
|
||||
const build_options = @import("build_options");
|
||||
const introspect = @import("introspect.zig");
|
||||
const EnvVar = introspect.EnvVar;
|
||||
const LibCInstallation = @import("libc_installation.zig").LibCInstallation;
|
||||
const EnvVar = std.zig.EnvVar;
|
||||
const LibCInstallation = std.zig.LibCInstallation;
|
||||
const wasi_libc = @import("wasi_libc.zig");
|
||||
const Cache = std.Build.Cache;
|
||||
const target_util = @import("target.zig");
|
||||
@ -294,17 +294,17 @@ fn mainArgs(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {
|
||||
} else if (mem.eql(u8, cmd, "rc")) {
|
||||
return cmdRc(gpa, arena, args[1..]);
|
||||
} else if (mem.eql(u8, cmd, "fmt")) {
|
||||
return jitCmd(gpa, arena, cmd_args, "fmt", "fmt.zig");
|
||||
return jitCmd(gpa, arena, cmd_args, "fmt", "fmt.zig", false);
|
||||
} else if (mem.eql(u8, cmd, "objcopy")) {
|
||||
return @import("objcopy.zig").cmdObjCopy(gpa, arena, cmd_args);
|
||||
} else if (mem.eql(u8, cmd, "fetch")) {
|
||||
return cmdFetch(gpa, arena, cmd_args);
|
||||
} else if (mem.eql(u8, cmd, "libc")) {
|
||||
return cmdLibC(gpa, cmd_args);
|
||||
return jitCmd(gpa, arena, cmd_args, "libc", "libc.zig", true);
|
||||
} else if (mem.eql(u8, cmd, "init")) {
|
||||
return cmdInit(gpa, arena, cmd_args);
|
||||
} else if (mem.eql(u8, cmd, "targets")) {
|
||||
const host = resolveTargetQueryOrFatal(.{});
|
||||
const host = std.zig.resolveTargetQueryOrFatal(.{});
|
||||
const stdout = io.getStdOut().writer();
|
||||
return @import("print_targets.zig").cmdTargets(arena, cmd_args, stdout, host);
|
||||
} else if (mem.eql(u8, cmd, "version")) {
|
||||
@ -317,7 +317,7 @@ fn mainArgs(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {
|
||||
verifyLibcxxCorrectlyLinked();
|
||||
return @import("print_env.zig").cmdEnv(arena, cmd_args, io.getStdOut().writer());
|
||||
} else if (mem.eql(u8, cmd, "reduce")) {
|
||||
return jitCmd(gpa, arena, cmd_args, "reduce", "reduce.zig");
|
||||
return jitCmd(gpa, arena, cmd_args, "reduce", "reduce.zig", false);
|
||||
} else if (mem.eql(u8, cmd, "zen")) {
|
||||
return io.getStdOut().writeAll(info_zen);
|
||||
} else if (mem.eql(u8, cmd, "help") or mem.eql(u8, cmd, "-h") or mem.eql(u8, cmd, "--help")) {
|
||||
@ -3259,7 +3259,7 @@ fn buildOutputType(
|
||||
const triple_name = try target.zigTriple(arena);
|
||||
std.log.err("unable to find or provide libc for target '{s}'", .{triple_name});
|
||||
|
||||
for (target_util.available_libcs) |t| {
|
||||
for (std.zig.target.available_libcs) |t| {
|
||||
if (t.arch == target.cpu.arch and t.os == target.os.tag) {
|
||||
if (t.os_ver) |os_ver| {
|
||||
std.log.info("zig can provide libc for related target {s}-{s}.{d}-{s}", .{
|
||||
@ -3530,16 +3530,16 @@ fn createModule(
|
||||
}
|
||||
}
|
||||
|
||||
const target_query = parseTargetQueryOrReportFatalError(arena, target_parse_options);
|
||||
const target_query = std.zig.parseTargetQueryOrReportFatalError(arena, target_parse_options);
|
||||
const adjusted_target_query = a: {
|
||||
if (!target_query.isNative()) break :a target_query;
|
||||
if (create_module.host_triple) |triple| target_parse_options.arch_os_abi = triple;
|
||||
if (create_module.host_cpu) |cpu| target_parse_options.cpu_features = cpu;
|
||||
if (create_module.host_dynamic_linker) |dl| target_parse_options.dynamic_linker = dl;
|
||||
break :a parseTargetQueryOrReportFatalError(arena, target_parse_options);
|
||||
break :a std.zig.parseTargetQueryOrReportFatalError(arena, target_parse_options);
|
||||
};
|
||||
|
||||
const target = resolveTargetQueryOrFatal(adjusted_target_query);
|
||||
const target = std.zig.resolveTargetQueryOrFatal(adjusted_target_query);
|
||||
break :t .{
|
||||
.result = target,
|
||||
.is_native_os = target_query.isNativeOs(),
|
||||
@ -4210,59 +4210,6 @@ fn serveUpdateResults(s: *Server, comp: *Compilation) !void {
|
||||
}
|
||||
}
|
||||
|
||||
fn parseTargetQueryOrReportFatalError(
|
||||
allocator: Allocator,
|
||||
opts: std.Target.Query.ParseOptions,
|
||||
) std.Target.Query {
|
||||
var opts_with_diags = opts;
|
||||
var diags: std.Target.Query.ParseOptions.Diagnostics = .{};
|
||||
if (opts_with_diags.diagnostics == null) {
|
||||
opts_with_diags.diagnostics = &diags;
|
||||
}
|
||||
return std.Target.Query.parse(opts_with_diags) catch |err| switch (err) {
|
||||
error.UnknownCpuModel => {
|
||||
help: {
|
||||
var help_text = std.ArrayList(u8).init(allocator);
|
||||
defer help_text.deinit();
|
||||
for (diags.arch.?.allCpuModels()) |cpu| {
|
||||
help_text.writer().print(" {s}\n", .{cpu.name}) catch break :help;
|
||||
}
|
||||
std.log.info("available CPUs for architecture '{s}':\n{s}", .{
|
||||
@tagName(diags.arch.?), help_text.items,
|
||||
});
|
||||
}
|
||||
fatal("unknown CPU: '{s}'", .{diags.cpu_name.?});
|
||||
},
|
||||
error.UnknownCpuFeature => {
|
||||
help: {
|
||||
var help_text = std.ArrayList(u8).init(allocator);
|
||||
defer help_text.deinit();
|
||||
for (diags.arch.?.allFeaturesList()) |feature| {
|
||||
help_text.writer().print(" {s}: {s}\n", .{ feature.name, feature.description }) catch break :help;
|
||||
}
|
||||
std.log.info("available CPU features for architecture '{s}':\n{s}", .{
|
||||
@tagName(diags.arch.?), help_text.items,
|
||||
});
|
||||
}
|
||||
fatal("unknown CPU feature: '{s}'", .{diags.unknown_feature_name.?});
|
||||
},
|
||||
error.UnknownObjectFormat => {
|
||||
help: {
|
||||
var help_text = std.ArrayList(u8).init(allocator);
|
||||
defer help_text.deinit();
|
||||
inline for (@typeInfo(std.Target.ObjectFormat).Enum.fields) |field| {
|
||||
help_text.writer().print(" {s}\n", .{field.name}) catch break :help;
|
||||
}
|
||||
std.log.info("available object formats:\n{s}", .{help_text.items});
|
||||
}
|
||||
fatal("unknown object format: '{s}'", .{opts.object_format.?});
|
||||
},
|
||||
else => |e| fatal("unable to parse target query '{s}': {s}", .{
|
||||
opts.arch_os_abi, @errorName(e),
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
fn runOrTest(
|
||||
comp: *Compilation,
|
||||
gpa: Allocator,
|
||||
@ -4871,9 +4818,9 @@ fn detectRcIncludeDirs(arena: Allocator, zig_lib_dir: []const u8, auto_includes:
|
||||
.os_tag = .windows,
|
||||
.abi = .msvc,
|
||||
};
|
||||
const target = resolveTargetQueryOrFatal(target_query);
|
||||
const target = std.zig.resolveTargetQueryOrFatal(target_query);
|
||||
const is_native_abi = target_query.isNativeAbi();
|
||||
const detected_libc = Compilation.detectLibCIncludeDirs(arena, zig_lib_dir, target, is_native_abi, true, null) catch |err| {
|
||||
const detected_libc = std.zig.LibCDirs.detect(arena, zig_lib_dir, target, is_native_abi, true, null) catch |err| {
|
||||
if (cur_includes == .any) {
|
||||
// fall back to mingw
|
||||
cur_includes = .gnu;
|
||||
@ -4899,9 +4846,9 @@ fn detectRcIncludeDirs(arena: Allocator, zig_lib_dir: []const u8, auto_includes:
|
||||
.os_tag = .windows,
|
||||
.abi = .gnu,
|
||||
};
|
||||
const target = resolveTargetQueryOrFatal(target_query);
|
||||
const target = std.zig.resolveTargetQueryOrFatal(target_query);
|
||||
const is_native_abi = target_query.isNativeAbi();
|
||||
const detected_libc = try Compilation.detectLibCIncludeDirs(arena, zig_lib_dir, target, is_native_abi, true, null);
|
||||
const detected_libc = try std.zig.LibCDirs.detect(arena, zig_lib_dir, target, is_native_abi, true, null);
|
||||
return .{
|
||||
.include_paths = detected_libc.libc_include_dir_list,
|
||||
.target_abi = "gnu",
|
||||
@ -4912,136 +4859,6 @@ fn detectRcIncludeDirs(arena: Allocator, zig_lib_dir: []const u8, auto_includes:
|
||||
}
|
||||
}
|
||||
|
||||
const usage_libc =
|
||||
\\Usage: zig libc
|
||||
\\
|
||||
\\ Detect the native libc installation and print the resulting
|
||||
\\ paths to stdout. You can save this into a file and then edit
|
||||
\\ the paths to create a cross compilation libc kit. Then you
|
||||
\\ can pass `--libc [file]` for Zig to use it.
|
||||
\\
|
||||
\\Usage: zig libc [paths_file]
|
||||
\\
|
||||
\\ Parse a libc installation text file and validate it.
|
||||
\\
|
||||
\\Options:
|
||||
\\ -h, --help Print this help and exit
|
||||
\\ -target [name] <arch><sub>-<os>-<abi> see the targets command
|
||||
\\ -includes Print the libc include directories for the target
|
||||
\\
|
||||
;
|
||||
|
||||
fn cmdLibC(gpa: Allocator, args: []const []const u8) !void {
|
||||
var input_file: ?[]const u8 = null;
|
||||
var target_arch_os_abi: []const u8 = "native";
|
||||
var print_includes: bool = false;
|
||||
{
|
||||
var i: usize = 0;
|
||||
while (i < args.len) : (i += 1) {
|
||||
const arg = args[i];
|
||||
if (mem.startsWith(u8, arg, "-")) {
|
||||
if (mem.eql(u8, arg, "-h") or mem.eql(u8, arg, "--help")) {
|
||||
const stdout = io.getStdOut().writer();
|
||||
try stdout.writeAll(usage_libc);
|
||||
return cleanExit();
|
||||
} else if (mem.eql(u8, arg, "-target")) {
|
||||
if (i + 1 >= args.len) fatal("expected parameter after {s}", .{arg});
|
||||
i += 1;
|
||||
target_arch_os_abi = args[i];
|
||||
} else if (mem.eql(u8, arg, "-includes")) {
|
||||
print_includes = true;
|
||||
} else {
|
||||
fatal("unrecognized parameter: '{s}'", .{arg});
|
||||
}
|
||||
} else if (input_file != null) {
|
||||
fatal("unexpected extra parameter: '{s}'", .{arg});
|
||||
} else {
|
||||
input_file = arg;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const target_query = parseTargetQueryOrReportFatalError(gpa, .{
|
||||
.arch_os_abi = target_arch_os_abi,
|
||||
});
|
||||
const target = resolveTargetQueryOrFatal(target_query);
|
||||
|
||||
if (print_includes) {
|
||||
var arena_state = std.heap.ArenaAllocator.init(gpa);
|
||||
defer arena_state.deinit();
|
||||
const arena = arena_state.allocator();
|
||||
|
||||
const libc_installation: ?*LibCInstallation = libc: {
|
||||
if (input_file) |libc_file| {
|
||||
const libc = try arena.create(LibCInstallation);
|
||||
libc.* = LibCInstallation.parse(arena, libc_file, target) catch |err| {
|
||||
fatal("unable to parse libc file at path {s}: {s}", .{ libc_file, @errorName(err) });
|
||||
};
|
||||
break :libc libc;
|
||||
} else {
|
||||
break :libc null;
|
||||
}
|
||||
};
|
||||
|
||||
const self_exe_path = try introspect.findZigExePath(arena);
|
||||
var zig_lib_directory = introspect.findZigLibDirFromSelfExe(arena, self_exe_path) catch |err| {
|
||||
fatal("unable to find zig installation directory: {s}\n", .{@errorName(err)});
|
||||
};
|
||||
defer zig_lib_directory.handle.close();
|
||||
|
||||
const is_native_abi = target_query.isNativeAbi();
|
||||
|
||||
const libc_dirs = Compilation.detectLibCIncludeDirs(
|
||||
arena,
|
||||
zig_lib_directory.path.?,
|
||||
target,
|
||||
is_native_abi,
|
||||
true,
|
||||
libc_installation,
|
||||
) catch |err| {
|
||||
const zig_target = try target.zigTriple(arena);
|
||||
fatal("unable to detect libc for target {s}: {s}", .{ zig_target, @errorName(err) });
|
||||
};
|
||||
|
||||
if (libc_dirs.libc_include_dir_list.len == 0) {
|
||||
const zig_target = try target.zigTriple(arena);
|
||||
fatal("no include dirs detected for target {s}", .{zig_target});
|
||||
}
|
||||
|
||||
var bw = io.bufferedWriter(io.getStdOut().writer());
|
||||
var writer = bw.writer();
|
||||
for (libc_dirs.libc_include_dir_list) |include_dir| {
|
||||
try writer.writeAll(include_dir);
|
||||
try writer.writeByte('\n');
|
||||
}
|
||||
try bw.flush();
|
||||
return cleanExit();
|
||||
}
|
||||
|
||||
if (input_file) |libc_file| {
|
||||
var libc = LibCInstallation.parse(gpa, libc_file, target) catch |err| {
|
||||
fatal("unable to parse libc file at path {s}: {s}", .{ libc_file, @errorName(err) });
|
||||
};
|
||||
defer libc.deinit(gpa);
|
||||
} else {
|
||||
if (!target_query.isNative()) {
|
||||
fatal("unable to detect libc for non-native target", .{});
|
||||
}
|
||||
var libc = LibCInstallation.findNative(.{
|
||||
.allocator = gpa,
|
||||
.verbose = true,
|
||||
.target = target,
|
||||
}) catch |err| {
|
||||
fatal("unable to detect native libc: {s}", .{@errorName(err)});
|
||||
};
|
||||
defer libc.deinit(gpa);
|
||||
|
||||
var bw = io.bufferedWriter(io.getStdOut().writer());
|
||||
try libc.render(bw.writer());
|
||||
try bw.flush();
|
||||
}
|
||||
}
|
||||
|
||||
const usage_init =
|
||||
\\Usage: zig init
|
||||
\\
|
||||
@ -5293,7 +5110,7 @@ fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !void {
|
||||
|
||||
const target_query: std.Target.Query = .{};
|
||||
const resolved_target: Package.Module.ResolvedTarget = .{
|
||||
.result = resolveTargetQueryOrFatal(target_query),
|
||||
.result = std.zig.resolveTargetQueryOrFatal(target_query),
|
||||
.is_native_os = true,
|
||||
.is_native_abi = true,
|
||||
};
|
||||
@ -5711,12 +5528,13 @@ fn jitCmd(
|
||||
args: []const []const u8,
|
||||
cmd_name: []const u8,
|
||||
root_src_path: []const u8,
|
||||
prepend_zig_lib_dir_path: bool,
|
||||
) !void {
|
||||
const color: Color = .auto;
|
||||
|
||||
const target_query: std.Target.Query = .{};
|
||||
const resolved_target: Package.Module.ResolvedTarget = .{
|
||||
.result = resolveTargetQueryOrFatal(target_query),
|
||||
.result = std.zig.resolveTargetQueryOrFatal(target_query),
|
||||
.is_native_os = true,
|
||||
.is_native_abi = true,
|
||||
};
|
||||
@ -5739,6 +5557,7 @@ fn jitCmd(
|
||||
.Debug
|
||||
else
|
||||
.ReleaseFast;
|
||||
const strip = optimize_mode != .Debug;
|
||||
const override_lib_dir: ?[]const u8 = try EnvVar.ZIG_LIB_DIR.get(arena);
|
||||
const override_global_cache_dir: ?[]const u8 = try EnvVar.ZIG_GLOBAL_CACHE_DIR.get(arena);
|
||||
|
||||
@ -5766,7 +5585,7 @@ fn jitCmd(
|
||||
defer thread_pool.deinit();
|
||||
|
||||
var child_argv: std.ArrayListUnmanaged([]const u8) = .{};
|
||||
try child_argv.ensureUnusedCapacity(arena, args.len + 1);
|
||||
try child_argv.ensureUnusedCapacity(arena, args.len + 2);
|
||||
|
||||
// We want to release all the locks before executing the child process, so we make a nice
|
||||
// big block here to ensure the cleanup gets run when we extract out our argv.
|
||||
@ -5781,6 +5600,7 @@ fn jitCmd(
|
||||
|
||||
const config = try Compilation.Config.resolve(.{
|
||||
.output_mode = .Exe,
|
||||
.root_strip = strip,
|
||||
.root_optimize_mode = optimize_mode,
|
||||
.resolved_target = resolved_target,
|
||||
.have_zcu = true,
|
||||
@ -5796,6 +5616,7 @@ fn jitCmd(
|
||||
.inherited = .{
|
||||
.resolved_target = resolved_target,
|
||||
.optimize_mode = optimize_mode,
|
||||
.strip = strip,
|
||||
},
|
||||
.global = config,
|
||||
.parent = null,
|
||||
@ -5829,6 +5650,9 @@ fn jitCmd(
|
||||
child_argv.appendAssumeCapacity(exe_path);
|
||||
}
|
||||
|
||||
if (prepend_zig_lib_dir_path)
|
||||
child_argv.appendAssumeCapacity(zig_lib_directory.path.?);
|
||||
|
||||
child_argv.appendSliceAssumeCapacity(args);
|
||||
|
||||
if (process.can_execv) {
|
||||
@ -6703,7 +6527,7 @@ fn warnAboutForeignBinaries(
|
||||
link_libc: bool,
|
||||
) !void {
|
||||
const host_query: std.Target.Query = .{};
|
||||
const host_target = resolveTargetQueryOrFatal(host_query);
|
||||
const host_target = std.zig.resolveTargetQueryOrFatal(host_query);
|
||||
|
||||
switch (std.zig.system.getExternalExecutor(host_target, target, .{ .link_libc = link_libc })) {
|
||||
.native => return,
|
||||
@ -7559,11 +7383,6 @@ fn parseWasiExecModel(s: []const u8) std.builtin.WasiExecModel {
|
||||
fatal("expected [command|reactor] for -mexec-mode=[value], found '{s}'", .{s});
|
||||
}
|
||||
|
||||
fn resolveTargetQueryOrFatal(target_query: std.Target.Query) std.Target {
|
||||
return std.zig.system.resolveTargetQuery(target_query) catch |err|
|
||||
fatal("unable to resolve target: {s}", .{@errorName(err)});
|
||||
}
|
||||
|
||||
fn parseStackSize(s: []const u8) u64 {
|
||||
return std.fmt.parseUnsigned(u64, s, 0) catch |err|
|
||||
fatal("unable to parse stack size '{s}': {s}", .{ s, @errorName(err) });
|
||||
|
||||
27
src/musl.zig
27
src/musl.zig
@ -4,6 +4,7 @@ const mem = std.mem;
|
||||
const path = std.fs.path;
|
||||
const assert = std.debug.assert;
|
||||
const Module = @import("Package/Module.zig");
|
||||
const archName = std.zig.target.muslArchName;
|
||||
|
||||
const Compilation = @import("Compilation.zig");
|
||||
const build_options = @import("build_options");
|
||||
@ -294,30 +295,6 @@ pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile, prog_node: *std.Progr
|
||||
}
|
||||
}
|
||||
|
||||
fn archName(arch: std.Target.Cpu.Arch) [:0]const u8 {
|
||||
switch (arch) {
|
||||
.aarch64, .aarch64_be => return "aarch64",
|
||||
.arm, .armeb, .thumb, .thumbeb => return "arm",
|
||||
.x86 => return "i386",
|
||||
.mips, .mipsel => return "mips",
|
||||
.mips64el, .mips64 => return "mips64",
|
||||
.powerpc => return "powerpc",
|
||||
.powerpc64, .powerpc64le => return "powerpc64",
|
||||
.riscv64 => return "riscv64",
|
||||
.s390x => return "s390x",
|
||||
.wasm32, .wasm64 => return "wasm",
|
||||
.x86_64 => return "x86_64",
|
||||
else => unreachable,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn archNameHeaders(arch: std.Target.Cpu.Arch) [:0]const u8 {
|
||||
return switch (arch) {
|
||||
.x86 => return "x86",
|
||||
else => archName(arch),
|
||||
};
|
||||
}
|
||||
|
||||
// Return true if musl has arch-specific crti/crtn sources.
|
||||
// See lib/libc/musl/crt/ARCH/crt?.s .
|
||||
pub fn needsCrtiCrtn(target: std.Target) bool {
|
||||
@ -405,7 +382,7 @@ fn addCcArgs(
|
||||
const arch_name = archName(target.cpu.arch);
|
||||
const os_name = @tagName(target.os.tag);
|
||||
const triple = try std.fmt.allocPrint(arena, "{s}-{s}-musl", .{
|
||||
archNameHeaders(target.cpu.arch), os_name,
|
||||
std.zig.target.muslArchNameHeaders(target.cpu.arch), os_name,
|
||||
});
|
||||
const o_arg = if (want_O3) "-O3" else "-Os";
|
||||
|
||||
|
||||
@ -47,9 +47,9 @@ pub fn cmdEnv(arena: Allocator, args: []const []const u8, stdout: std.fs.File.Wr
|
||||
|
||||
try jws.objectField("env");
|
||||
try jws.beginObject();
|
||||
inline for (@typeInfo(introspect.EnvVar).Enum.fields) |field| {
|
||||
inline for (@typeInfo(std.zig.EnvVar).Enum.fields) |field| {
|
||||
try jws.objectField(field.name);
|
||||
try jws.write(try @field(introspect.EnvVar, field.name).get(arena));
|
||||
try jws.write(try @field(std.zig.EnvVar, field.name).get(arena));
|
||||
}
|
||||
try jws.endObject();
|
||||
|
||||
|
||||
@ -67,7 +67,7 @@ pub fn cmdTargets(
|
||||
|
||||
try jws.objectField("libc");
|
||||
try jws.beginArray();
|
||||
for (target.available_libcs) |libc| {
|
||||
for (std.zig.target.available_libcs) |libc| {
|
||||
const tmp = try std.fmt.allocPrint(allocator, "{s}-{s}-{s}", .{
|
||||
@tagName(libc.arch), @tagName(libc.os), @tagName(libc.abi),
|
||||
});
|
||||
|
||||
163
src/target.zig
163
src/target.zig
@ -6,169 +6,6 @@ const Feature = @import("Module.zig").Feature;
|
||||
|
||||
pub const default_stack_protector_buffer_size = 4;
|
||||
|
||||
pub const ArchOsAbi = struct {
|
||||
arch: std.Target.Cpu.Arch,
|
||||
os: std.Target.Os.Tag,
|
||||
abi: std.Target.Abi,
|
||||
os_ver: ?std.SemanticVersion = null,
|
||||
|
||||
// Minimum glibc version that provides support for the arch/os when ABI is GNU.
|
||||
glibc_min: ?std.SemanticVersion = null,
|
||||
};
|
||||
|
||||
pub const available_libcs = [_]ArchOsAbi{
|
||||
.{ .arch = .aarch64_be, .os = .linux, .abi = .gnu, .glibc_min = .{ .major = 2, .minor = 17, .patch = 0 } },
|
||||
.{ .arch = .aarch64_be, .os = .linux, .abi = .musl },
|
||||
.{ .arch = .aarch64_be, .os = .windows, .abi = .gnu },
|
||||
.{ .arch = .aarch64, .os = .linux, .abi = .gnu },
|
||||
.{ .arch = .aarch64, .os = .linux, .abi = .musl },
|
||||
.{ .arch = .aarch64, .os = .windows, .abi = .gnu },
|
||||
.{ .arch = .aarch64, .os = .macos, .abi = .none, .os_ver = .{ .major = 11, .minor = 0, .patch = 0 } },
|
||||
.{ .arch = .armeb, .os = .linux, .abi = .gnueabi },
|
||||
.{ .arch = .armeb, .os = .linux, .abi = .gnueabihf },
|
||||
.{ .arch = .armeb, .os = .linux, .abi = .musleabi },
|
||||
.{ .arch = .armeb, .os = .linux, .abi = .musleabihf },
|
||||
.{ .arch = .armeb, .os = .windows, .abi = .gnu },
|
||||
.{ .arch = .arm, .os = .linux, .abi = .gnueabi },
|
||||
.{ .arch = .arm, .os = .linux, .abi = .gnueabihf },
|
||||
.{ .arch = .arm, .os = .linux, .abi = .musleabi },
|
||||
.{ .arch = .arm, .os = .linux, .abi = .musleabihf },
|
||||
.{ .arch = .thumb, .os = .linux, .abi = .gnueabi },
|
||||
.{ .arch = .thumb, .os = .linux, .abi = .gnueabihf },
|
||||
.{ .arch = .thumb, .os = .linux, .abi = .musleabi },
|
||||
.{ .arch = .thumb, .os = .linux, .abi = .musleabihf },
|
||||
.{ .arch = .arm, .os = .windows, .abi = .gnu },
|
||||
.{ .arch = .csky, .os = .linux, .abi = .gnueabi },
|
||||
.{ .arch = .csky, .os = .linux, .abi = .gnueabihf },
|
||||
.{ .arch = .x86, .os = .linux, .abi = .gnu },
|
||||
.{ .arch = .x86, .os = .linux, .abi = .musl },
|
||||
.{ .arch = .x86, .os = .windows, .abi = .gnu },
|
||||
.{ .arch = .m68k, .os = .linux, .abi = .gnu },
|
||||
.{ .arch = .m68k, .os = .linux, .abi = .musl },
|
||||
.{ .arch = .mips64el, .os = .linux, .abi = .gnuabi64 },
|
||||
.{ .arch = .mips64el, .os = .linux, .abi = .gnuabin32 },
|
||||
.{ .arch = .mips64el, .os = .linux, .abi = .musl },
|
||||
.{ .arch = .mips64, .os = .linux, .abi = .gnuabi64 },
|
||||
.{ .arch = .mips64, .os = .linux, .abi = .gnuabin32 },
|
||||
.{ .arch = .mips64, .os = .linux, .abi = .musl },
|
||||
.{ .arch = .mipsel, .os = .linux, .abi = .gnueabi },
|
||||
.{ .arch = .mipsel, .os = .linux, .abi = .gnueabihf },
|
||||
.{ .arch = .mipsel, .os = .linux, .abi = .musl },
|
||||
.{ .arch = .mips, .os = .linux, .abi = .gnueabi },
|
||||
.{ .arch = .mips, .os = .linux, .abi = .gnueabihf },
|
||||
.{ .arch = .mips, .os = .linux, .abi = .musl },
|
||||
.{ .arch = .powerpc64le, .os = .linux, .abi = .gnu, .glibc_min = .{ .major = 2, .minor = 19, .patch = 0 } },
|
||||
.{ .arch = .powerpc64le, .os = .linux, .abi = .musl },
|
||||
.{ .arch = .powerpc64, .os = .linux, .abi = .gnu },
|
||||
.{ .arch = .powerpc64, .os = .linux, .abi = .musl },
|
||||
.{ .arch = .powerpc, .os = .linux, .abi = .gnueabi },
|
||||
.{ .arch = .powerpc, .os = .linux, .abi = .gnueabihf },
|
||||
.{ .arch = .powerpc, .os = .linux, .abi = .musl },
|
||||
.{ .arch = .riscv64, .os = .linux, .abi = .gnu, .glibc_min = .{ .major = 2, .minor = 27, .patch = 0 } },
|
||||
.{ .arch = .riscv64, .os = .linux, .abi = .musl },
|
||||
.{ .arch = .s390x, .os = .linux, .abi = .gnu },
|
||||
.{ .arch = .s390x, .os = .linux, .abi = .musl },
|
||||
.{ .arch = .sparc, .os = .linux, .abi = .gnu },
|
||||
.{ .arch = .sparc64, .os = .linux, .abi = .gnu },
|
||||
.{ .arch = .wasm32, .os = .freestanding, .abi = .musl },
|
||||
.{ .arch = .wasm32, .os = .wasi, .abi = .musl },
|
||||
.{ .arch = .x86_64, .os = .linux, .abi = .gnu },
|
||||
.{ .arch = .x86_64, .os = .linux, .abi = .gnux32 },
|
||||
.{ .arch = .x86_64, .os = .linux, .abi = .musl },
|
||||
.{ .arch = .x86_64, .os = .windows, .abi = .gnu },
|
||||
.{ .arch = .x86_64, .os = .macos, .abi = .none, .os_ver = .{ .major = 10, .minor = 7, .patch = 0 } },
|
||||
};
|
||||
|
||||
pub fn libCGenericName(target: std.Target) [:0]const u8 {
|
||||
switch (target.os.tag) {
|
||||
.windows => return "mingw",
|
||||
.macos, .ios, .tvos, .watchos => return "darwin",
|
||||
else => {},
|
||||
}
|
||||
switch (target.abi) {
|
||||
.gnu,
|
||||
.gnuabin32,
|
||||
.gnuabi64,
|
||||
.gnueabi,
|
||||
.gnueabihf,
|
||||
.gnuf32,
|
||||
.gnuf64,
|
||||
.gnusf,
|
||||
.gnux32,
|
||||
.gnuilp32,
|
||||
=> return "glibc",
|
||||
.musl,
|
||||
.musleabi,
|
||||
.musleabihf,
|
||||
.muslx32,
|
||||
.none,
|
||||
=> return "musl",
|
||||
.code16,
|
||||
.eabi,
|
||||
.eabihf,
|
||||
.android,
|
||||
.msvc,
|
||||
.itanium,
|
||||
.cygnus,
|
||||
.coreclr,
|
||||
.simulator,
|
||||
.macabi,
|
||||
=> unreachable,
|
||||
|
||||
.pixel,
|
||||
.vertex,
|
||||
.geometry,
|
||||
.hull,
|
||||
.domain,
|
||||
.compute,
|
||||
.library,
|
||||
.raygeneration,
|
||||
.intersection,
|
||||
.anyhit,
|
||||
.closesthit,
|
||||
.miss,
|
||||
.callable,
|
||||
.mesh,
|
||||
.amplification,
|
||||
=> unreachable,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn osArchName(target: std.Target) [:0]const u8 {
|
||||
return switch (target.os.tag) {
|
||||
.linux => switch (target.cpu.arch) {
|
||||
.arm, .armeb, .thumb, .thumbeb => "arm",
|
||||
.aarch64, .aarch64_be, .aarch64_32 => "aarch64",
|
||||
.mips, .mipsel, .mips64, .mips64el => "mips",
|
||||
.powerpc, .powerpcle, .powerpc64, .powerpc64le => "powerpc",
|
||||
.riscv32, .riscv64 => "riscv",
|
||||
.sparc, .sparcel, .sparc64 => "sparc",
|
||||
.x86, .x86_64 => "x86",
|
||||
else => @tagName(target.cpu.arch),
|
||||
},
|
||||
else => @tagName(target.cpu.arch),
|
||||
};
|
||||
}
|
||||
|
||||
pub fn canBuildLibC(target: std.Target) bool {
|
||||
for (available_libcs) |libc| {
|
||||
if (target.cpu.arch == libc.arch and target.os.tag == libc.os and target.abi == libc.abi) {
|
||||
if (target.os.tag == .macos) {
|
||||
const ver = target.os.version_range.semver;
|
||||
return ver.min.order(libc.os_ver.?) != .lt;
|
||||
}
|
||||
// Ensure glibc (aka *-linux-gnu) version is supported
|
||||
if (target.isGnuLibC()) {
|
||||
const min_glibc_ver = libc.glibc_min orelse return true;
|
||||
const target_glibc_ver = target.os.version_range.linux.glibc;
|
||||
return target_glibc_ver.order(min_glibc_ver) != .lt;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
pub fn cannotDynamicLink(target: std.Target) bool {
|
||||
return switch (target.os.tag) {
|
||||
.freestanding, .other => true,
|
||||
|
||||
@ -5,8 +5,6 @@ const path = std.fs.path;
|
||||
const Allocator = std.mem.Allocator;
|
||||
const Compilation = @import("Compilation.zig");
|
||||
const build_options = @import("build_options");
|
||||
const target_util = @import("target.zig");
|
||||
const musl = @import("musl.zig");
|
||||
|
||||
pub const CRTFile = enum {
|
||||
crt1_reactor_o,
|
||||
@ -273,7 +271,7 @@ fn addCCArgs(
|
||||
options: CCOptions,
|
||||
) error{OutOfMemory}!void {
|
||||
const target = comp.getTarget();
|
||||
const arch_name = musl.archNameHeaders(target.cpu.arch);
|
||||
const arch_name = std.zig.target.muslArchNameHeaders(target.cpu.arch);
|
||||
const os_name = @tagName(target.os.tag);
|
||||
const triple = try std.fmt.allocPrint(arena, "{s}-{s}-musl", .{ arch_name, os_name });
|
||||
const o_arg = if (options.want_O3) "-O3" else "-Os";
|
||||
|
||||
@ -662,13 +662,15 @@ uint32_t wasi_snapshot_preview1_fd_filestat_set_times(uint32_t fd, uint64_t atim
|
||||
}
|
||||
|
||||
uint32_t wasi_snapshot_preview1_environ_sizes_get(uint32_t environ_size, uint32_t environ_buf_size) {
|
||||
(void)environ_size;
|
||||
(void)environ_buf_size;
|
||||
uint8_t *const m = *wasm_memory;
|
||||
uint32_t *environ_size_ptr = (uint32_t *)&m[environ_size];
|
||||
uint32_t *environ_buf_size_ptr = (uint32_t *)&m[environ_buf_size];
|
||||
#if LOG_TRACE
|
||||
fprintf(stderr, "wasi_snapshot_preview1_environ_sizes_get()\n");
|
||||
#endif
|
||||
|
||||
panic("unimplemented");
|
||||
*environ_size_ptr = 0;
|
||||
*environ_buf_size_ptr = 0;
|
||||
return wasi_errno_success;
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user