From bd98bcaea04991a2a60ddd5e76d1c4433e296e5e Mon Sep 17 00:00:00 2001 From: Evan Haas Date: Tue, 11 Jul 2023 17:31:28 -0700 Subject: [PATCH 01/50] Distro: code for determining the Linux distro --- src/Distro.zig | 322 +++++++++++++++++++++++++++++++++++++++++++++++++ src/main.zig | 1 + 2 files changed, 323 insertions(+) create mode 100644 src/Distro.zig diff --git a/src/Distro.zig b/src/Distro.zig new file mode 100644 index 00000000..fe9d2823 --- /dev/null +++ b/src/Distro.zig @@ -0,0 +1,322 @@ +//! Tools for figuring out what Linux distro we're running on + +const std = @import("std"); +const mem = std.mem; +const util = @import("util.zig"); + +const MAX_BYTES = 1024; // TODO: Can we assume 1024 bytes enough for the info we need? + +/// Read the file at `path` into `buf`. +/// Returns null if any errors are encountered +/// Otherwise returns a slice of `buf`. If the file is larger than `buf` partial contents are returned +fn readFile(path: []const u8, buf: []u8) ?[]const u8 { + const file = std.fs.openFileAbsolute(path, .{}) catch return null; + defer file.close(); + + const bytes_read = file.readAll(buf) catch return null; + return buf[0..bytes_read]; +} + +pub const Tag = enum { + alpine, + arch, + debian_lenny, + debian_squeeze, + debian_wheezy, + debian_jessie, + debian_stretch, + debian_buster, + debian_bullseye, + debian_bookworm, + debian_trixie, + exherbo, + rhel5, + rhel6, + rhel7, + fedora, + gentoo, + open_suse, + ubuntu_hardy, + ubuntu_intrepid, + ubuntu_jaunty, + ubuntu_karmic, + ubuntu_lucid, + ubuntu_maverick, + ubuntu_natty, + ubuntu_oneiric, + ubuntu_precise, + ubuntu_quantal, + ubuntu_raring, + ubuntu_saucy, + ubuntu_trusty, + ubuntu_utopic, + ubuntu_vivid, + ubuntu_wily, + ubuntu_xenial, + ubuntu_yakkety, + ubuntu_zesty, + ubuntu_artful, + ubuntu_bionic, + ubuntu_cosmic, + ubuntu_disco, + ubuntu_eoan, + ubuntu_focal, + ubuntu_groovy, + ubuntu_hirsute, + ubuntu_impish, + ubuntu_jammy, + ubuntu_kinetic, + ubuntu_lunar, + unknown, + + pub fn isRedhat(self: Tag) bool { + return switch (self) { + .fedora, + .rhel5, + .rhel6, + .rhel7, + => true, + else => false, + }; + } + + pub fn isOpenSUSE(self: Tag) bool { + return self == .open_suse; + } + + pub fn isDebian(self: Tag) bool { + return switch (self) { + .debian_lenny, + .debian_squeeze, + .debian_wheezy, + .debian_jessie, + .debian_stretch, + .debian_buster, + .debian_bullseye, + .debian_bookworm, + .debian_trixie, + => true, + else => false, + }; + } + pub fn isUbuntu(self: Tag) bool { + return switch (self) { + .ubuntu_hardy, + .ubuntu_intrepid, + .ubuntu_jaunty, + .ubuntu_karmic, + .ubuntu_lucid, + .ubuntu_maverick, + .ubuntu_natty, + .ubuntu_oneiric, + .ubuntu_precise, + .ubuntu_quantal, + .ubuntu_raring, + .ubuntu_saucy, + .ubuntu_trusty, + .ubuntu_utopic, + .ubuntu_vivid, + .ubuntu_wily, + .ubuntu_xenial, + .ubuntu_yakkety, + .ubuntu_zesty, + .ubuntu_artful, + .ubuntu_bionic, + .ubuntu_cosmic, + .ubuntu_disco, + .ubuntu_eoan, + .ubuntu_focal, + .ubuntu_groovy, + .ubuntu_hirsute, + .ubuntu_impish, + .ubuntu_jammy, + .ubuntu_kinetic, + .ubuntu_lunar, + => true, + + else => false, + }; + } + pub fn isAlpine(self: Tag) bool { + return self == .alpine; + } + pub fn isGentoo(self: Tag) bool { + return self == .gentoo; + } +}; + +fn scanForOsRelease(buf: []const u8) ?Tag { + var it = mem.splitScalar(u8, buf, '\n'); + while (it.next()) |line| { + if (mem.startsWith(u8, line, "ID=")) { + const rest = line["ID=".len..]; + if (mem.eql(u8, rest, "alpine")) return .alpine; + if (mem.eql(u8, rest, "fedora")) return .fedora; + if (mem.eql(u8, rest, "gentoo")) return .gentoo; + if (mem.eql(u8, rest, "arch")) return .arch; + if (mem.eql(u8, rest, "sles")) return .open_suse; + if (mem.eql(u8, rest, "opensuse")) return .open_suse; + if (mem.eql(u8, rest, "exherbo")) return .exherbo; + } + } + return null; +} + +fn detectOsRelease() ?Tag { + var buf: [MAX_BYTES]u8 = undefined; + const data = readFile("/etc/os-release", &buf) orelse readFile("/usr/lib/os-release", &buf) orelse return null; + return scanForOsRelease(data); +} + +fn scanForLSBRelease(buf: []const u8) ?Tag { + var it = mem.splitScalar(u8, buf, '\n'); + while (it.next()) |line| { + if (mem.startsWith(u8, line, "DISTRIB_CODENAME=")) { + const rest = line["DISTRIB_CODENAME=".len..]; + if (mem.eql(u8, rest, "hardy")) return .ubuntu_hardy; + if (mem.eql(u8, rest, "intrepid")) return .ubuntu_intrepid; + if (mem.eql(u8, rest, "jaunty")) return .ubuntu_jaunty; + if (mem.eql(u8, rest, "karmic")) return .ubuntu_karmic; + if (mem.eql(u8, rest, "lucid")) return .ubuntu_lucid; + if (mem.eql(u8, rest, "maverick")) return .ubuntu_maverick; + if (mem.eql(u8, rest, "natty")) return .ubuntu_natty; + if (mem.eql(u8, rest, "oneiric")) return .ubuntu_oneiric; + if (mem.eql(u8, rest, "precise")) return .ubuntu_precise; + if (mem.eql(u8, rest, "quantal")) return .ubuntu_quantal; + if (mem.eql(u8, rest, "raring")) return .ubuntu_raring; + if (mem.eql(u8, rest, "saucy")) return .ubuntu_saucy; + if (mem.eql(u8, rest, "trusty")) return .ubuntu_trusty; + if (mem.eql(u8, rest, "utopic")) return .ubuntu_utopic; + if (mem.eql(u8, rest, "vivid")) return .ubuntu_vivid; + if (mem.eql(u8, rest, "wily")) return .ubuntu_wily; + if (mem.eql(u8, rest, "xenial")) return .ubuntu_xenial; + if (mem.eql(u8, rest, "yakkety")) return .ubuntu_yakkety; + if (mem.eql(u8, rest, "zesty")) return .ubuntu_zesty; + if (mem.eql(u8, rest, "artful")) return .ubuntu_artful; + if (mem.eql(u8, rest, "bionic")) return .ubuntu_bionic; + if (mem.eql(u8, rest, "cosmic")) return .ubuntu_cosmic; + if (mem.eql(u8, rest, "disco")) return .ubuntu_disco; + if (mem.eql(u8, rest, "eoan")) return .ubuntu_eoan; + if (mem.eql(u8, rest, "focal")) return .ubuntu_focal; + if (mem.eql(u8, rest, "groovy")) return .ubuntu_groovy; + if (mem.eql(u8, rest, "hirsute")) return .ubuntu_hirsute; + if (mem.eql(u8, rest, "impish")) return .ubuntu_impish; + if (mem.eql(u8, rest, "jammy")) return .ubuntu_jammy; + if (mem.eql(u8, rest, "kinetic")) return .ubuntu_kinetic; + if (mem.eql(u8, rest, "lunar")) return .ubuntu_lunar; + } + } + return null; +} + +fn detectLSBRelease() ?Tag { + var buf: [MAX_BYTES]u8 = undefined; + const data = readFile("/etc/lsb-release", &buf) orelse return null; + + return scanForLSBRelease(data); +} + +fn scanForRedHat(buf: []const u8) Tag { + if (mem.startsWith(u8, buf, "Fedora release")) return .fedora; + if (mem.startsWith(u8, buf, "Red Hat Enterprise Linux") or mem.startsWith(u8, buf, "CentOS") or mem.startsWith(u8, buf, "Scientific Linux")) { + if (mem.indexOfPos(u8, buf, 0, "release 7") != null) return .rhel7; + if (mem.indexOfPos(u8, buf, 0, "release 6") != null) return .rhel6; + if (mem.indexOfPos(u8, buf, 0, "release 5") != null) return .rhel5; + } + + return .unknown; +} + +fn detectRedhat() ?Tag { + var buf: [MAX_BYTES]u8 = undefined; + const data = readFile("/etc/redhat-release", &buf) orelse return null; + return scanForRedHat(data); +} + +fn scanForDebian(buf: []const u8) Tag { + var it = mem.splitScalar(u8, buf, '.'); + if (std.fmt.parseInt(u8, it.next().?, 10)) |major| { + return switch (major) { + 5 => .debian_lenny, + 6 => .debian_squeeze, + 7 => .debian_wheezy, + 8 => .debian_jessie, + 9 => .debian_stretch, + 10 => .debian_buster, + 11 => .debian_bullseye, + 12 => .debian_bookworm, + 13 => .debian_trixie, + else => .unknown, + }; + } else |_| {} + + it = mem.splitScalar(u8, buf, '\n'); + const name = it.next().?; + if (mem.eql(u8, name, "squeeze/sid")) return .debian_squeeze; + if (mem.eql(u8, name, "wheezy/sid")) return .debian_wheezy; + if (mem.eql(u8, name, "jessie/sid")) return .debian_jessie; + if (mem.eql(u8, name, "stretch/sid")) return .debian_stretch; + if (mem.eql(u8, name, "buster/sid")) return .debian_buster; + if (mem.eql(u8, name, "bullseye/sid")) return .debian_bullseye; + if (mem.eql(u8, name, "bookworm/sid")) return .debian_bookworm; + + return .unknown; +} + +fn detectDebian() ?Tag { + var buf: [MAX_BYTES]u8 = undefined; + const data = readFile("/etc/debian_version", &buf) orelse return null; + return scanForDebian(data); +} + +pub fn detect(target: std.Target) Tag { + if (target.os.tag != .linux) return .unknown; + + if (detectOsRelease()) |tag| return tag; + if (detectLSBRelease()) |tag| return tag; + if (detectRedhat()) |tag| return tag; + if (detectDebian()) |tag| return tag; + + if (util.exists("/etc/gentoo-release")) return .gentoo; + + return .unknown; +} + +test scanForDebian { + try std.testing.expectEqual(Tag.debian_squeeze, scanForDebian("squeeze/sid")); + try std.testing.expectEqual(Tag.debian_bullseye, scanForDebian("11.1.2")); + try std.testing.expectEqual(Tag.unknown, scanForDebian("None")); + try std.testing.expectEqual(Tag.unknown, scanForDebian("")); +} + +test scanForRedHat { + try std.testing.expectEqual(Tag.fedora, scanForRedHat("Fedora release 7")); + try std.testing.expectEqual(Tag.rhel7, scanForRedHat("Red Hat Enterprise Linux release 7")); + try std.testing.expectEqual(Tag.rhel5, scanForRedHat("CentOS release 5")); + try std.testing.expectEqual(Tag.unknown, scanForRedHat("CentOS release 4")); + try std.testing.expectEqual(Tag.unknown, scanForRedHat("")); +} + +test scanForLSBRelease { + const text = + \\DISTRIB_ID=Ubuntu + \\DISTRIB_RELEASE=20.04 + \\DISTRIB_CODENAME=focal + \\DISTRIB_DESCRIPTION="Ubuntu 20.04.6 LTS" + \\ + ; + try std.testing.expectEqual(Tag.ubuntu_focal, scanForLSBRelease(text).?); +} + +test scanForOsRelease { + const text = + \\NAME="Alpine Linux" + \\ID=alpine + \\VERSION_ID=3.18.2 + \\PRETTY_NAME="Alpine Linux v3.18" + \\HOME_URL="https://alpinelinux.org/" + \\BUG_REPORT_URL="https://gitlab.alpinelinux.org/alpine/aports/-/issues" + \\ + ; + try std.testing.expectEqual(Tag.alpine, scanForOsRelease(text).?); +} diff --git a/src/main.zig b/src/main.zig index 3363c201..975faf2d 100644 --- a/src/main.zig +++ b/src/main.zig @@ -79,6 +79,7 @@ test { _ = @import("Codegen_legacy.zig"); _ = @import("Compilation.zig"); _ = @import("Diagnostics.zig"); + _ = @import("Distro.zig"); _ = @import("InitList.zig"); _ = @import("LangOpts.zig"); _ = @import("Parser.zig"); From 72a860f3ad7e3756b66d3c8dc12063830179182b Mon Sep 17 00:00:00 2001 From: Evan Haas Date: Wed, 12 Jul 2023 00:30:48 -0700 Subject: [PATCH 02/50] build.zig: Add default linker option --- build.zig | 2 ++ 1 file changed, 2 insertions(+) diff --git a/build.zig b/build.zig index 81773d8a..1cafa570 100644 --- a/build.zig +++ b/build.zig @@ -40,6 +40,7 @@ pub fn build(b: *Build) !void { // between Debug, ReleaseSafe, ReleaseFast, and ReleaseSmall. const mode = b.standardOptimizeOption(.{}); + const default_linker = b.option([]const u8, "default-linker", "Default linker aro will use if none is supplied via -fuse-ld") orelse "ld"; const test_all_allocation_failures = b.option(bool, "test-all-allocation-failures", "Test all allocation failures") orelse false; const link_libc = b.option(bool, "link-libc", "Force self-hosted compiler to link libc") orelse (mode != .Debug); const tracy = b.option([]const u8, "tracy", "Enable Tracy integration. Supply path to Tracy source"); @@ -65,6 +66,7 @@ pub fn build(b: *Build) !void { }); const exe_options = b.addOptions(); exe.addOptions("build_options", exe_options); + exe_options.addOption([]const u8, "default_linker", default_linker); exe_options.addOption(bool, "enable_tracy", tracy != null); exe_options.addOption(bool, "enable_tracy_callstack", tracy_callstack); exe_options.addOption(bool, "enable_tracy_allocation", tracy_allocation); From cc105ccb42e5f5724209251c348df204581a4c0a Mon Sep 17 00:00:00 2001 From: Evan Haas Date: Tue, 11 Jul 2023 17:49:37 -0700 Subject: [PATCH 03/50] Toolchain: code for finding/using other parts of the build toolchain --- src/Diagnostics.zig | 6 + src/Distro.zig | 17 +++ src/Driver.zig | 130 ++++++++++++------- src/Toolchain.zig | 245 ++++++++++++++++++++++++++++++++++++ src/main.zig | 9 +- src/target.zig | 36 ++++++ src/toolchains/Linux.zig | 262 +++++++++++++++++++++++++++++++++++++++ src/util.zig | 11 ++ 8 files changed, 670 insertions(+), 46 deletions(-) create mode 100644 src/Toolchain.zig create mode 100644 src/toolchains/Linux.zig diff --git a/src/Diagnostics.zig b/src/Diagnostics.zig index 78add705..883fa381 100644 --- a/src/Diagnostics.zig +++ b/src/Diagnostics.zig @@ -165,6 +165,7 @@ pub const Options = packed struct { @"gnu-auto-type": Kind = .default, @"gnu-union-cast": Kind = .default, @"pointer-sign": Kind = .default, + @"fuse-ld-path": Kind = .default, }; const messages = struct { @@ -2351,6 +2352,11 @@ const messages = struct { const msg = "environment variable SOURCE_DATE_EPOCH must expand to a non-negative integer less than or equal to 253402300799"; const kind = .@"error"; }; + pub const fuse_ld_path = struct { + const msg = "'-fuse-ld=' taking a path is deprecated; use '--ld-path=' instead"; + const kind = .off; + const opt = "fuse-ld-path"; + }; }; list: std.ArrayListUnmanaged(Message) = .{}, diff --git a/src/Distro.zig b/src/Distro.zig index fe9d2823..9e35e968 100644 --- a/src/Distro.zig +++ b/src/Distro.zig @@ -6,6 +6,12 @@ const util = @import("util.zig"); const MAX_BYTES = 1024; // TODO: Can we assume 1024 bytes enough for the info we need? +/// Value for linker `--hash-style=` argument +pub const HashStyle = enum { + both, + gnu, +}; + /// Read the file at `path` into `buf`. /// Returns null if any errors are encountered /// Otherwise returns a slice of `buf`. If the file is larger than `buf` partial contents are returned @@ -69,6 +75,17 @@ pub const Tag = enum { ubuntu_lunar, unknown, + pub fn getHashStyle(self: Tag) HashStyle { + if (self.isOpenSUSE()) return .both; + return switch (self) { + .ubuntu_lucid, + .ubuntu_jaunty, + .ubuntu_karmic, + => .both, + else => .gnu, + }; + } + pub fn isRedhat(self: Tag) bool { return switch (self) { .fedora, diff --git a/src/Driver.zig b/src/Driver.zig index e87bb8a5..2bc2d83d 100644 --- a/src/Driver.zig +++ b/src/Driver.zig @@ -8,6 +8,7 @@ const LangOpts = @import("LangOpts.zig"); const Preprocessor = @import("Preprocessor.zig"); const Parser = @import("Parser.zig"); const Source = @import("Source.zig"); +const Toolchain = @import("Toolchain.zig"); const util = @import("util.zig"); const target_util = @import("target.zig"); @@ -25,6 +26,7 @@ comp: *Compilation, inputs: std.ArrayListUnmanaged(Source) = .{}, link_objects: std.ArrayListUnmanaged([]const u8) = .{}, output_name: ?[]const u8 = null, +sysroot: ?[]const u8 = null, temp_file_count: u32 = 0, only_preprocess: bool = false, only_syntax: bool = false, @@ -33,10 +35,27 @@ only_preprocess_and_compile: bool = false, verbose_ast: bool = false, verbose_pp: bool = false, verbose_ir: bool = false, +verbose_linker_args: bool = false, + +/// name of the aro executable +aro_name: []const u8 = "arocc", +/// Directory from which aro was invoked +aro_dir: []const u8 = "", // linker options -use_linker: Linker = .ld, +use_linker: ?[]const u8 = null, linker_path: ?[]const u8 = null, +nodefaultlibs: bool = false, +nolibc: bool = false, +nostartfiles: bool = false, +nostdlib: bool = false, +pie: ?bool = null, +rdynamic: bool = false, +relocatable: bool = false, +shared: bool = false, +static: bool = false, +static_pie: bool = false, +strip: bool = false, pub fn deinit(d: *Driver) void { for (d.link_objects.items[d.link_objects.items.len - d.temp_file_count ..]) |obj| { @@ -93,6 +112,7 @@ pub const usage = \\ -pedantic Warn on language extensions \\ -std= Specify language standard \\ -S, --assemble Only run preprocess and compilation steps + \\ --sysroot= Use dir as the logical root directory for headers and libraries (not fully implemented) \\ --target= Generate code for the given target \\ -U Undefine \\ -Werror Treat all warnings as errors @@ -103,12 +123,24 @@ pub const usage = \\Link options: \\ -fuse-ld=[bfd|gold|lld|mold] \\ Use specific linker + \\ -nodefaultlibs Do not use the standard system libraries when linking. + \\ -nolibc Do not use the C library or system libraries tightly coupled with it when linking. + \\ -nostdlib Do not use the standard system startup files or libraries when linking + \\ -nostartfiles Do not use the standard system startup files when linking. + \\ -pie Produce a dynamically linked position independent executable on targets that support it. \\ --ld-path= Use linker specified by + \\ -r Produce a relocatable object as output. + \\ -rdynamic Pass the flag -export-dynamic to the ELF linker, on targets that support it. + \\ -s Remove all symbol table and relocation information from the executable. + \\ -shared Produce a shared object which can then be linked with other objects to form an executable. + \\ -static On systems that support dynamic linking, this overrides -pie and prevents linking with the shared libraries. + \\ -static-pie Produce a static position independent executable on targets that support it. \\ \\Debug options: \\ --verbose-ast Dump produced AST to stdout \\ --verbose-pp Dump preprocessor state \\ --verbose-ir Dump ir to stdout + \\ --verbose-linker-args Dump linker args to stdout \\ \\ ; @@ -270,6 +302,8 @@ pub fn parseArgs( file = args[i]; } d.output_name = file; + } else if (option(arg, "--sysroot=")) |sysroot| { + d.sysroot = sysroot; } else if (mem.eql(u8, arg, "-pedantic")) { d.comp.diag.options.pedantic = .warning; } else if (option(arg, "-Werror=")) |err_name| { @@ -300,13 +334,38 @@ pub fn parseArgs( d.verbose_pp = true; } else if (mem.eql(u8, arg, "--verbose-ir")) { d.verbose_ir = true; + } else if (mem.eql(u8, arg, "--verbose-linker-args")) { + d.verbose_linker_args = true; } else if (option(arg, "-fuse-ld=")) |linker_name| { - d.use_linker = std.meta.stringToEnum(Linker, linker_name) orelse { - try d.comp.diag.add(.{ .tag = .cli_unknown_linker, .extra = .{ .str = arg } }, &.{}); - continue; - }; + d.use_linker = linker_name; + } else if (mem.eql(u8, arg, "-fuse-ld=")) { + d.use_linker = null; } else if (option(arg, "--ld-path=")) |linker_path| { d.linker_path = linker_path; + } else if (mem.eql(u8, arg, "-r")) { + d.relocatable = true; + } else if (mem.eql(u8, arg, "-shared")) { + d.shared = true; + } else if (mem.eql(u8, arg, "-static")) { + d.static = true; + } else if (mem.eql(u8, arg, "-static-pie")) { + d.static_pie = true; + } else if (mem.eql(u8, arg, "-pie")) { + d.pie = true; + } else if (mem.eql(u8, arg, "-no-pie") or mem.eql(u8, arg, "-nopie")) { + d.pie = false; + } else if (mem.eql(u8, arg, "-rdynamic")) { + d.rdynamic = true; + } else if (mem.eql(u8, arg, "-s")) { + d.strip = true; + } else if (mem.eql(u8, arg, "-nodefaultlibs")) { + d.nodefaultlibs = true; + } else if (mem.eql(u8, arg, "-nolibc")) { + d.nolibc = true; + } else if (mem.eql(u8, arg, "-nostdlib")) { + d.nostdlib = true; + } else if (mem.eql(u8, arg, "-nostartfiles")) { + d.nostartfiles = true; } else { try d.comp.diag.add(.{ .tag = .cli_unknown_arg, .extra = .{ .str = arg } }, &.{}); } @@ -324,6 +383,8 @@ pub fn parseArgs( .off => false, .unset => util.fileSupportsColor(std.io.getStdErr()) and !std.process.hasEnvVarConstant("NO_COLOR"), }; + d.aro_name = std.fs.path.basename(args[0]); + d.aro_dir = std.fs.path.dirname(args[0]) orelse ""; return false; } @@ -344,21 +405,22 @@ fn addSource(d: *Driver, path: []const u8) !Source { return d.comp.addSourceFromPath(path); } -fn err(d: *Driver, msg: []const u8) !void { +pub fn err(d: *Driver, msg: []const u8) !void { try d.comp.diag.add(.{ .tag = .cli_error, .extra = .{ .str = msg } }, &.{}); } -fn fatal(d: *Driver, comptime fmt: []const u8, args: anytype) error{FatalError} { +pub fn fatal(d: *Driver, comptime fmt: []const u8, args: anytype) error{FatalError} { d.comp.renderErrors(); return d.comp.diag.fatalNoSrc(fmt, args); } -pub fn main(d: *Driver, args: []const []const u8) !void { +pub fn main(d: *Driver, tc: *Toolchain, args: []const []const u8) !void { var macro_buf = std.ArrayList(u8).init(d.comp.gpa); defer macro_buf.deinit(); const std_out = std.io.getStdOut().writer(); if (try parseArgs(d, std_out, macro_buf.writer(), args)) return; + try tc.discover(); const linking = !(d.only_preprocess or d.only_syntax or d.only_compile or d.only_preprocess_and_compile); if (d.inputs.items.len == 0) { @@ -371,10 +433,6 @@ pub fn main(d: *Driver, args: []const []const u8) !void { try d.comp.diag.add(.{ .tag = .cli_unused_link_object, .extra = .{ .str = obj } }, &.{}); }; - if (linking and d.linker_path == null) { - d.linker_path = d.getLinkerPath(); - } - d.comp.defineSystemIncludes() catch |er| switch (er) { error.OutOfMemory => return error.OutOfMemory, error.SelfExeNotFound => return d.fatal("unable to find Aro executable path", .{}), @@ -387,7 +445,7 @@ pub fn main(d: *Driver, args: []const []const u8) !void { const fast_exit = @import("builtin").mode != .Debug; if (fast_exit and d.inputs.items.len == 1) { - d.processSource(d.inputs.items[0], builtin, user_macros, fast_exit) catch |e| switch (e) { + d.processSource(tc, d.inputs.items[0], builtin, user_macros, fast_exit) catch |e| switch (e) { error.OutOfMemory => return error.OutOfMemory, error.FatalError => { d.comp.renderErrors(); @@ -398,7 +456,7 @@ pub fn main(d: *Driver, args: []const []const u8) !void { } for (d.inputs.items) |source| { - d.processSource(source, builtin, user_macros, fast_exit) catch |e| switch (e) { + d.processSource(tc, source, builtin, user_macros, fast_exit) catch |e| switch (e) { error.OutOfMemory => return error.OutOfMemory, error.FatalError => { d.comp.renderErrors(); @@ -410,7 +468,7 @@ pub fn main(d: *Driver, args: []const []const u8) !void { return; } if (linking) { - try d.invokeLinker(); + try d.invokeLinker(tc); if (fast_exit) d.exitWithCleanup(0); } if (fast_exit) std.process.exit(0); @@ -418,6 +476,7 @@ pub fn main(d: *Driver, args: []const []const u8) !void { fn processSource( d: *Driver, + tc: *Toolchain, source: Source, builtin: Source, user_macros: Source, @@ -538,31 +597,25 @@ fn processSource( d.link_objects.appendAssumeCapacity(try d.comp.gpa.dupe(u8, out_file_name)); d.temp_file_count += 1; if (fast_exit) { - try d.invokeLinker(); + try d.invokeLinker(tc); d.exitWithCleanup(0); } } -pub fn invokeLinker(d: *Driver) !void { - const args_len = 1 // linker name - + 2 // -o output - + 2 // -dynamic-linker - + 1 // -lc - + 1 // Scrt1.0 - + d.link_objects.items.len; - - var argv = try std.ArrayList([]const u8).initCapacity(d.comp.gpa, args_len); +pub fn invokeLinker(d: *Driver, tc: *Toolchain) !void { + var argv = std.ArrayList([]const u8).init(d.comp.gpa); defer argv.deinit(); - argv.appendAssumeCapacity(d.linker_path.?); - argv.appendAssumeCapacity("-o"); - argv.appendAssumeCapacity(d.output_name orelse "a.out"); - argv.appendAssumeCapacity("-dynamic-linker"); - argv.appendAssumeCapacity(d.comp.target.standardDynamicLinkerPath().get().?); - argv.appendAssumeCapacity("-lc"); - argv.appendAssumeCapacity("/usr/lib/Scrt1.o"); // TODO very bad - argv.appendSliceAssumeCapacity(d.link_objects.items); + var linker_path_buf: [std.fs.MAX_PATH_BYTES]u8 = undefined; + const linker_path = try tc.getLinkerPath(&linker_path_buf); + try argv.append(linker_path); + try tc.buildLinkerArgs(&argv); + + if (d.verbose_linker_args) { + const stdout = std.io.getStdOut().writer(); + try stdout.print("{s}\n", .{argv.items}); + } var child = std.ChildProcess.init(argv.items, d.comp.gpa); // TODO handle better child.stdin_behavior = .Inherit; @@ -585,14 +638,3 @@ fn exitWithCleanup(d: *Driver, code: u8) noreturn { } std.process.exit(code); } - -pub fn getLinkerPath(d: *Driver) []const u8 { - // TODO extremely incomplete - return switch (d.use_linker) { - .ld => "/usr/bin/ld", - .bfd => "/usr/bin/ld.bfd", - .gold => "/usr/bin/ld.gold", - .lld => "/usr/bin/ld.lld", - .mold => "/usr/bin/ld.mold", - }; -} diff --git a/src/Toolchain.zig b/src/Toolchain.zig new file mode 100644 index 00000000..ad1d1b82 --- /dev/null +++ b/src/Toolchain.zig @@ -0,0 +1,245 @@ +const std = @import("std"); +const Driver = @import("Driver.zig"); +const Compilation = @import("Compilation.zig"); +const util = @import("util.zig"); +const mem = std.mem; +const build_options = @import("build_options"); +const Linux = @import("toolchains/Linux.zig"); + +const Toolchain = @This(); + +pub const PathList = std.ArrayListUnmanaged([]const u8); + +const Inner = union(enum) { + linux: Linux, + unknown: void, + + fn deinit(self: *Inner, allocator: mem.Allocator) void { + switch (self.*) { + .linux => |*linux| linux.deinit(allocator), + .unknown => {}, + } + } +}; + +driver: *Driver, +arena: mem.Allocator, + +/// The list of toolchain specific path prefixes to search for libraries. +library_paths: PathList = .{}, + +/// The list of toolchain specific path prefixes to search for files. +file_paths: PathList = .{}, + +/// The list of toolchain specific path prefixes to search for programs. +program_paths: PathList = .{}, + +inner: Inner = .{ .unknown = {} }, + +pub fn getTarget(self: *const Toolchain) std.Target { + return self.driver.comp.target; +} + +fn getDefaultLinker(self: *const Toolchain) []const u8 { + _ = self; + return "ld"; +} + +/// Call this after driver has finished parsing command line arguments to find the toolchain +pub fn discover(self: *Toolchain) !void { + const target = self.getTarget(); + self.inner = switch (target.os.tag) { + .elfiamcu, + .linux, + => if (target.cpu.arch == .hexagon) + .{ .unknown = {} } // TODO + else if (target.cpu.arch.isMIPS()) + .{ .unknown = {} } // TODO + else if (target.cpu.arch.isPPC()) + .{ .unknown = {} } // TODO + else if (target.cpu.arch == .ve) + .{ .unknown = {} } // TODO + else + .{ .linux = .{} }, + else => .{ .unknown = {} }, // TODO + }; + return switch (self.inner) { + .linux => |*linux| linux.discover(self), + .unknown => {}, + }; +} + +pub fn deinit(self: *Toolchain) void { + const gpa = self.driver.comp.gpa; + self.inner.deinit(gpa); + + for (self.library_paths.items) |item| { + gpa.free(item); + } + self.library_paths.deinit(gpa); + + for (self.file_paths.items) |item| { + gpa.free(item); + } + self.file_paths.deinit(gpa); + + for (self.program_paths.items) |item| { + gpa.free(item); + } + self.program_paths.deinit(gpa); +} + +/// Write linker path to `buf` and return a slice of it +pub fn getLinkerPath(self: *const Toolchain, buf: []u8) ![]const u8 { + // --ld-path= takes precedence over -fuse-ld= and specifies the executable + // name. -B, COMPILER_PATH and PATH are consulted if the value does not + // contain a path component separator. + // -fuse-ld=lld can be used with --ld-path= to indicate that the binary + // that --ld-path= points to is lld. + const use_linker = self.driver.use_linker orelse build_options.default_linker; + + if (self.driver.linker_path) |ld_path| { + var path = ld_path; + if (path.len > 0) { + if (std.fs.path.dirname(path) == null) { + path = self.getProgramPath(path, buf); + } + if (util.canExecute(path)) { + return path; + } + } + return self.driver.fatal( + "invalid linker name in argument '--ld-path={s}'", + .{path}, + ); + } + + // If we're passed -fuse-ld= with no argument, or with the argument ld, + // then use whatever the default system linker is. + if (use_linker.len == 0 or mem.eql(u8, use_linker, "ld")) { + const default = self.getDefaultLinker(); + if (std.fs.path.isAbsolute(default)) return default; + return self.getProgramPath(default, buf); + } + + // Extending -fuse-ld= to an absolute or relative path is unexpected. Checking + // for the linker flavor is brittle. In addition, prepending "ld." or "ld64." + // to a relative path is surprising. This is more complex due to priorities + // among -B, COMPILER_PATH and PATH. --ld-path= should be used instead. + if (mem.indexOfScalar(u8, use_linker, '/') != null) { + try self.driver.comp.diag.add(.{ .tag = .fuse_ld_path }, &.{}); + } + + if (std.fs.path.isAbsolute(use_linker)) { + if (util.canExecute(use_linker)) { + return use_linker; + } + } else { + var linker_name = try std.ArrayList(u8).initCapacity(self.driver.comp.gpa, 5 + use_linker.len); // "ld64." ++ use_linker + defer linker_name.deinit(); + if (self.getTarget().isDarwin()) { + linker_name.appendSliceAssumeCapacity("ld64."); + } else { + linker_name.appendSliceAssumeCapacity("ld."); + } + linker_name.appendSliceAssumeCapacity(use_linker); + const linker_path = self.getProgramPath(linker_name.items, buf); + if (util.canExecute(linker_path)) { + return linker_path; + } + } + + if (self.driver.use_linker) |linker| { + return self.driver.fatal( + "invalid linker name in argument '-fuse-ld={s}'", + .{linker}, + ); + } + const default_linker = self.getDefaultLinker(); + return self.getProgramPath(default_linker, buf); +} + +fn getProgramPath(toolchain: *const Toolchain, name: []const u8, buf: []u8) []const u8 { + _ = toolchain; + _ = buf; + return name; +} + +const PathStackSize = 128; +const PathAllocator = std.heap.StackFallbackAllocator(PathStackSize); + +pub fn getFilePath(toolchain: *const Toolchain, name: []const u8) ![]const u8 { + const d = toolchain.driver; + + var stack_fb = std.heap.stackFallback(PathStackSize, d.comp.gpa); + var allocator = stack_fb.get(); + + // todo check resource dir + // todo check compiler RT path + + const candidate = try std.fs.path.join(allocator, &.{ d.aro_dir, "..", name }); + defer allocator.free(candidate); + if (util.exists(candidate)) { + return toolchain.arena.dupe(u8, candidate); + } + + if (try searchPaths(&stack_fb, toolchain.library_paths.items, name)) |path| { + defer allocator.free(path); + return toolchain.arena.dupe(u8, path); + } + + if (try searchPaths(&stack_fb, toolchain.file_paths.items, name)) |path| { + defer allocator.free(path); + return try toolchain.arena.dupe(u8, path); + } + + return name; +} + +/// find path +fn searchPaths(path_allocator: *PathAllocator, paths: []const []const u8, name: []const u8) !?[]const u8 { + for (paths) |path| { + if (path.len == 0) continue; + + const allocator = path_allocator.get(); // resets underlying fixed buffer + const candidate = try std.fs.path.join(allocator, &.{ path, name }); + + if (util.exists(candidate)) { + const duped = try path_allocator.fallback_allocator.dupe(u8, candidate); + return duped; + } + allocator.free(candidate); + } + return null; +} + +const PathKind = enum { + library, + file, + program, +}; + +pub fn addPathIfExists(self: *Toolchain, components: []const []const u8, dest_kind: PathKind) !void { + var stack_fb = std.heap.stackFallback(PathStackSize, self.driver.comp.gpa); + var allocator = stack_fb.get(); + const candidate = try std.fs.path.join(allocator, components); + defer allocator.free(candidate); + if (util.exists(candidate)) { + const gpa = self.driver.comp.gpa; + const duped = try gpa.dupe(u8, candidate); + errdefer gpa.free(duped); + const dest = switch (dest_kind) { + .library => &self.library_paths, + .file => &self.file_paths, + .program => &self.program_paths, + }; + try dest.append(gpa, duped); + } +} + +pub fn buildLinkerArgs(self: *Toolchain, argv: *std.ArrayList([]const u8)) !void { + return switch (self.inner) { + .linux => |*linux| linux.buildLinkerArgs(self, argv), + .unknown => {}, + }; +} diff --git a/src/main.zig b/src/main.zig index 975faf2d..8958ec04 100644 --- a/src/main.zig +++ b/src/main.zig @@ -5,10 +5,11 @@ const process = std.process; const Compilation = @import("Compilation.zig"); const Driver = @import("Driver.zig"); const target_util = @import("target.zig"); +const Toolchain = @import("Toolchain.zig"); var general_purpose_allocator = std.heap.GeneralPurposeAllocator(.{}){}; -pub fn main() u8 { +pub fn main() !u8 { const gpa = if (@import("builtin").link_libc) std.heap.raw_c_allocator else @@ -53,7 +54,10 @@ pub fn main() u8 { var driver: Driver = .{ .comp = &comp }; defer driver.deinit(); - driver.main(args) catch |er| switch (er) { + var toolchain: Toolchain = .{ .driver = &driver, .arena = arena }; + defer toolchain.deinit(); + + driver.main(&toolchain, args) catch |er| switch (er) { error.OutOfMemory => { std.debug.print("out of memory\n", .{}); if (fast_exit) std.process.exit(1); @@ -69,6 +73,7 @@ pub fn main() u8 { if (fast_exit) std.process.exit(1); return 1; }, + else => |err| return err, }; if (fast_exit) std.process.exit(@intFromBool(comp.diag.errors != 0)); return @intFromBool(comp.diag.errors != 0); diff --git a/src/target.zig b/src/target.zig index 45b0c91f..56f74cdb 100644 --- a/src/target.zig +++ b/src/target.zig @@ -376,6 +376,42 @@ pub fn defaultFpEvalMethod(target: std.Target) LangOpts.FPEvalMethod { return .source; } +/// Value of the `-m` flag for `ld` for this target +pub fn ldEmulationOption(target: std.Target, arm_endianness: ?std.builtin.Endian) ?[]const u8 { + return switch (target.cpu.arch) { + .x86 => if (target.os.tag == .elfiamcu) "elf_iamcu" else "elf_i386", + .arm, + .armeb, + .thumb, + .thumbeb, + => switch (arm_endianness orelse target.cpu.arch.endian()) { + .Little => "armelf_linux_eabi", + .Big => "armelfb_linux_eabi", + }, + .aarch64 => "aarch64linux", + .aarch64_be => "aarch64linuxb", + .m68k => "m68kelf", + .powerpc => if (target.os.tag == .linux) "elf32ppclinux" else "elf32ppc", + .powerpcle => if (target.os.tag == .linux) "elf32lppclinux" else "elf32lppc", + .powerpc64 => "elf64ppc", + .powerpc64le => "elf64lppc", + .riscv32 => "elf32lriscv", + .riscv64 => "elf64lriscv", + .sparc, .sparcel => "elf32_sparc", + .sparc64 => "elf64_sparc", + .loongarch32 => "elf32loongarch", + .loongarch64 => "elf64loongarch", + .mips => "elf32btsmip", + .mipsel => "elf32ltsmip", + .mips64 => if (target.abi == .gnuabin32) "elf32btsmipn32" else "elf64btsmip", + .mips64el => if (target.abi == .gnuabin32) "elf32ltsmipn32" else "elf64ltsmip", + .x86_64 => if (target.abi == .gnux32 or target.abi == .muslx32) "elf32_x86_64" else "elf_x86_64", + .ve => "elf64ve", + .csky => "cskyelf_linux", + else => null, + }; +} + test "alignment functions - smoke test" { var target: std.Target = undefined; const x86 = std.Target.Cpu.Arch.x86_64; diff --git a/src/toolchains/Linux.zig b/src/toolchains/Linux.zig new file mode 100644 index 00000000..6c12212a --- /dev/null +++ b/src/toolchains/Linux.zig @@ -0,0 +1,262 @@ +const std = @import("std"); +const mem = std.mem; +const Compilation = @import("../Compilation.zig"); +const Toolchain = @import("../Toolchain.zig"); +const Driver = @import("../Driver.zig"); +const Distro = @import("../Distro.zig"); +const target_util = @import("../target.zig"); + +const Linux = @This(); + +distro: Distro.Tag = .unknown, +extra_opts: std.ArrayListUnmanaged([]const u8) = .{}, + +pub fn discover(self: *Linux, tc: *Toolchain) !void { + self.distro = Distro.detect(tc.getTarget()); + try self.buildExtraOpts(tc); + try self.findPaths(tc); +} + +fn buildExtraOpts(self: *Linux, tc: *Toolchain) !void { + const gpa = tc.driver.comp.gpa; + const target = tc.getTarget(); + const is_android = target.isAndroid(); + if (self.distro.isAlpine() or is_android) { + try self.extra_opts.ensureUnusedCapacity(gpa, 2); + self.extra_opts.appendAssumeCapacity("-z"); + self.extra_opts.appendAssumeCapacity("now"); + } + + if (self.distro.isOpenSUSE() or self.distro.isUbuntu() or self.distro.isAlpine() or is_android) { + try self.extra_opts.ensureUnusedCapacity(gpa, 2); + self.extra_opts.appendAssumeCapacity("-z"); + self.extra_opts.appendAssumeCapacity("relro"); + } + + if (target.cpu.arch.isARM() or target.cpu.arch.isAARCH64() or is_android) { + try self.extra_opts.ensureUnusedCapacity(gpa, 2); + self.extra_opts.appendAssumeCapacity("-z"); + self.extra_opts.appendAssumeCapacity("max-page-size=4096"); + } + + if (target.cpu.arch == .arm or target.cpu.arch == .thumb) { + try self.extra_opts.append(gpa, "-X"); + } + + if (!target.cpu.arch.isMIPS() and target.cpu.arch != .hexagon) { + const hash_style = if (is_android) .both else self.distro.getHashStyle(); + try self.extra_opts.append(gpa, switch (hash_style) { + inline else => |tag| "--hash-style=" ++ @tagName(tag), + }); + } +} + +/// TODO: Very incomplete +fn findPaths(self: *Linux, tc: *Toolchain) !void { + _ = self; + const target = tc.getTarget(); + const sysroot = tc.driver.sysroot orelse ""; + + const os_lib_dir = getOSLibDir(target); + const multiarch_triple = getMultiarchTriple(target); + + try tc.addPathIfExists(&.{ sysroot, "/lib", multiarch_triple }, .file); + try tc.addPathIfExists(&.{ sysroot, "/lib/..", os_lib_dir }, .file); + try tc.addPathIfExists(&.{ sysroot, "/usr/lib", multiarch_triple }, .file); + try tc.addPathIfExists(&.{ sysroot, "/usr/lib/..", os_lib_dir }, .file); + + try tc.addPathIfExists(&.{ sysroot, "/lib" }, .file); + try tc.addPathIfExists(&.{ sysroot, "/usr/lib" }, .file); +} + +pub fn deinit(self: *Linux, allocator: std.mem.Allocator) void { + self.extra_opts.deinit(allocator); +} + +fn isPIEDefault(self: *const Linux) bool { + _ = self; + return false; +} + +fn getPIE(self: *const Linux, d: *const Driver) bool { + if (d.shared or d.static or d.relocatable or d.static_pie) { + return false; + } + return d.pie orelse self.isPIEDefault(); +} + +fn getStaticPIE(self: *const Linux, d: *Driver) !bool { + _ = self; + if (d.static_pie and d.pie != null) { + try d.err("cannot specify 'nopie' along with 'static-pie'"); + } + return d.static_pie; +} + +fn getStatic(self: *const Linux, d: *const Driver) bool { + _ = self; + return d.static and !d.static_pie; +} + +pub fn buildLinkerArgs(self: *const Linux, tc: *const Toolchain, argv: *std.ArrayList([]const u8)) Compilation.Error!void { + const d = tc.driver; + + const is_pie = self.getPIE(d); + const is_static_pie = try self.getStaticPIE(d); + const is_static = self.getStatic(d); + const is_android = d.comp.target.isAndroid(); + const is_iamcu = d.comp.target.os.tag == .elfiamcu; + + if (is_pie) { + try argv.append("-pie"); + } + if (is_static_pie) { + try argv.ensureUnusedCapacity(5); + argv.appendAssumeCapacity("-static"); + argv.appendAssumeCapacity("-pie"); + argv.appendAssumeCapacity("--no-dynamic-linker"); + argv.appendAssumeCapacity("-z"); + argv.appendAssumeCapacity("text"); + } + + if (d.rdynamic) { + try argv.append("-export-dynamic"); + } + + if (d.strip) { + try argv.append("-s"); + } + + try argv.ensureUnusedCapacity(self.extra_opts.items.len); + argv.appendSliceAssumeCapacity(self.extra_opts.items); + + try argv.append("--eh-frame-hdr"); + + // Todo: Driver should parse `-EL`/`-EB` for arm to set endianness for arm targets + if (target_util.ldEmulationOption(d.comp.target, null)) |emulation| { + try argv.ensureUnusedCapacity(2); + argv.appendAssumeCapacity("-m"); + argv.appendAssumeCapacity(emulation); + } else { + try d.err("Unknown target triple"); + return; + } + if (d.comp.target.cpu.arch.isRISCV()) { + try argv.append("-X"); + } + if (d.shared) { + try argv.append("-shared"); + } + if (is_static) { + try argv.append("-static"); + } else { + if (d.rdynamic) { + try argv.append("-export-dynamic"); + } + if (!d.shared and !is_static_pie and !d.relocatable) { + const dynamic_linker = d.comp.target.standardDynamicLinkerPath(); + // todo: check for --dyld-prefix + if (dynamic_linker.get()) |path| { + try argv.ensureUnusedCapacity(2); + argv.appendAssumeCapacity("-dynamic-linker"); + argv.appendAssumeCapacity(try tc.arena.dupe(u8, path)); + } else { + try d.err("Could not find dynamic linker path"); + } + } + } + + try argv.ensureUnusedCapacity(2); + argv.appendAssumeCapacity("-o"); + argv.appendAssumeCapacity(d.output_name orelse "a.out"); + + if (!d.nostdlib and !d.nostartfiles and !d.relocatable) { + if (!is_android and !is_iamcu) { + if (!d.shared) { + const crt1 = if (is_pie) + "Scrt1.o" + else if (is_static_pie) + "rcrt1.o" + else + "crt1.o"; + try argv.append(try tc.getFilePath(crt1)); + } + try argv.append(try tc.getFilePath("crti.o")); + } + } + + // TODO add -L opts + // TODO add -u opts + // TODO add filepath lib args + // TODO handle LTO + + try argv.appendSlice(d.link_objects.items); + + if (!d.nostdlib and !d.relocatable) { + if (!d.nodefaultlibs) { + if (is_static or is_static_pie) { + try argv.append("--start-group"); + } + // TODO: add pthread if needed + if (!d.nolibc) { + try argv.append("-lc"); + } + if (is_iamcu) { + try argv.append("-lgloss"); + } + if (is_static or is_static_pie) { + try argv.append("--end-group"); + } else {} + if (is_iamcu) { + try argv.ensureUnusedCapacity(3); + argv.appendAssumeCapacity("--as-needed"); + argv.appendAssumeCapacity("-lsoftfp"); + argv.appendAssumeCapacity("--no-as-needed"); + } + } + if (!d.nostartfiles and !is_iamcu) { + // TODO: handle CRT begin/end files + if (!is_android) { + try argv.append(try tc.getFilePath("crtn.o")); + } + } + } + + // TODO add -T args +} + +fn getMultiarchTriple(target: std.Target) []const u8 { + const is_android = target.isAndroid(); + + return switch (target.cpu.arch) { + .aarch64 => if (is_android) "aarch64-linux-android" else "aarch64-linux-gnu", + .aarch64_be => "aarch64_be-linux-gnu", + .x86 => if (is_android) "i686-linux-android" else "i386-linux-gnu", + .x86_64 => if (is_android) "x86_64-linux-android" else if (target.abi == .gnux32) "x86_64-linux-gnux32" else "x86_64-linux-gnu", + + // TODO: expand this + else => "", + }; +} + +fn getOSLibDir(target: std.Target) []const u8 { + switch (target.cpu.arch) { + .x86, + .powerpc, + .powerpcle, + .sparc, + .sparcel, + => return "lib32", + else => {}, + } + if (target.cpu.arch == .x86_64 and (target.abi == .gnux32 or target.abi == .muslx32)) { + return "libx32"; + } + if (target.cpu.arch == .riscv32) { + return "lib32"; + } + if (target.ptrBitWidth() == 32) { + return "lib"; + } + return "lib64"; +} diff --git a/src/util.zig b/src/util.zig index aa78188c..7de69d5e 100644 --- a/src/util.zig +++ b/src/util.zig @@ -79,3 +79,14 @@ pub fn errorDescription(err: anyerror) []const u8 { else => @errorName(err), }; } + +pub fn canExecute(path: []const u8) bool { + std.os.access(path, std.os.X_OK) catch return false; + // Todo: ensure path is not a directory + return true; +} + +pub fn exists(path: []const u8) bool { + std.os.access(path, std.os.F_OK) catch return false; + return true; +} From 75bd4a1b38444f0f03be8abeac6b39e122b5e1dd Mon Sep 17 00:00:00 2001 From: Evan Haas Date: Wed, 12 Jul 2023 10:21:57 -0700 Subject: [PATCH 04/50] Toolchain: Add an option for passing --build-id to linker --- build.zig | 2 ++ src/toolchains/Linux.zig | 5 +++++ 2 files changed, 7 insertions(+) diff --git a/build.zig b/build.zig index 1cafa570..25c462a9 100644 --- a/build.zig +++ b/build.zig @@ -40,6 +40,7 @@ pub fn build(b: *Build) !void { // between Debug, ReleaseSafe, ReleaseFast, and ReleaseSmall. const mode = b.standardOptimizeOption(.{}); + const enable_linker_build_id = b.option(bool, "enable-linker-build-id", "pass --build-id to linker") orelse false; const default_linker = b.option([]const u8, "default-linker", "Default linker aro will use if none is supplied via -fuse-ld") orelse "ld"; const test_all_allocation_failures = b.option(bool, "test-all-allocation-failures", "Test all allocation failures") orelse false; const link_libc = b.option(bool, "link-libc", "Force self-hosted compiler to link libc") orelse (mode != .Debug); @@ -66,6 +67,7 @@ pub fn build(b: *Build) !void { }); const exe_options = b.addOptions(); exe.addOptions("build_options", exe_options); + exe_options.addOption(bool, "enable_linker_build_id", enable_linker_build_id); exe_options.addOption([]const u8, "default_linker", default_linker); exe_options.addOption(bool, "enable_tracy", tracy != null); exe_options.addOption(bool, "enable_tracy_callstack", tracy_callstack); diff --git a/src/toolchains/Linux.zig b/src/toolchains/Linux.zig index 6c12212a..95c2e9d2 100644 --- a/src/toolchains/Linux.zig +++ b/src/toolchains/Linux.zig @@ -5,6 +5,7 @@ const Toolchain = @import("../Toolchain.zig"); const Driver = @import("../Driver.zig"); const Distro = @import("../Distro.zig"); const target_util = @import("../target.zig"); +const build_options = @import("build_options"); const Linux = @This(); @@ -49,6 +50,10 @@ fn buildExtraOpts(self: *Linux, tc: *Toolchain) !void { inline else => |tag| "--hash-style=" ++ @tagName(tag), }); } + + if (build_options.enable_linker_build_id) { + try self.extra_opts.append(gpa, "--build-id"); + } } /// TODO: Very incomplete From 6d67f842bb0166969384c2384cf1a14fbf987eaf Mon Sep 17 00:00:00 2001 From: Evan Haas Date: Wed, 12 Jul 2023 10:34:00 -0700 Subject: [PATCH 05/50] Driver: store raw target triple --- src/Driver.zig | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Driver.zig b/src/Driver.zig index 2bc2d83d..3a0be6cf 100644 --- a/src/Driver.zig +++ b/src/Driver.zig @@ -42,6 +42,9 @@ aro_name: []const u8 = "arocc", /// Directory from which aro was invoked aro_dir: []const u8 = "", +/// Value of --triple= passed via CLI +raw_target_triple: ?[]const u8 = null, + // linker options use_linker: ?[]const u8 = null, linker_path: ?[]const u8 = null, @@ -328,6 +331,7 @@ pub fn parseArgs( }; d.comp.target = cross.toTarget(); // TODO deprecated d.comp.langopts.setEmulatedCompiler(target_util.systemCompiler(d.comp.target)); + d.raw_target_triple = triple; } else if (mem.eql(u8, arg, "--verbose-ast")) { d.verbose_ast = true; } else if (mem.eql(u8, arg, "--verbose-pp")) { From 4af282e11f927a13d591740e87c3b002df7a46e7 Mon Sep 17 00:00:00 2001 From: Evan Haas Date: Wed, 12 Jul 2023 11:23:59 -0700 Subject: [PATCH 06/50] Toolchain: search program paths for programs --- src/Toolchain.zig | 37 ++++++++++++++++++++++++++++++++++--- 1 file changed, 34 insertions(+), 3 deletions(-) diff --git a/src/Toolchain.zig b/src/Toolchain.zig index ad1d1b82..67321cc5 100644 --- a/src/Toolchain.zig +++ b/src/Toolchain.zig @@ -159,9 +159,40 @@ pub fn getLinkerPath(self: *const Toolchain, buf: []u8) ![]const u8 { return self.getProgramPath(default_linker, buf); } -fn getProgramPath(toolchain: *const Toolchain, name: []const u8, buf: []u8) []const u8 { - _ = toolchain; - _ = buf; +const TargetSpecificToolName = std.BoundedArray(u8, 64); + +fn possibleProgramNames(raw_triple: ?[]const u8, name: []const u8, target_specific: *TargetSpecificToolName) std.BoundedArray([]const u8, 2) { + var possible_names = std.BoundedArray([]const u8, 2).init(0) catch unreachable; + if (raw_triple) |triple| { + const w = target_specific.writer(); + if (w.print("{s}-{s}", .{ triple, name })) { + possible_names.appendAssumeCapacity(target_specific.slice()); + } else |_| {} + } + possible_names.appendAssumeCapacity(name); + + return possible_names; +} + +fn getProgramPath(tc: *const Toolchain, name: []const u8, buf: []u8) []const u8 { + var stack_fb = std.heap.stackFallback(PathStackSize, tc.driver.comp.gpa); + var allocator = stack_fb.get(); + + var tool_specific_name = TargetSpecificToolName.init(0) catch unreachable; + const possible_names = possibleProgramNames(tc.driver.raw_target_triple, name, &tool_specific_name); + + for (possible_names.slice()) |tool_name| { + for (tc.program_paths.items) |program_path| { + const candidate = std.fs.path.join(allocator, &.{ program_path, tool_name }) catch continue; + defer allocator.free(candidate); + + if (util.canExecute(candidate) and candidate.len <= buf.len) { + @memcpy(buf[0..candidate.len], candidate); + return buf[0..candidate.len]; + } + } + // todo: check $PATH + } return name; } From 3ff47ca7f350af3cf7a72aa596e739b93771caef Mon Sep 17 00:00:00 2001 From: Evan Haas Date: Wed, 12 Jul 2023 11:34:43 -0700 Subject: [PATCH 07/50] Toolchain: use a FixedBufferAllocator for path testing --- src/Toolchain.zig | 44 +++++++++++++++++++------------------------- 1 file changed, 19 insertions(+), 25 deletions(-) diff --git a/src/Toolchain.zig b/src/Toolchain.zig index 67321cc5..d13e8028 100644 --- a/src/Toolchain.zig +++ b/src/Toolchain.zig @@ -175,16 +175,17 @@ fn possibleProgramNames(raw_triple: ?[]const u8, name: []const u8, target_specif } fn getProgramPath(tc: *const Toolchain, name: []const u8, buf: []u8) []const u8 { - var stack_fb = std.heap.stackFallback(PathStackSize, tc.driver.comp.gpa); - var allocator = stack_fb.get(); + var path_buf: [std.fs.MAX_PATH_BYTES]u8 = undefined; + var fib = std.heap.FixedBufferAllocator.init(&path_buf); var tool_specific_name = TargetSpecificToolName.init(0) catch unreachable; const possible_names = possibleProgramNames(tc.driver.raw_target_triple, name, &tool_specific_name); for (possible_names.slice()) |tool_name| { for (tc.program_paths.items) |program_path| { - const candidate = std.fs.path.join(allocator, &.{ program_path, tool_name }) catch continue; - defer allocator.free(candidate); + defer fib.reset(); + + const candidate = std.fs.path.join(fib.allocator(), &.{ program_path, tool_name }) catch continue; if (util.canExecute(candidate) and candidate.len <= buf.len) { @memcpy(buf[0..candidate.len], candidate); @@ -196,31 +197,27 @@ fn getProgramPath(tc: *const Toolchain, name: []const u8, buf: []u8) []const u8 return name; } -const PathStackSize = 128; -const PathAllocator = std.heap.StackFallbackAllocator(PathStackSize); - pub fn getFilePath(toolchain: *const Toolchain, name: []const u8) ![]const u8 { + var path_buf: [std.fs.MAX_PATH_BYTES]u8 = undefined; + var fib = std.heap.FixedBufferAllocator.init(&path_buf); + const allocator = fib.allocator(); const d = toolchain.driver; - var stack_fb = std.heap.stackFallback(PathStackSize, d.comp.gpa); - var allocator = stack_fb.get(); - // todo check resource dir // todo check compiler RT path const candidate = try std.fs.path.join(allocator, &.{ d.aro_dir, "..", name }); - defer allocator.free(candidate); if (util.exists(candidate)) { return toolchain.arena.dupe(u8, candidate); } - if (try searchPaths(&stack_fb, toolchain.library_paths.items, name)) |path| { - defer allocator.free(path); + fib.reset(); + if (try searchPaths(allocator, toolchain.library_paths.items, name)) |path| { return toolchain.arena.dupe(u8, path); } - if (try searchPaths(&stack_fb, toolchain.file_paths.items, name)) |path| { - defer allocator.free(path); + fib.reset(); + if (try searchPaths(allocator, toolchain.file_paths.items, name)) |path| { return try toolchain.arena.dupe(u8, path); } @@ -228,18 +225,14 @@ pub fn getFilePath(toolchain: *const Toolchain, name: []const u8) ![]const u8 { } /// find path -fn searchPaths(path_allocator: *PathAllocator, paths: []const []const u8, name: []const u8) !?[]const u8 { +fn searchPaths(allocator: mem.Allocator, paths: []const []const u8, name: []const u8) !?[]const u8 { for (paths) |path| { if (path.len == 0) continue; - const allocator = path_allocator.get(); // resets underlying fixed buffer const candidate = try std.fs.path.join(allocator, &.{ path, name }); - if (util.exists(candidate)) { - const duped = try path_allocator.fallback_allocator.dupe(u8, candidate); - return duped; + return candidate; } - allocator.free(candidate); } return null; } @@ -251,10 +244,11 @@ const PathKind = enum { }; pub fn addPathIfExists(self: *Toolchain, components: []const []const u8, dest_kind: PathKind) !void { - var stack_fb = std.heap.stackFallback(PathStackSize, self.driver.comp.gpa); - var allocator = stack_fb.get(); - const candidate = try std.fs.path.join(allocator, components); - defer allocator.free(candidate); + var path_buf: [std.fs.MAX_PATH_BYTES]u8 = undefined; + var fib = std.heap.FixedBufferAllocator.init(&path_buf); + + const candidate = try std.fs.path.join(fib.allocator(), components); + if (util.exists(candidate)) { const gpa = self.driver.comp.gpa; const duped = try gpa.dupe(u8, candidate); From 9ee156eb37cbc9a02c7aff96118b5a695e15dbb0 Mon Sep 17 00:00:00 2001 From: Evan Haas Date: Wed, 12 Jul 2023 11:37:03 -0700 Subject: [PATCH 08/50] Toolchain: skip too-long search paths instead of erroring --- src/Toolchain.zig | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Toolchain.zig b/src/Toolchain.zig index d13e8028..66bcf8af 100644 --- a/src/Toolchain.zig +++ b/src/Toolchain.zig @@ -212,12 +212,12 @@ pub fn getFilePath(toolchain: *const Toolchain, name: []const u8) ![]const u8 { } fib.reset(); - if (try searchPaths(allocator, toolchain.library_paths.items, name)) |path| { + if (searchPaths(allocator, toolchain.library_paths.items, name)) |path| { return toolchain.arena.dupe(u8, path); } fib.reset(); - if (try searchPaths(allocator, toolchain.file_paths.items, name)) |path| { + if (searchPaths(allocator, toolchain.file_paths.items, name)) |path| { return try toolchain.arena.dupe(u8, path); } @@ -225,11 +225,11 @@ pub fn getFilePath(toolchain: *const Toolchain, name: []const u8) ![]const u8 { } /// find path -fn searchPaths(allocator: mem.Allocator, paths: []const []const u8, name: []const u8) !?[]const u8 { +fn searchPaths(allocator: mem.Allocator, paths: []const []const u8, name: []const u8) ?[]const u8 { for (paths) |path| { if (path.len == 0) continue; - const candidate = try std.fs.path.join(allocator, &.{ path, name }); + const candidate = std.fs.path.join(allocator, &.{ path, name }) catch continue; if (util.exists(candidate)) { return candidate; } From f9b8e7dec03e4d296c1fd8f54fa3bf2f24576932 Mon Sep 17 00:00:00 2001 From: Evan Haas Date: Wed, 12 Jul 2023 11:42:08 -0700 Subject: [PATCH 09/50] Toolchain: improve doc comment for searchPaths --- src/Toolchain.zig | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/Toolchain.zig b/src/Toolchain.zig index 66bcf8af..7dea09bc 100644 --- a/src/Toolchain.zig +++ b/src/Toolchain.zig @@ -224,12 +224,13 @@ pub fn getFilePath(toolchain: *const Toolchain, name: []const u8) ![]const u8 { return name; } -/// find path -fn searchPaths(allocator: mem.Allocator, paths: []const []const u8, name: []const u8) ?[]const u8 { - for (paths) |path| { +/// Search a list of `path_prefixes` for the existence `name` +/// Assumes that `fba` is a fixed-buffer allocator, so does not free joined path candidates +fn searchPaths(fba: mem.Allocator, path_prefixes: []const []const u8, name: []const u8) ?[]const u8 { + for (path_prefixes) |path| { if (path.len == 0) continue; - const candidate = std.fs.path.join(allocator, &.{ path, name }) catch continue; + const candidate = std.fs.path.join(fba, &.{ path, name }) catch continue; if (util.exists(candidate)) { return candidate; } From 10aa61e55acfe143f4b7d49f549828632d424b84 Mon Sep 17 00:00:00 2001 From: Evan Haas Date: Wed, 12 Jul 2023 11:45:44 -0700 Subject: [PATCH 10/50] Driver: make default sysroot an aro build setting --- build.zig | 2 ++ src/Driver.zig | 3 ++- src/toolchains/Linux.zig | 2 +- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/build.zig b/build.zig index 25c462a9..60cb24d6 100644 --- a/build.zig +++ b/build.zig @@ -42,6 +42,7 @@ pub fn build(b: *Build) !void { const enable_linker_build_id = b.option(bool, "enable-linker-build-id", "pass --build-id to linker") orelse false; const default_linker = b.option([]const u8, "default-linker", "Default linker aro will use if none is supplied via -fuse-ld") orelse "ld"; + const default_sysroot = b.option([]const u8, "default-sysroot", "Default to all compiler invocations for --sysroot=.") orelse ""; const test_all_allocation_failures = b.option(bool, "test-all-allocation-failures", "Test all allocation failures") orelse false; const link_libc = b.option(bool, "link-libc", "Force self-hosted compiler to link libc") orelse (mode != .Debug); const tracy = b.option([]const u8, "tracy", "Enable Tracy integration. Supply path to Tracy source"); @@ -69,6 +70,7 @@ pub fn build(b: *Build) !void { exe.addOptions("build_options", exe_options); exe_options.addOption(bool, "enable_linker_build_id", enable_linker_build_id); exe_options.addOption([]const u8, "default_linker", default_linker); + exe_options.addOption([]const u8, "default_sysroot", default_sysroot); exe_options.addOption(bool, "enable_tracy", tracy != null); exe_options.addOption(bool, "enable_tracy_callstack", tracy_callstack); exe_options.addOption(bool, "enable_tracy_allocation", tracy_allocation); diff --git a/src/Driver.zig b/src/Driver.zig index 3a0be6cf..4473e28a 100644 --- a/src/Driver.zig +++ b/src/Driver.zig @@ -11,6 +11,7 @@ const Source = @import("Source.zig"); const Toolchain = @import("Toolchain.zig"); const util = @import("util.zig"); const target_util = @import("target.zig"); +const build_options = @import("build_options"); const Driver = @This(); @@ -26,7 +27,7 @@ comp: *Compilation, inputs: std.ArrayListUnmanaged(Source) = .{}, link_objects: std.ArrayListUnmanaged([]const u8) = .{}, output_name: ?[]const u8 = null, -sysroot: ?[]const u8 = null, +sysroot: []const u8 = build_options.default_sysroot, temp_file_count: u32 = 0, only_preprocess: bool = false, only_syntax: bool = false, diff --git a/src/toolchains/Linux.zig b/src/toolchains/Linux.zig index 95c2e9d2..0e041c7b 100644 --- a/src/toolchains/Linux.zig +++ b/src/toolchains/Linux.zig @@ -60,7 +60,7 @@ fn buildExtraOpts(self: *Linux, tc: *Toolchain) !void { fn findPaths(self: *Linux, tc: *Toolchain) !void { _ = self; const target = tc.getTarget(); - const sysroot = tc.driver.sysroot orelse ""; + const sysroot = tc.driver.sysroot; const os_lib_dir = getOSLibDir(target); const multiarch_triple = getMultiarchTriple(target); From 1adc734cc0d0a2222a90f3d76fc064afdda7ae95 Mon Sep 17 00:00:00 2001 From: Evan Haas Date: Wed, 12 Jul 2023 11:54:32 -0700 Subject: [PATCH 11/50] Toolchain: Add sysroot support for path searching --- src/Toolchain.zig | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/src/Toolchain.zig b/src/Toolchain.zig index 7dea09bc..87e195ff 100644 --- a/src/Toolchain.zig +++ b/src/Toolchain.zig @@ -197,28 +197,27 @@ fn getProgramPath(tc: *const Toolchain, name: []const u8, buf: []u8) []const u8 return name; } -pub fn getFilePath(toolchain: *const Toolchain, name: []const u8) ![]const u8 { +pub fn getFilePath(tc: *const Toolchain, name: []const u8) ![]const u8 { var path_buf: [std.fs.MAX_PATH_BYTES]u8 = undefined; var fib = std.heap.FixedBufferAllocator.init(&path_buf); const allocator = fib.allocator(); - const d = toolchain.driver; // todo check resource dir // todo check compiler RT path - const candidate = try std.fs.path.join(allocator, &.{ d.aro_dir, "..", name }); + const candidate = try std.fs.path.join(allocator, &.{ tc.driver.aro_dir, "..", name }); if (util.exists(candidate)) { - return toolchain.arena.dupe(u8, candidate); + return tc.arena.dupe(u8, candidate); } fib.reset(); - if (searchPaths(allocator, toolchain.library_paths.items, name)) |path| { - return toolchain.arena.dupe(u8, path); + if (searchPaths(allocator, tc.driver.sysroot, tc.library_paths.items, name)) |path| { + return tc.arena.dupe(u8, path); } fib.reset(); - if (searchPaths(allocator, toolchain.file_paths.items, name)) |path| { - return try toolchain.arena.dupe(u8, path); + if (searchPaths(allocator, tc.driver.sysroot, tc.file_paths.items, name)) |path| { + return try tc.arena.dupe(u8, path); } return name; @@ -226,11 +225,15 @@ pub fn getFilePath(toolchain: *const Toolchain, name: []const u8) ![]const u8 { /// Search a list of `path_prefixes` for the existence `name` /// Assumes that `fba` is a fixed-buffer allocator, so does not free joined path candidates -fn searchPaths(fba: mem.Allocator, path_prefixes: []const []const u8, name: []const u8) ?[]const u8 { +fn searchPaths(fba: mem.Allocator, sysroot: []const u8, path_prefixes: []const []const u8, name: []const u8) ?[]const u8 { for (path_prefixes) |path| { if (path.len == 0) continue; - const candidate = std.fs.path.join(fba, &.{ path, name }) catch continue; + const candidate = if (path[0] == '=') + std.fs.path.join(fba, &.{ sysroot, path[1..], name }) catch continue + else + std.fs.path.join(fba, &.{ path, name }) catch continue; + if (util.exists(candidate)) { return candidate; } From dadcce01b4e8f9e02082fb69a4eaaccc9c88bcb9 Mon Sep 17 00:00:00 2001 From: Evan Haas Date: Wed, 12 Jul 2023 12:13:45 -0700 Subject: [PATCH 12/50] Toolchain: add some doc comments --- src/Toolchain.zig | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/Toolchain.zig b/src/Toolchain.zig index 87e195ff..6953ee5c 100644 --- a/src/Toolchain.zig +++ b/src/Toolchain.zig @@ -161,6 +161,10 @@ pub fn getLinkerPath(self: *const Toolchain, buf: []u8) ![]const u8 { const TargetSpecificToolName = std.BoundedArray(u8, 64); +/// If an explicit target is provided, also check the prefixed tool-specific name +/// TODO: this isn't exactly right since our target names don't necessarily match up +/// with GCC's. +/// For example the Zig target `arm-freestanding-eabi` would need the `arm-none-eabi` tools fn possibleProgramNames(raw_triple: ?[]const u8, name: []const u8, target_specific: *TargetSpecificToolName) std.BoundedArray([]const u8, 2) { var possible_names = std.BoundedArray([]const u8, 2).init(0) catch unreachable; if (raw_triple) |triple| { @@ -174,6 +178,9 @@ fn possibleProgramNames(raw_triple: ?[]const u8, name: []const u8, target_specif return possible_names; } +/// Search for an executable called `name` or `{triple}-{name} in program_paths and the $PATH environment variable +/// If not found there, just use `name` +/// Writes the result to `buf` and returns a slice of it fn getProgramPath(tc: *const Toolchain, name: []const u8, buf: []u8) []const u8 { var path_buf: [std.fs.MAX_PATH_BYTES]u8 = undefined; var fib = std.heap.FixedBufferAllocator.init(&path_buf); @@ -194,9 +201,12 @@ fn getProgramPath(tc: *const Toolchain, name: []const u8, buf: []u8) []const u8 } // todo: check $PATH } - return name; + @memcpy(buf[0..name.len], name); + return buf[0..name.len]; } +/// Search for `name` in a variety of places +/// TODO: cache results based on `name` so we're not repeatedly allocating the same strings? pub fn getFilePath(tc: *const Toolchain, name: []const u8) ![]const u8 { var path_buf: [std.fs.MAX_PATH_BYTES]u8 = undefined; var fib = std.heap.FixedBufferAllocator.init(&path_buf); @@ -247,6 +257,7 @@ const PathKind = enum { program, }; +/// Join `components` into a path. If the path exists, add it to the specified path list. pub fn addPathIfExists(self: *Toolchain, components: []const []const u8, dest_kind: PathKind) !void { var path_buf: [std.fs.MAX_PATH_BYTES]u8 = undefined; var fib = std.heap.FixedBufferAllocator.init(&path_buf); From 36f938556de4b9f36f3db964e2b963a93baee2b1 Mon Sep 17 00:00:00 2001 From: Evan Haas Date: Wed, 12 Jul 2023 12:16:47 -0700 Subject: [PATCH 13/50] Toolchain: panic if we try to link when we shouldn't --- src/Toolchain.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Toolchain.zig b/src/Toolchain.zig index 6953ee5c..48d28d7e 100644 --- a/src/Toolchain.zig +++ b/src/Toolchain.zig @@ -280,6 +280,6 @@ pub fn addPathIfExists(self: *Toolchain, components: []const []const u8, dest_ki pub fn buildLinkerArgs(self: *Toolchain, argv: *std.ArrayList([]const u8)) !void { return switch (self.inner) { .linux => |*linux| linux.buildLinkerArgs(self, argv), - .unknown => {}, + .unknown => @panic("This toolchain does not support linking yet"), }; } From 38d7fc148b4bd5cd503d812a50ab0396800b35bc Mon Sep 17 00:00:00 2001 From: Evan Haas Date: Wed, 12 Jul 2023 12:26:33 -0700 Subject: [PATCH 14/50] Toolchain: use target-specific default linker --- src/Toolchain.zig | 6 ++++-- src/toolchains/Linux.zig | 8 ++++++++ 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/src/Toolchain.zig b/src/Toolchain.zig index 48d28d7e..425fefe2 100644 --- a/src/Toolchain.zig +++ b/src/Toolchain.zig @@ -41,8 +41,10 @@ pub fn getTarget(self: *const Toolchain) std.Target { } fn getDefaultLinker(self: *const Toolchain) []const u8 { - _ = self; - return "ld"; + return switch (self.inner) { + .linux => |linux| linux.getDefaultLinker(self.getTarget()), + .unknown => "ld", + }; } /// Call this after driver has finished parsing command line arguments to find the toolchain diff --git a/src/toolchains/Linux.zig b/src/toolchains/Linux.zig index 0e041c7b..0488cc42 100644 --- a/src/toolchains/Linux.zig +++ b/src/toolchains/Linux.zig @@ -103,6 +103,14 @@ fn getStatic(self: *const Linux, d: *const Driver) bool { return d.static and !d.static_pie; } +pub fn getDefaultLinker(self: *const Linux, target: std.Target) []const u8 { + _ = self; + if (target.isAndroid()) { + return "ld.lld"; + } + return "ld"; +} + pub fn buildLinkerArgs(self: *const Linux, tc: *const Toolchain, argv: *std.ArrayList([]const u8)) Compilation.Error!void { const d = tc.driver; From a63c731c864253ed51879aa430f898fd9a42e2f5 Mon Sep 17 00:00:00 2001 From: Evan Haas Date: Wed, 12 Jul 2023 15:33:30 -0700 Subject: [PATCH 15/50] Build: put system defaults into their own set of options --- build.zig | 12 +++++++++--- src/Driver.zig | 3 +-- src/Toolchain.zig | 14 ++++++++++---- src/toolchains/Linux.zig | 6 +++--- 4 files changed, 23 insertions(+), 12 deletions(-) diff --git a/build.zig b/build.zig index 60cb24d6..488cebf0 100644 --- a/build.zig +++ b/build.zig @@ -68,9 +68,14 @@ pub fn build(b: *Build) !void { }); const exe_options = b.addOptions(); exe.addOptions("build_options", exe_options); - exe_options.addOption(bool, "enable_linker_build_id", enable_linker_build_id); - exe_options.addOption([]const u8, "default_linker", default_linker); - exe_options.addOption([]const u8, "default_sysroot", default_sysroot); + + const system_defaults = b.addOptions(); + exe.addOptions("system_defaults", system_defaults); + + system_defaults.addOption(bool, "enable_linker_build_id", enable_linker_build_id); + system_defaults.addOption([]const u8, "linker", default_linker); + system_defaults.addOption([]const u8, "sysroot", default_sysroot); + exe_options.addOption(bool, "enable_tracy", tracy != null); exe_options.addOption(bool, "enable_tracy_callstack", tracy_callstack); exe_options.addOption(bool, "enable_tracy_allocation", tracy_allocation); @@ -117,6 +122,7 @@ pub fn build(b: *Build) !void { integration_tests.addModule("aro", aro_module); const test_runner_options = b.addOptions(); integration_tests.addOptions("build_options", test_runner_options); + integration_tests.addOptions("system_defaults", system_defaults); test_runner_options.addOption(bool, "test_all_allocation_failures", test_all_allocation_failures); const integration_test_runner = b.addRunArtifact(integration_tests); diff --git a/src/Driver.zig b/src/Driver.zig index 4473e28a..3a0be6cf 100644 --- a/src/Driver.zig +++ b/src/Driver.zig @@ -11,7 +11,6 @@ const Source = @import("Source.zig"); const Toolchain = @import("Toolchain.zig"); const util = @import("util.zig"); const target_util = @import("target.zig"); -const build_options = @import("build_options"); const Driver = @This(); @@ -27,7 +26,7 @@ comp: *Compilation, inputs: std.ArrayListUnmanaged(Source) = .{}, link_objects: std.ArrayListUnmanaged([]const u8) = .{}, output_name: ?[]const u8 = null, -sysroot: []const u8 = build_options.default_sysroot, +sysroot: ?[]const u8 = null, temp_file_count: u32 = 0, only_preprocess: bool = false, only_syntax: bool = false, diff --git a/src/Toolchain.zig b/src/Toolchain.zig index 425fefe2..14cd6f80 100644 --- a/src/Toolchain.zig +++ b/src/Toolchain.zig @@ -3,7 +3,7 @@ const Driver = @import("Driver.zig"); const Compilation = @import("Compilation.zig"); const util = @import("util.zig"); const mem = std.mem; -const build_options = @import("build_options"); +const system_defaults = @import("system_defaults"); const Linux = @import("toolchains/Linux.zig"); const Toolchain = @This(); @@ -98,7 +98,7 @@ pub fn getLinkerPath(self: *const Toolchain, buf: []u8) ![]const u8 { // contain a path component separator. // -fuse-ld=lld can be used with --ld-path= to indicate that the binary // that --ld-path= points to is lld. - const use_linker = self.driver.use_linker orelse build_options.default_linker; + const use_linker = self.driver.use_linker orelse system_defaults.linker; if (self.driver.linker_path) |ld_path| { var path = ld_path; @@ -207,6 +207,10 @@ fn getProgramPath(tc: *const Toolchain, name: []const u8, buf: []u8) []const u8 return buf[0..name.len]; } +pub fn getSysroot(tc: *const Toolchain) []const u8 { + return tc.driver.sysroot orelse system_defaults.sysroot; +} + /// Search for `name` in a variety of places /// TODO: cache results based on `name` so we're not repeatedly allocating the same strings? pub fn getFilePath(tc: *const Toolchain, name: []const u8) ![]const u8 { @@ -214,6 +218,8 @@ pub fn getFilePath(tc: *const Toolchain, name: []const u8) ![]const u8 { var fib = std.heap.FixedBufferAllocator.init(&path_buf); const allocator = fib.allocator(); + const sysroot = tc.getSysroot(); + // todo check resource dir // todo check compiler RT path @@ -223,12 +229,12 @@ pub fn getFilePath(tc: *const Toolchain, name: []const u8) ![]const u8 { } fib.reset(); - if (searchPaths(allocator, tc.driver.sysroot, tc.library_paths.items, name)) |path| { + if (searchPaths(allocator, sysroot, tc.library_paths.items, name)) |path| { return tc.arena.dupe(u8, path); } fib.reset(); - if (searchPaths(allocator, tc.driver.sysroot, tc.file_paths.items, name)) |path| { + if (searchPaths(allocator, sysroot, tc.file_paths.items, name)) |path| { return try tc.arena.dupe(u8, path); } diff --git a/src/toolchains/Linux.zig b/src/toolchains/Linux.zig index 0488cc42..9d49b13a 100644 --- a/src/toolchains/Linux.zig +++ b/src/toolchains/Linux.zig @@ -5,7 +5,7 @@ const Toolchain = @import("../Toolchain.zig"); const Driver = @import("../Driver.zig"); const Distro = @import("../Distro.zig"); const target_util = @import("../target.zig"); -const build_options = @import("build_options"); +const system_defaults = @import("system_defaults"); const Linux = @This(); @@ -51,7 +51,7 @@ fn buildExtraOpts(self: *Linux, tc: *Toolchain) !void { }); } - if (build_options.enable_linker_build_id) { + if (system_defaults.enable_linker_build_id) { try self.extra_opts.append(gpa, "--build-id"); } } @@ -60,7 +60,7 @@ fn buildExtraOpts(self: *Linux, tc: *Toolchain) !void { fn findPaths(self: *Linux, tc: *Toolchain) !void { _ = self; const target = tc.getTarget(); - const sysroot = tc.driver.sysroot; + const sysroot = tc.getSysroot(); const os_lib_dir = getOSLibDir(target); const multiarch_triple = getMultiarchTriple(target); From 9da2446314cbf2bff55116da269baf87ae2a409c Mon Sep 17 00:00:00 2001 From: Evan Haas Date: Wed, 12 Jul 2023 16:01:50 -0700 Subject: [PATCH 16/50] linux toolchain: make tc arg const --- src/toolchains/Linux.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/toolchains/Linux.zig b/src/toolchains/Linux.zig index 9d49b13a..aefef9fe 100644 --- a/src/toolchains/Linux.zig +++ b/src/toolchains/Linux.zig @@ -18,7 +18,7 @@ pub fn discover(self: *Linux, tc: *Toolchain) !void { try self.findPaths(tc); } -fn buildExtraOpts(self: *Linux, tc: *Toolchain) !void { +fn buildExtraOpts(self: *Linux, tc: *const Toolchain) !void { const gpa = tc.driver.comp.gpa; const target = tc.getTarget(); const is_android = target.isAndroid(); From f3490c8bee54e7439134a13beebb61f1d947b567 Mon Sep 17 00:00:00 2001 From: Evan Haas Date: Wed, 12 Jul 2023 16:02:09 -0700 Subject: [PATCH 17/50] Toolchain: put paths into the toolchain arena --- src/Toolchain.zig | 20 ++++---------------- 1 file changed, 4 insertions(+), 16 deletions(-) diff --git a/src/Toolchain.zig b/src/Toolchain.zig index 14cd6f80..b9393c35 100644 --- a/src/Toolchain.zig +++ b/src/Toolchain.zig @@ -75,19 +75,8 @@ pub fn deinit(self: *Toolchain) void { const gpa = self.driver.comp.gpa; self.inner.deinit(gpa); - for (self.library_paths.items) |item| { - gpa.free(item); - } self.library_paths.deinit(gpa); - - for (self.file_paths.items) |item| { - gpa.free(item); - } self.file_paths.deinit(gpa); - - for (self.program_paths.items) |item| { - gpa.free(item); - } self.program_paths.deinit(gpa); } @@ -265,7 +254,8 @@ const PathKind = enum { program, }; -/// Join `components` into a path. If the path exists, add it to the specified path list. +/// Join `components` into a path. If the path exists, dupe it into the toolchain arena and +/// add it to the specified path list. pub fn addPathIfExists(self: *Toolchain, components: []const []const u8, dest_kind: PathKind) !void { var path_buf: [std.fs.MAX_PATH_BYTES]u8 = undefined; var fib = std.heap.FixedBufferAllocator.init(&path_buf); @@ -273,15 +263,13 @@ pub fn addPathIfExists(self: *Toolchain, components: []const []const u8, dest_ki const candidate = try std.fs.path.join(fib.allocator(), components); if (util.exists(candidate)) { - const gpa = self.driver.comp.gpa; - const duped = try gpa.dupe(u8, candidate); - errdefer gpa.free(duped); + const duped = try self.arena.dupe(u8, candidate); const dest = switch (dest_kind) { .library => &self.library_paths, .file => &self.file_paths, .program => &self.program_paths, }; - try dest.append(gpa, duped); + try dest.append(self.driver.comp.gpa, duped); } } From d35d66971f638c650a5baf7c0a8916b3566664c9 Mon Sep 17 00:00:00 2001 From: Evan Haas Date: Wed, 12 Jul 2023 20:58:11 -0700 Subject: [PATCH 18/50] Toolchain: implement searching PATH for executables --- src/Toolchain.zig | 2 +- src/util.zig | 43 ++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 43 insertions(+), 2 deletions(-) diff --git a/src/Toolchain.zig b/src/Toolchain.zig index b9393c35..d9df98cc 100644 --- a/src/Toolchain.zig +++ b/src/Toolchain.zig @@ -190,7 +190,7 @@ fn getProgramPath(tc: *const Toolchain, name: []const u8, buf: []u8) []const u8 return buf[0..candidate.len]; } } - // todo: check $PATH + return util.findProgramByName(tc.driver.comp.gpa, name, buf) orelse continue; } @memcpy(buf[0..name.len], name); return buf[0..name.len]; diff --git a/src/util.zig b/src/util.zig index 7de69d5e..5c666228 100644 --- a/src/util.zig +++ b/src/util.zig @@ -1,5 +1,7 @@ const std = @import("std"); -const is_windows = @import("builtin").os.tag == .windows; +const mem = std.mem; +const builtin = @import("builtin"); +const is_windows = builtin.os.tag == .windows; pub const Color = enum { reset, @@ -90,3 +92,42 @@ pub fn exists(path: []const u8) bool { std.os.access(path, std.os.F_OK) catch return false; return true; } + +/// TODO +fn findProgramByNameWindows(allocator: std.mem.Allocator, name: []const u8, buf: []u8) ?[]const u8 { + _ = buf; + _ = name; + _ = allocator; + return null; +} + +/// TODO: does WASI need special handling? +fn findProgramByNamePosix(name: []const u8, buf: []u8) ?[]const u8 { + if (mem.indexOfScalar(u8, name, '/') != null) { + @memcpy(buf[0..name.len], name); + return buf[0..name.len]; + } + const path_env = std.os.getenvZ("PATH") orelse return null; + var fib = std.heap.FixedBufferAllocator.init(buf); + + var it = mem.tokenizeScalar(u8, path_env, ':'); + while (it.next()) |path_dir| { + defer fib.reset(); + const full_path = std.fs.path.join(fib.allocator(), &.{ path_dir, name }) catch continue; + if (canExecute(full_path)) return full_path; + } + + return null; +} + +/// Search for an executable named `name` using platform-specific logic +/// If it's found, write the full path to `buf` and return a slice of it +/// Otherwise retun null +pub fn findProgramByName(allocator: std.mem.Allocator, name: []const u8, buf: []u8) ?[]const u8 { + std.debug.assert(name.len > 0); + if (is_windows) { + return findProgramByNameWindows(allocator, name, buf); + } else { + return findProgramByNamePosix(name, buf); + } +} From 984ee2460a888923357fe152c14dd4b6767d6f09 Mon Sep 17 00:00:00 2001 From: Evan Haas Date: Wed, 12 Jul 2023 22:18:43 -0700 Subject: [PATCH 19/50] Toolchain: add -L arguments for linker --- src/Toolchain.zig | 18 ++++++++++++++++++ src/toolchains/Linux.zig | 4 +++- 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/src/Toolchain.zig b/src/Toolchain.zig index d9df98cc..4c1760a9 100644 --- a/src/Toolchain.zig +++ b/src/Toolchain.zig @@ -169,6 +169,24 @@ fn possibleProgramNames(raw_triple: ?[]const u8, name: []const u8, target_specif return possible_names; } +/// Add toolchain `file_paths` to argv as `-L` arguments +pub fn addFilePathLibArgs(tc: *const Toolchain, argv: *std.ArrayList([]const u8)) !void { + try argv.ensureUnusedCapacity(tc.file_paths.items.len); + + var bytes_needed: usize = 0; + for (tc.file_paths.items) |path| { + bytes_needed += path.len + 2; // +2 for `-L` + } + var bytes = try tc.arena.alloc(u8, bytes_needed); + var index: usize = 0; + for (tc.file_paths.items) |path| { + @memcpy(bytes[index..][0..2], "-L"); + @memcpy(bytes[index + 2 ..][0..path.len], path); + argv.appendAssumeCapacity(bytes[index..][0 .. path.len + 2]); + index += path.len + 2; + } +} + /// Search for an executable called `name` or `{triple}-{name} in program_paths and the $PATH environment variable /// If not found there, just use `name` /// Writes the result to `buf` and returns a slice of it diff --git a/src/toolchains/Linux.zig b/src/toolchains/Linux.zig index aefef9fe..47176d55 100644 --- a/src/toolchains/Linux.zig +++ b/src/toolchains/Linux.zig @@ -200,7 +200,9 @@ pub fn buildLinkerArgs(self: *const Linux, tc: *const Toolchain, argv: *std.Arra // TODO add -L opts // TODO add -u opts - // TODO add filepath lib args + + try tc.addFilePathLibArgs(argv); + // TODO handle LTO try argv.appendSlice(d.link_objects.items); From d45425de575f076d67450cd15b15d521ccdaa33a Mon Sep 17 00:00:00 2001 From: Evan Haas Date: Fri, 14 Jul 2023 12:57:17 -0700 Subject: [PATCH 20/50] util: use platform-specific code for canExecute --- src/util.zig | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/src/util.zig b/src/util.zig index 5c666228..54030b00 100644 --- a/src/util.zig +++ b/src/util.zig @@ -82,12 +82,26 @@ pub fn errorDescription(err: anyerror) []const u8 { }; } -pub fn canExecute(path: []const u8) bool { +fn canExecutePosix(path: []const u8) bool { std.os.access(path, std.os.X_OK) catch return false; // Todo: ensure path is not a directory return true; } +/// TODO +fn canExecuteWindows(path: []const u8) bool { + _ = path; + return true; +} + +pub fn canExecute(path: []const u8) bool { + if (is_windows) { + return canExecuteWindows(path); + } else { + return canExecutePosix(path); + } +} + pub fn exists(path: []const u8) bool { std.os.access(path, std.os.F_OK) catch return false; return true; From f6f3ff1e27accdfd0a890b4d3fe31c559733f3b3 Mon Sep 17 00:00:00 2001 From: Evan Haas Date: Fri, 14 Jul 2023 15:54:42 -0700 Subject: [PATCH 21/50] GCCVersion: Add ability to parse GCC versions --- src/main.zig | 1 + src/toolchains/GCCVersion.zig | 121 ++++++++++++++++++++++++++++++++++ 2 files changed, 122 insertions(+) create mode 100644 src/toolchains/GCCVersion.zig diff --git a/src/main.zig b/src/main.zig index 8958ec04..c77ab120 100644 --- a/src/main.zig +++ b/src/main.zig @@ -92,6 +92,7 @@ test { _ = @import("Preprocessor.zig"); _ = @import("Source.zig"); _ = @import("Tokenizer.zig"); + _ = @import("toolchains/GCCVersion.zig"); _ = @import("Tree.zig"); _ = @import("Type.zig"); _ = @import("target.zig"); diff --git a/src/toolchains/GCCVersion.zig b/src/toolchains/GCCVersion.zig new file mode 100644 index 00000000..211db8a2 --- /dev/null +++ b/src/toolchains/GCCVersion.zig @@ -0,0 +1,121 @@ +const std = @import("std"); +const mem = std.mem; +const Order = std.math.Order; + +const GCCVersion = @This(); + +/// Raw version number text +raw: []const u8 = "", + +major: i32, +/// -1 indicates not present +minor: i32 = -1, +/// -1 indicates not present +patch: i32 = -1, + +/// Text of parsed major version number +major_str: []const u8 = "", +/// Text of parsed major + minor version number +minor_str: []const u8 = "", + +/// Patch number suffix +suffix: []const u8 = "", + +/// This orders versions according to the preferred usage order, not a notion of release-time ordering +/// Higher version numbers are preferred, but nonexistent minor/patch/suffix is preferred to one that does exist +/// e.g. `4.1` is preferred over `4.0` but `4` is preferred over both `4.0` and `4.1` +pub fn isLessThan(self: GCCVersion, rhs_major: i32, rhs_minor: i32, rhs_patch: i32, rhs_suffix: []const u8) bool { + if (self.major != rhs_major) { + return self.major < rhs_major; + } + if (self.minor != rhs_minor) { + if (rhs_minor == -1) return true; + if (self.minor == -1) return false; + return self.minor < rhs_minor; + } + if (self.patch != rhs_patch) { + if (rhs_patch == -1) return true; + if (self.patch == -1) return false; + return self.patch < rhs_patch; + } + if (!mem.eql(u8, self.suffix, rhs_suffix)) { + if (rhs_suffix.len == 0) return true; + if (self.suffix.len == 0) return false; + return switch (std.mem.order(u8, self.suffix, rhs_suffix)) { + .lt => true, + .eq => unreachable, + .gt => false, + }; + } + return false; +} + +/// Strings in the returned GCCVersion struct have the same lifetime as `text` +pub fn parse(text: []const u8) GCCVersion { + const bad = GCCVersion{ .major = -1 }; + var good = bad; + + var it = mem.splitScalar(u8, text, '.'); + const first = it.next().?; + const second = it.next() orelse ""; + const rest = it.next() orelse ""; + + good.major = std.fmt.parseInt(i32, first, 10) catch return bad; + if (good.major < 0) return bad; + good.major_str = first; + + if (second.len == 0) return good; + var minor_str = second; + + if (rest.len == 0) { + const end = mem.indexOfNone(u8, minor_str, "0123456789") orelse minor_str.len; + if (end > 0) { + good.suffix = minor_str[end..]; + minor_str = minor_str[0..end]; + } + } + good.minor = std.fmt.parseInt(i32, minor_str, 10) catch return bad; + if (good.minor < 0) return bad; + good.minor_str = minor_str; + + if (rest.len > 0) { + const end = mem.indexOfNone(u8, rest, "0123456789") orelse rest.len; + if (end > 0) { + const patch_num_text = rest[0..end]; + good.patch = std.fmt.parseInt(i32, patch_num_text, 10) catch return bad; + if (good.patch < 0) return bad; + good.suffix = rest[end..]; + } + } + + return good; +} + +pub fn order(a: GCCVersion, b: GCCVersion) Order { + if (a.isLessThan(b.major, b.minor, b.patch, b.suffix)) return .lt; + if (b.isLessThan(a.major, a.minor, a.patch, a.suffix)) return .gt; + return .eq; +} + +test parse { + const versions = [10]GCCVersion{ + parse("5"), + parse("4"), + parse("4.2"), + parse("4.0"), + parse("4.0-patched"), + parse("4.0.2"), + parse("4.0.1"), + parse("4.0.1-patched"), + parse("4.0.0"), + parse("4.0.0-patched"), + }; + + for (versions[0 .. versions.len - 1], versions[1..versions.len]) |first, second| { + try std.testing.expectEqual(Order.eq, first.order(first)); + try std.testing.expectEqual(Order.gt, first.order(second)); + try std.testing.expectEqual(Order.lt, second.order(first)); + } + const last = versions[versions.len - 1]; + try std.testing.expectEqual(Order.eq, last.order(last)); +} From 07b9b31eee54936f6678a5cfd38445a91f8ce68c Mon Sep 17 00:00:00 2001 From: Evan Haas Date: Mon, 17 Jul 2023 09:23:22 -0700 Subject: [PATCH 22/50] Toolchain: Add GCC detector to find GCC installs --- build.zig | 2 + src/Toolchain.zig | 15 + src/target.zig | 250 ++++++++++++++ src/toolchains/GCCDetector.zig | 576 +++++++++++++++++++++++++++++++++ src/toolchains/GCCVersion.zig | 3 +- src/toolchains/Linux.zig | 59 +++- src/toolchains/Multilib.zig | 67 ++++ src/util.zig | 7 + 8 files changed, 973 insertions(+), 6 deletions(-) create mode 100644 src/toolchains/GCCDetector.zig create mode 100644 src/toolchains/Multilib.zig diff --git a/build.zig b/build.zig index 488cebf0..81e28b83 100644 --- a/build.zig +++ b/build.zig @@ -43,6 +43,7 @@ pub fn build(b: *Build) !void { const enable_linker_build_id = b.option(bool, "enable-linker-build-id", "pass --build-id to linker") orelse false; const default_linker = b.option([]const u8, "default-linker", "Default linker aro will use if none is supplied via -fuse-ld") orelse "ld"; const default_sysroot = b.option([]const u8, "default-sysroot", "Default to all compiler invocations for --sysroot=.") orelse ""; + const gcc_install_prefix = b.option([]const u8, "gcc-install-prefix", "Directory where gcc is installed.") orelse ""; const test_all_allocation_failures = b.option(bool, "test-all-allocation-failures", "Test all allocation failures") orelse false; const link_libc = b.option(bool, "link-libc", "Force self-hosted compiler to link libc") orelse (mode != .Debug); const tracy = b.option([]const u8, "tracy", "Enable Tracy integration. Supply path to Tracy source"); @@ -75,6 +76,7 @@ pub fn build(b: *Build) !void { system_defaults.addOption(bool, "enable_linker_build_id", enable_linker_build_id); system_defaults.addOption([]const u8, "linker", default_linker); system_defaults.addOption([]const u8, "sysroot", default_sysroot); + system_defaults.addOption([]const u8, "gcc_install_prefix", gcc_install_prefix); exe_options.addOption(bool, "enable_tracy", tracy != null); exe_options.addOption(bool, "enable_tracy_callstack", tracy_callstack); diff --git a/src/Toolchain.zig b/src/Toolchain.zig index 4c1760a9..0394cd11 100644 --- a/src/Toolchain.zig +++ b/src/Toolchain.zig @@ -5,6 +5,7 @@ const util = @import("util.zig"); const mem = std.mem; const system_defaults = @import("system_defaults"); const Linux = @import("toolchains/Linux.zig"); +const Multilib = @import("toolchains/Multilib.zig"); const Toolchain = @This(); @@ -34,6 +35,8 @@ file_paths: PathList = .{}, /// The list of toolchain specific path prefixes to search for programs. program_paths: PathList = .{}, +selected_multilib: Multilib = .{}, + inner: Inner = .{ .unknown = {} }, pub fn getTarget(self: *const Toolchain) std.Target { @@ -291,6 +294,18 @@ pub fn addPathIfExists(self: *Toolchain, components: []const []const u8, dest_ki } } +/// Join `components` using the toolchain arena and add the resulting path to `dest_kind`. Does not check +/// whether the path actually exists +pub fn addPathFromComponents(self: *Toolchain, components: []const []const u8, dest_kind: PathKind) !void { + const full_path = try std.fs.path.join(self.arena, components); + const dest = switch (dest_kind) { + .library => &self.library_paths, + .file => &self.file_paths, + .program => &self.program_paths, + }; + try dest.append(self.driver.comp.gpa, full_path); +} + pub fn buildLinkerArgs(self: *Toolchain, argv: *std.ArrayList([]const u8)) !void { return switch (self.inner) { .linux => |*linux| linux.buildLinkerArgs(self, argv), diff --git a/src/target.zig b/src/target.zig index 56f74cdb..a4aeea12 100644 --- a/src/target.zig +++ b/src/target.zig @@ -1,6 +1,7 @@ const std = @import("std"); const LangOpts = @import("LangOpts.zig"); const Type = @import("Type.zig"); +const llvm = @import("zig").codegen.llvm; const TargetSet = @import("builtins/Properties.zig").TargetSet; /// intmax_t for this target @@ -412,6 +413,255 @@ pub fn ldEmulationOption(target: std.Target, arm_endianness: ?std.builtin.Endian }; } +pub fn get32BitArchVariant(target: std.Target) ?std.Target { + var copy = target; + switch (target.cpu.arch) { + .amdgcn, + .avr, + .msp430, + .spu_2, + .ve, + .bpfel, + .bpfeb, + .s390x, + => return null, + + .arc, + .arm, + .armeb, + .csky, + .hexagon, + .m68k, + .le32, + .mips, + .mipsel, + .powerpc, + .powerpcle, + .r600, + .riscv32, + .sparc, + .sparcel, + .tce, + .tcele, + .thumb, + .thumbeb, + .x86, + .xcore, + .nvptx, + .amdil, + .hsail, + .spir, + .kalimba, + .shave, + .lanai, + .wasm32, + .renderscript32, + .aarch64_32, + .spirv32, + .loongarch32, + .dxil, + .xtensa, + => {}, // Already 32 bit + + .aarch64 => copy.cpu.arch = .arm, + .aarch64_be => copy.cpu.arch = .armeb, + .le64 => copy.cpu.arch = .le32, + .amdil64 => copy.cpu.arch = .amdil, + .nvptx64 => copy.cpu.arch = .nvptx, + .wasm64 => copy.cpu.arch = .wasm32, + .hsail64 => copy.cpu.arch = .hsail, + .spir64 => copy.cpu.arch = .spir, + .spirv64 => copy.cpu.arch = .spirv32, + .renderscript64 => copy.cpu.arch = .renderscript32, + .loongarch64 => copy.cpu.arch = .loongarch32, + .mips64 => copy.cpu.arch = .mips, + .mips64el => copy.cpu.arch = .mipsel, + .powerpc64 => copy.cpu.arch = .powerpc, + .powerpc64le => copy.cpu.arch = .powerpcle, + .riscv64 => copy.cpu.arch = .riscv32, + .sparc64 => copy.cpu.arch = .sparc, + .x86_64 => copy.cpu.arch = .x86, + } + return copy; +} + +pub fn get64BitArchVariant(target: std.Target) ?std.Target { + // TODO: do the right thing + return get32BitArchVariant(target); +} + +/// Adapted from Zig's src/codegen/llvm.zig +pub fn toLLVMTriple(writer: anytype, target: std.Target) !void { + const llvm_arch = switch (target.cpu.arch) { + .arm => "arm", + .armeb => "armeb", + .aarch64 => "aarch64", + .aarch64_be => "aarch64_be", + .aarch64_32 => "aarch64_32", + .arc => "arc", + .avr => "avr", + .bpfel => "bpfel", + .bpfeb => "bpfeb", + .csky => "csky", + .dxil => "dxil", + .hexagon => "hexagon", + .loongarch32 => "loongarch32", + .loongarch64 => "loongarch64", + .m68k => "m68k", + .mips => "mips", + .mipsel => "mipsel", + .mips64 => "mips64", + .mips64el => "mips64el", + .msp430 => "msp430", + .powerpc => "powerpc", + .powerpcle => "powerpcle", + .powerpc64 => "powerpc64", + .powerpc64le => "powerpc64le", + .r600 => "r600", + .amdgcn => "amdgcn", + .riscv32 => "riscv32", + .riscv64 => "riscv64", + .sparc => "sparc", + .sparc64 => "sparc64", + .sparcel => "sparcel", + .s390x => "s390x", + .tce => "tce", + .tcele => "tcele", + .thumb => "thumb", + .thumbeb => "thumbeb", + .x86 => "i386", + .x86_64 => "x86_64", + .xcore => "xcore", + .xtensa => "xtensa", + .nvptx => "nvptx", + .nvptx64 => "nvptx64", + .le32 => "le32", + .le64 => "le64", + .amdil => "amdil", + .amdil64 => "amdil64", + .hsail => "hsail", + .hsail64 => "hsail64", + .spir => "spir", + .spir64 => "spir64", + .spirv32 => "spirv32", + .spirv64 => "spirv64", + .kalimba => "kalimba", + .shave => "shave", + .lanai => "lanai", + .wasm32 => "wasm32", + .wasm64 => "wasm64", + .renderscript32 => "renderscript32", + .renderscript64 => "renderscript64", + .ve => "ve", + .spu_2 => return error.@"LLVM backend does not support SPU Mark II", + }; + try writer.writeAll(llvm_arch); + try writer.writeByte('-'); + + const llvm_os = switch (target.os.tag) { + .freestanding => "unknown", + .ananas => "ananas", + .cloudabi => "cloudabi", + .dragonfly => "dragonfly", + .freebsd => "freebsd", + .fuchsia => "fuchsia", + .kfreebsd => "kfreebsd", + .linux => "linux", + .lv2 => "lv2", + .netbsd => "netbsd", + .openbsd => "openbsd", + .solaris => "solaris", + .windows => "windows", + .zos => "zos", + .haiku => "haiku", + .minix => "minix", + .rtems => "rtems", + .nacl => "nacl", + .aix => "aix", + .cuda => "cuda", + .nvcl => "nvcl", + .amdhsa => "amdhsa", + .ps4 => "ps4", + .ps5 => "ps5", + .elfiamcu => "elfiamcu", + .mesa3d => "mesa3d", + .contiki => "contiki", + .amdpal => "amdpal", + .hermit => "hermit", + .hurd => "hurd", + .wasi => "wasi", + .emscripten => "emscripten", + .uefi => "windows", + .macos => "macosx", + .ios => "ios", + .tvos => "tvos", + .watchos => "watchos", + .driverkit => "driverkit", + .shadermodel => "shadermodel", + .opencl, + .glsl450, + .vulkan, + .plan9, + .other, + => "unknown", + }; + try writer.writeAll(llvm_os); + + if (target.os.tag.isDarwin()) { + const min_version = target.os.version_range.semver.min; + try writer.print("{d}.{d}.{d}", .{ + min_version.major, + min_version.minor, + min_version.patch, + }); + } + try writer.writeByte('-'); + + const llvm_abi = switch (target.abi) { + .none => "unknown", + .gnu => "gnu", + .gnuabin32 => "gnuabin32", + .gnuabi64 => "gnuabi64", + .gnueabi => "gnueabi", + .gnueabihf => "gnueabihf", + .gnuf32 => "gnuf32", + .gnuf64 => "gnuf64", + .gnusf => "gnusf", + .gnux32 => "gnux32", + .gnuilp32 => "gnuilp32", + .code16 => "code16", + .eabi => "eabi", + .eabihf => "eabihf", + .android => "android", + .musl => "musl", + .musleabi => "musleabi", + .musleabihf => "musleabihf", + .muslx32 => "muslx32", + .msvc => "msvc", + .itanium => "itanium", + .cygnus => "cygnus", + .coreclr => "coreclr", + .simulator => "simulator", + .macabi => "macabi", + .pixel => "pixel", + .vertex => "vertex", + .geometry => "geometry", + .hull => "hull", + .domain => "domain", + .compute => "compute", + .library => "library", + .raygeneration => "raygeneration", + .intersection => "intersection", + .anyhit => "anyhit", + .closesthit => "closesthit", + .miss => "miss", + .callable => "callable", + .mesh => "mesh", + .amplification => "amplification", + }; + try writer.writeAll(llvm_abi); +} + test "alignment functions - smoke test" { var target: std.Target = undefined; const x86 = std.Target.Cpu.Arch.x86_64; diff --git a/src/toolchains/GCCDetector.zig b/src/toolchains/GCCDetector.zig new file mode 100644 index 00000000..15325a9d --- /dev/null +++ b/src/toolchains/GCCDetector.zig @@ -0,0 +1,576 @@ +const std = @import("std"); +const Driver = @import("../Driver.zig"); +const Toolchain = @import("../Toolchain.zig"); +const target_util = @import("../target.zig"); +const system_defaults = @import("system_defaults"); +const util = @import("../util.zig"); +const GCCVersion = @import("GCCVersion.zig"); +const Multilib = @import("Multilib.zig"); +const GCCDetector = @This(); + +is_valid: bool = false, +install_path: []const u8 = "", +parent_lib_path: []const u8 = "", +version: GCCVersion = .{}, +gcc_triple: []const u8 = "", +selected: Multilib = .{}, +biarch_sibling: ?Multilib = null, + +pub fn deinit(self: *GCCDetector) void { + if (!self.is_valid) return; +} + +pub fn appendToolPath(self: *const GCCDetector, tc: *Toolchain) !void { + if (!self.is_valid) return; + return tc.addPathFromComponents(&.{ + self.parent_lib_path, + "..", + self.gcc_triple, + "bin", + }, .program); +} + +fn addDefaultGCCPrefixes(prefixes: *PathPrefixes, tc: *const Toolchain) !void { + const sysroot = tc.getSysroot(); + const target = tc.getTarget(); + if (sysroot.len == 0 and target.os.tag == .linux and util.exists("/opt/rh")) { + prefixes.appendAssumeCapacity("/opt/rh/gcc-toolset-12/root/usr"); + prefixes.appendAssumeCapacity("/opt/rh/gcc-toolset-11/root/usr"); + prefixes.appendAssumeCapacity("/opt/rh/gcc-toolset-10/root/usr"); + prefixes.appendAssumeCapacity("/opt/rh/devtoolset-12/root/usr"); + prefixes.appendAssumeCapacity("/opt/rh/devtoolset-11/root/usr"); + prefixes.appendAssumeCapacity("/opt/rh/devtoolset-10/root/usr"); + prefixes.appendAssumeCapacity("/opt/rh/devtoolset-9/root/usr"); + prefixes.appendAssumeCapacity("/opt/rh/devtoolset-8/root/usr"); + prefixes.appendAssumeCapacity("/opt/rh/devtoolset-7/root/usr"); + prefixes.appendAssumeCapacity("/opt/rh/devtoolset-6/root/usr"); + prefixes.appendAssumeCapacity("/opt/rh/devtoolset-4/root/usr"); + prefixes.appendAssumeCapacity("/opt/rh/devtoolset-3/root/usr"); + prefixes.appendAssumeCapacity("/opt/rh/devtoolset-2/root/usr"); + } + if (sysroot.len == 0) { + prefixes.appendAssumeCapacity("/usr"); + } else { + var usr_path = try tc.arena.alloc(u8, 4 + sysroot.len); + @memcpy(usr_path[0..4], "/usr"); + @memcpy(usr_path[4..], sysroot); + prefixes.appendAssumeCapacity(usr_path); + } +} + +const PathPrefixes = std.BoundedArray([]const u8, 16); + +fn collectLibDirsAndTriples( + self: *GCCDetector, + tc: *Toolchain, + lib_dirs: *PathPrefixes, + triple_aliases: *PathPrefixes, + biarch_libdirs: *PathPrefixes, + biarch_triple_aliases: *PathPrefixes, +) !void { + _ = self; + + const AArch64LibDirs: [2][]const u8 = .{ "/lib64", "/lib" }; + const AArch64Triples: [4][]const u8 = .{ "aarch64-none-linux-gnu", "aarch64-linux-gnu", "aarch64-redhat-linux", "aarch64-suse-linux" }; + const AArch64beLibDirs: [1][]const u8 = .{"/lib"}; + const AArch64beTriples: [2][]const u8 = .{ "aarch64_be-none-linux-gnu", "aarch64_be-linux-gnu" }; + + const ARMLibDirs: [1][]const u8 = .{"/lib"}; + const ARMTriples: [1][]const u8 = .{"arm-linux-gnueabi"}; + const ARMHFTriples: [4][]const u8 = .{ "arm-linux-gnueabihf", "armv7hl-redhat-linux-gnueabi", "armv6hl-suse-linux-gnueabi", "armv7hl-suse-linux-gnueabi" }; + + const ARMebLibDirs: [1][]const u8 = .{"/lib"}; + const ARMebTriples: [1][]const u8 = .{"armeb-linux-gnueabi"}; + const ARMebHFTriples: [2][]const u8 = .{ "armeb-linux-gnueabihf", "armebv7hl-redhat-linux-gnueabi" }; + + const AVRLibDirs: [1][]const u8 = .{"/lib"}; + const AVRTriples: [1][]const u8 = .{"avr"}; + + const CSKYLibDirs: [1][]const u8 = .{"/lib"}; + const CSKYTriples: [3][]const u8 = .{ "csky-linux-gnuabiv2", "csky-linux-uclibcabiv2", "csky-elf-noneabiv2" }; + + const X86_64LibDirs: [2][]const u8 = .{ "/lib64", "/lib" }; + const X86_64Triples: [11][]const u8 = .{ + "x86_64-linux-gnu", "x86_64-unknown-linux-gnu", + "x86_64-pc-linux-gnu", "x86_64-redhat-linux6E", + "x86_64-redhat-linux", "x86_64-suse-linux", + "x86_64-manbo-linux-gnu", "x86_64-linux-gnu", + "x86_64-slackware-linux", "x86_64-unknown-linux", + "x86_64-amazon-linux", + }; + const X32Triples: [2][]const u8 = .{ "x86_64-linux-gnux32", "x86_64-pc-linux-gnux32" }; + const X32LibDirs: [2][]const u8 = .{ "/libx32", "/lib" }; + const X86LibDirs: [2][]const u8 = .{ "/lib32", "/lib" }; + const X86Triples: [9][]const u8 = .{ + "i586-linux-gnu", "i686-linux-gnu", "i686-pc-linux-gnu", + "i386-redhat-linux6E", "i686-redhat-linux", "i386-redhat-linux", + "i586-suse-linux", "i686-montavista-linux", "i686-gnu", + }; + + const LoongArch64LibDirs: [2][]const u8 = .{ "/lib64", "/lib" }; + const LoongArch64Triples: [2][]const u8 = .{ "loongarch64-linux-gnu", "loongarch64-unknown-linux-gnu" }; + + const M68kLibDirs: [1][]const u8 = .{"/lib"}; + const M68kTriples: [3][]const u8 = .{ "m68k-linux-gnu", "m68k-unknown-linux-gnu", "m68k-suse-linux" }; + + const MIPSLibDirs: [2][]const u8 = .{ "/libo32", "/lib" }; + const MIPSTriples: [5][]const u8 = .{ + "mips-linux-gnu", "mips-mti-linux", + "mips-mti-linux-gnu", "mips-img-linux-gnu", + "mipsisa32r6-linux-gnu", + }; + const MIPSELLibDirs: [2][]const u8 = .{ "/libo32", "/lib" }; + const MIPSELTriples: [3][]const u8 = .{ "mipsel-linux-gnu", "mips-img-linux-gnu", "mipsisa32r6el-linux-gnu" }; + + const MIPS64LibDirs: [2][]const u8 = .{ "/lib64", "/lib" }; + const MIPS64Triples: [6][]const u8 = .{ + "mips64-linux-gnu", "mips-mti-linux-gnu", + "mips-img-linux-gnu", "mips64-linux-gnuabi64", + "mipsisa64r6-linux-gnu", "mipsisa64r6-linux-gnuabi64", + }; + const MIPS64ELLibDirs: [2][]const u8 = .{ "/lib64", "/lib" }; + const MIPS64ELTriples: [6][]const u8 = .{ + "mips64el-linux-gnu", "mips-mti-linux-gnu", + "mips-img-linux-gnu", "mips64el-linux-gnuabi64", + "mipsisa64r6el-linux-gnu", "mipsisa64r6el-linux-gnuabi64", + }; + + const MIPSN32LibDirs: [1][]const u8 = .{"/lib32"}; + const MIPSN32Triples: [2][]const u8 = .{ "mips64-linux-gnuabin32", "mipsisa64r6-linux-gnuabin32" }; + const MIPSN32ELLibDirs: [1][]const u8 = .{"/lib32"}; + const MIPSN32ELTriples: [2][]const u8 = .{ "mips64el-linux-gnuabin32", "mipsisa64r6el-linux-gnuabin32" }; + + const MSP430LibDirs: [1][]const u8 = .{"/lib"}; + _ = MSP430LibDirs; + const MSP430Triples: [1][]const u8 = .{"msp430-elf"}; + _ = MSP430Triples; + + const PPCLibDirs: [2][]const u8 = .{ "/lib32", "/lib" }; + _ = PPCLibDirs; + const PPCTriples: [5][]const u8 = .{ + "powerpc-linux-gnu", "powerpc-unknown-linux-gnu", "powerpc-linux-gnuspe", + // On 32-bit PowerPC systems running SUSE Linux, gcc is configured as a + // 64-bit compiler which defaults to "-m32", hence "powerpc64-suse-linux". + "powerpc64-suse-linux", "powerpc-montavista-linuxspe", + }; + _ = PPCTriples; + const PPCLELibDirs: [2][]const u8 = .{ "/lib32", "/lib" }; + _ = PPCLELibDirs; + const PPCLETriples: [3][]const u8 = .{ "powerpcle-linux-gnu", "powerpcle-unknown-linux-gnu", "powerpcle-linux-musl" }; + _ = PPCLETriples; + + const PPC64LibDirs: [2][]const u8 = .{ "/lib64", "/lib" }; + _ = PPC64LibDirs; + const PPC64Triples: [4][]const u8 = .{ + "powerpc64-linux-gnu", "powerpc64-unknown-linux-gnu", + "powerpc64-suse-linux", "ppc64-redhat-linux", + }; + _ = PPC64Triples; + const PPC64LELibDirs: [2][]const u8 = .{ "/lib64", "/lib" }; + _ = PPC64LELibDirs; + const PPC64LETriples: [5][]const u8 = .{ + "powerpc64le-linux-gnu", "powerpc64le-unknown-linux-gnu", + "powerpc64le-none-linux-gnu", "powerpc64le-suse-linux", + "ppc64le-redhat-linux", + }; + _ = PPC64LETriples; + + const RISCV32LibDirs: [2][]const u8 = .{ "/lib32", "/lib" }; + _ = RISCV32LibDirs; + const RISCV32Triples: [3][]const u8 = .{ "riscv32-unknown-linux-gnu", "riscv32-linux-gnu", "riscv32-unknown-elf" }; + _ = RISCV32Triples; + const RISCV64LibDirs: [2][]const u8 = .{ "/lib64", "/lib" }; + _ = RISCV64LibDirs; + const RISCV64Triples: [3][]const u8 = .{ + "riscv64-unknown-linux-gnu", + "riscv64-linux-gnu", + "riscv64-unknown-elf", + }; + _ = RISCV64Triples; + + const SPARCv8LibDirs: [2][]const u8 = .{ "/lib32", "/lib" }; + _ = SPARCv8LibDirs; + const SPARCv8Triples: [2][]const u8 = .{ "sparc-linux-gnu", "sparcv8-linux-gnu" }; + _ = SPARCv8Triples; + const SPARCv9LibDirs: [2][]const u8 = .{ "/lib64", "/lib" }; + _ = SPARCv9LibDirs; + const SPARCv9Triples: [2][]const u8 = .{ "sparc64-linux-gnu", "sparcv9-linux-gnu" }; + _ = SPARCv9Triples; + + const SystemZLibDirs: [2][]const u8 = .{ "/lib64", "/lib" }; + _ = SystemZLibDirs; + const SystemZTriples: [5][]const u8 = .{ + "s390x-linux-gnu", "s390x-unknown-linux-gnu", "s390x-ibm-linux-gnu", + "s390x-suse-linux", "s390x-redhat-linux", + }; + _ = SystemZTriples; + const target = tc.getTarget(); + if (target.os.tag == .solaris) { + // TODO + return; + } + if (target.isAndroid()) { + const AArch64AndroidTriples: [1][]const u8 = .{"aarch64-linux-android"}; + const ARMAndroidTriples: [1][]const u8 = .{"arm-linux-androideabi"}; + const MIPSELAndroidTriples: [1][]const u8 = .{"mipsel-linux-android"}; + const MIPS64ELAndroidTriples: [1][]const u8 = .{"mips64el-linux-android"}; + const X86AndroidTriples: [1][]const u8 = .{"i686-linux-android"}; + const X86_64AndroidTriples: [1][]const u8 = .{"x86_64-linux-android"}; + + switch (target.cpu.arch) { + .aarch64 => { + lib_dirs.appendSliceAssumeCapacity(&AArch64LibDirs); + triple_aliases.appendSliceAssumeCapacity(&AArch64AndroidTriples); + }, + .arm, + .thumb, + => { + lib_dirs.appendSliceAssumeCapacity(&ARMLibDirs); + triple_aliases.appendSliceAssumeCapacity(&ARMAndroidTriples); + }, + .mipsel => { + lib_dirs.appendSliceAssumeCapacity(&MIPSELLibDirs); + triple_aliases.appendSliceAssumeCapacity(&MIPSELAndroidTriples); + biarch_libdirs.appendSliceAssumeCapacity(&MIPS64ELLibDirs); + biarch_triple_aliases.appendSliceAssumeCapacity(&MIPS64ELAndroidTriples); + }, + .mips64el => { + lib_dirs.appendSliceAssumeCapacity(&MIPS64ELLibDirs); + triple_aliases.appendSliceAssumeCapacity(&MIPS64ELAndroidTriples); + biarch_libdirs.appendSliceAssumeCapacity(&MIPSELLibDirs); + biarch_triple_aliases.appendSliceAssumeCapacity(&MIPSELAndroidTriples); + }, + .x86_64 => { + lib_dirs.appendSliceAssumeCapacity(&X86_64LibDirs); + triple_aliases.appendSliceAssumeCapacity(&X86_64AndroidTriples); + biarch_libdirs.appendSliceAssumeCapacity(&X86LibDirs); + biarch_triple_aliases.appendSliceAssumeCapacity(&X86AndroidTriples); + }, + .x86 => { + lib_dirs.appendSliceAssumeCapacity(&X86LibDirs); + triple_aliases.appendSliceAssumeCapacity(&X86AndroidTriples); + biarch_libdirs.appendSliceAssumeCapacity(&X86_64LibDirs); + biarch_triple_aliases.appendSliceAssumeCapacity(&X86_64AndroidTriples); + }, + else => {}, + } + return; + } + switch (target.cpu.arch) { + .aarch64 => { + lib_dirs.appendSliceAssumeCapacity(&AArch64LibDirs); + triple_aliases.appendSliceAssumeCapacity(&AArch64Triples); + biarch_libdirs.appendSliceAssumeCapacity(&AArch64LibDirs); + biarch_triple_aliases.appendSliceAssumeCapacity(&AArch64Triples); + }, + .aarch64_be => { + lib_dirs.appendSliceAssumeCapacity(&AArch64beLibDirs); + triple_aliases.appendSliceAssumeCapacity(&AArch64beTriples); + biarch_libdirs.appendSliceAssumeCapacity(&AArch64beLibDirs); + biarch_triple_aliases.appendSliceAssumeCapacity(&AArch64beTriples); + }, + .arm, .thumb => { + lib_dirs.appendSliceAssumeCapacity(&ARMLibDirs); + if (target.abi == .gnueabihf) { + triple_aliases.appendSliceAssumeCapacity(&ARMHFTriples); + } else { + triple_aliases.appendSliceAssumeCapacity(&ARMTriples); + } + }, + .armeb, .thumbeb => { + lib_dirs.appendSliceAssumeCapacity(&ARMebLibDirs); + if (target.abi == .gnueabihf) { + triple_aliases.appendSliceAssumeCapacity(&ARMebHFTriples); + } else { + triple_aliases.appendSliceAssumeCapacity(&ARMebTriples); + } + }, + .avr => { + lib_dirs.appendSliceAssumeCapacity(&AVRLibDirs); + triple_aliases.appendSliceAssumeCapacity(&AVRTriples); + }, + .csky => { + lib_dirs.appendSliceAssumeCapacity(&CSKYLibDirs); + triple_aliases.appendSliceAssumeCapacity(&CSKYTriples); + }, + .x86_64 => { + if (target.abi == .gnux32 or target.abi == .muslx32) { + lib_dirs.appendSliceAssumeCapacity(&X32LibDirs); + triple_aliases.appendSliceAssumeCapacity(&X32Triples); + biarch_libdirs.appendSliceAssumeCapacity(&X86_64LibDirs); + biarch_triple_aliases.appendSliceAssumeCapacity(&X86_64Triples); + } else { + lib_dirs.appendSliceAssumeCapacity(&X86_64LibDirs); + triple_aliases.appendSliceAssumeCapacity(&X86_64Triples); + biarch_libdirs.appendSliceAssumeCapacity(&X32LibDirs); + biarch_triple_aliases.appendSliceAssumeCapacity(&X32Triples); + } + biarch_libdirs.appendSliceAssumeCapacity(&X86LibDirs); + biarch_triple_aliases.appendSliceAssumeCapacity(&X86Triples); + }, + .x86 => { + lib_dirs.appendSliceAssumeCapacity(&X86LibDirs); + // MCU toolchain is 32 bit only and its triple alias is TargetTriple + // itself, which will be appended below. + if (target.os.tag != .elfiamcu) { + triple_aliases.appendSliceAssumeCapacity(&X86Triples); + biarch_libdirs.appendSliceAssumeCapacity(&X86_64LibDirs); + biarch_triple_aliases.appendSliceAssumeCapacity(&X86_64Triples); + biarch_libdirs.appendSliceAssumeCapacity(&X32LibDirs); + biarch_triple_aliases.appendSliceAssumeCapacity(&X32Triples); + } + }, + .loongarch64 => { + lib_dirs.appendSliceAssumeCapacity(&LoongArch64LibDirs); + triple_aliases.appendSliceAssumeCapacity(&LoongArch64Triples); + }, + .m68k => { + lib_dirs.appendSliceAssumeCapacity(&M68kLibDirs); + triple_aliases.appendSliceAssumeCapacity(&M68kTriples); + }, + .mips => { + lib_dirs.appendSliceAssumeCapacity(&MIPSLibDirs); + triple_aliases.appendSliceAssumeCapacity(&MIPSTriples); + biarch_libdirs.appendSliceAssumeCapacity(&MIPS64LibDirs); + biarch_triple_aliases.appendSliceAssumeCapacity(&MIPS64Triples); + biarch_libdirs.appendSliceAssumeCapacity(&MIPSN32LibDirs); + biarch_triple_aliases.appendSliceAssumeCapacity(&MIPSN32Triples); + }, + .mipsel => { + lib_dirs.appendSliceAssumeCapacity(&MIPSELLibDirs); + triple_aliases.appendSliceAssumeCapacity(&MIPSELTriples); + triple_aliases.appendSliceAssumeCapacity(&MIPSTriples); + biarch_libdirs.appendSliceAssumeCapacity(&MIPS64ELLibDirs); + biarch_triple_aliases.appendSliceAssumeCapacity(&MIPS64ELTriples); + biarch_libdirs.appendSliceAssumeCapacity(&MIPSN32ELLibDirs); + biarch_triple_aliases.appendSliceAssumeCapacity(&MIPSN32ELTriples); + }, + .mips64 => @panic("TODO"), + .mips64el => @panic("TODO"), + .msp430 => @panic("TODO"), + .powerpc => @panic("TODO"), + .powerpcle => @panic("TODO"), + .powerpc64 => @panic("TODO"), + .powerpc64le => @panic("TODO"), + .riscv32 => @panic("TODO"), + .riscv64 => @panic("TODO"), + .sparc, .sparcel => @panic("TODO"), + .sparc64 => @panic("TODO"), + .s390x => @panic("TODO"), + else => {}, + } +} + +pub fn discover(self: *GCCDetector, tc: *Toolchain) !void { + var path_buf: [std.fs.MAX_PATH_BYTES]u8 = undefined; + var fib = std.heap.FixedBufferAllocator.init(&path_buf); + + const target = tc.getTarget(); + const bivariant_target = if (target.ptrBitWidth() == 32) target_util.get64BitArchVariant(target) else target_util.get32BitArchVariant(target); + _ = bivariant_target; + + var candidate_lib_dirs = PathPrefixes.init(0) catch unreachable; + var candidate_biarch_lib_dirs = PathPrefixes.init(0) catch unreachable; + var candidate_triple_aliases = PathPrefixes.init(0) catch unreachable; + var candidate_biarch_triple_aliases = PathPrefixes.init(0) catch unreachable; + try self.collectLibDirsAndTriples(tc, &candidate_lib_dirs, &candidate_biarch_lib_dirs, &candidate_triple_aliases, &candidate_biarch_triple_aliases); + + var target_buf: std.BoundedArray(u8, 32) = .{}; + try target_util.toLLVMTriple(target_buf.writer(), target); + const triple_str = target_buf.constSlice(); + candidate_triple_aliases.appendAssumeCapacity(triple_str); + + // // Also include the multiarch variant if it's different. + // if (TargetTriple.str() != BiarchTriple.str()) + // BiarchTripleAliases.push_back(BiarchTriple.str()); + + var prefixes = PathPrefixes.init(0) catch unreachable; + const gcc_toolchain_dir = gccToolchainDir(tc); + if (gcc_toolchain_dir.len != 0) { + const adjusted = if (gcc_toolchain_dir[gcc_toolchain_dir.len - 1] == '/') + gcc_toolchain_dir[0 .. gcc_toolchain_dir.len - 1] + else + gcc_toolchain_dir; + prefixes.appendAssumeCapacity(adjusted); + } else { + const sysroot = tc.getSysroot(); + if (sysroot.len > 0) { + prefixes.appendAssumeCapacity(sysroot); + try addDefaultGCCPrefixes(&prefixes, tc); + } + + if (sysroot.len == 0) { + try addDefaultGCCPrefixes(&prefixes, tc); + } + // TODO: Special-case handling for Gentoo + } + + const v0 = GCCVersion.parse("0.0.0"); + for (prefixes.constSlice()) |prefix| { + if (!util.exists(prefix)) continue; + + for (candidate_lib_dirs.constSlice()) |suffix| { + defer fib.reset(); + const lib_dir = std.fs.path.join(fib.allocator(), &.{ prefix, suffix }) catch continue; + if (!util.exists(lib_dir)) continue; + + const gcc_dir_exists = util.joinedExists(&.{ lib_dir, "/gcc" }); + const gcc_cross_dir_exists = util.joinedExists(&.{ lib_dir, "/gcc-cross" }); + + try self.scanLibDirForGCCTriple(tc.arena, target, lib_dir, triple_str, false, gcc_dir_exists, gcc_cross_dir_exists); + for (candidate_triple_aliases.constSlice()) |candidate| { + try self.scanLibDirForGCCTriple(tc.arena, target, lib_dir, candidate, false, gcc_dir_exists, gcc_cross_dir_exists); + } + } + for (candidate_biarch_lib_dirs.constSlice()) |suffix| { + const lib_dir = std.fs.path.join(fib.allocator(), &.{ prefix, suffix }) catch continue; + if (!util.exists(lib_dir)) continue; + + const gcc_dir_exists = util.joinedExists(&.{ lib_dir, "/gcc" }); + const gcc_cross_dir_exists = util.joinedExists(&.{ lib_dir, "/gcc-cross" }); + for (candidate_biarch_triple_aliases.constSlice()) |candidate| { + try self.scanLibDirForGCCTriple(tc.arena, target, lib_dir, candidate, true, gcc_dir_exists, gcc_cross_dir_exists); + } + } + if (self.version.order(v0) == .gt) break; + } +} + +fn findBiarchMultilibs(self: *GCCDetector, result: *Multilib.Detected, target: std.Target, path: [2][]const u8, needs_biarch_suffix: bool) !bool { + const suff64 = if (target.os.tag == .solaris) switch (target.cpu.arch) { + .x86, .x86_64 => "/amd64", + .sparc => "/sparcv9", + else => "/64", + } else "/64"; + + _ = self; + const alt_64 = Multilib.init(suff64, suff64, &.{ "-m32", "+m64", "-mx32" }); + const alt_32 = Multilib.init("/32", "/32", &.{ "+m32", "-m64", "-mx32" }); + const alt_x32 = Multilib.init("/x32", "/x32", &.{ "-m32", "-m64", "+mx32" }); + + const multilib_filter = Multilib.Filter{ + .base = path, + .file = if (target.os.tag == .elfiamcu) "libgcc.a" else "crtbegin.o", + }; + + const Want = enum { + want32, + want64, + wantx32, + }; + const is_x32 = target.abi == .gnux32 or target.abi == .muslx32; + const target_ptr_width = target.ptrBitWidth(); + const want: Want = if (target_ptr_width == 32 and multilib_filter.exists(alt_32)) + .want64 + else if (target_ptr_width == 64 and is_x32 and multilib_filter.exists(alt_x32)) + .want64 + else if (target_ptr_width == 64 and !is_x32 and multilib_filter.exists(alt_64)) + .want32 + else if (target_ptr_width == 32) + if (needs_biarch_suffix) .want64 else .want32 + else if (is_x32) + if (needs_biarch_suffix) .want64 else .wantx32 + else if (needs_biarch_suffix) .want32 else .want64; + + const default = switch (want) { + .want32 => Multilib.init("", "", &.{ "+m32", "-m64", "-mx32" }), + .want64 => Multilib.init("", "", &.{ "-m32", "+m64", "-mx32" }), + .wantx32 => Multilib.init("", "", &.{ "-m32", "-m64", "+mx32" }), + }; + result.multilibs.appendSliceAssumeCapacity(&.{ + default, + alt_64, + alt_32, + alt_x32, + }); + result.filter(multilib_filter); + var flags: Multilib.Flags = .{}; + flags.appendAssumeCapacity(if (target_ptr_width == 64 and !is_x32) "+m64" else "-m64"); + flags.appendAssumeCapacity(if (target_ptr_width == 32) "+m32" else "-m32"); + flags.appendAssumeCapacity(if (target_ptr_width == 64 and is_x32) "+mx32" else "-mx32"); + + const success = result.select(flags); + if (!success) return false; + + return true; +} + +fn scanGCCForMultilibs(self: *GCCDetector, target: std.Target, path: [2][]const u8, needs_biarch_suffix: bool) !bool { + var detected: Multilib.Detected = .{}; + if (target.cpu.arch == .csky) { + // TODO + } else if (target.cpu.arch.isMIPS()) { + // TODO + } else if (target.cpu.arch.isRISCV()) { + // TODO + } else if (target.cpu.arch == .msp430) { + // TODO + } else if (target.cpu.arch == .avr) { + // No multilibs + } else if (!try self.findBiarchMultilibs(&detected, target, path, needs_biarch_suffix)) { + return false; + } + self.selected = detected.selected; + self.biarch_sibling = detected.biarch_sibling; + return true; +} + +fn scanLibDirForGCCTriple( + self: *GCCDetector, + allocator: std.mem.Allocator, + target: std.Target, + lib_dir: []const u8, + candidate_triple: []const u8, + needs_biarch_suffix: bool, + gcc_dir_exists: bool, + gcc_cross_dir_exists: bool, +) !void { + var path_buf: [std.fs.MAX_PATH_BYTES]u8 = undefined; + var fib = std.heap.FixedBufferAllocator.init(&path_buf); + for (0..2) |i| { + if (i == 0 and !gcc_dir_exists) continue; + if (i == 1 and !gcc_cross_dir_exists) continue; + defer fib.reset(); + + const base: []const u8 = if (i == 0) "gcc" else "gcc-cross"; + var lib_suffix_buf: std.BoundedArray(u8, 64) = .{}; + lib_suffix_buf.appendSliceAssumeCapacity(base); + lib_suffix_buf.appendAssumeCapacity(std.fs.path.sep); + lib_suffix_buf.appendSliceAssumeCapacity(candidate_triple); + const lib_suffix = lib_suffix_buf.constSlice(); + + const dir_name = std.fs.path.join(fib.allocator(), &.{ lib_dir, lib_suffix }) catch continue; + var parent_dir = std.fs.cwd().openIterableDir(dir_name, .{ .access_sub_paths = false }) catch continue; + defer parent_dir.close(); + + var it = parent_dir.iterate(); + while (try it.next()) |entry| { + if (entry.kind != .directory) continue; + + const version_text = entry.name; + const candidate_version = GCCVersion.parse(version_text); + if (candidate_version.major != -1) { + // TODO: cache path so we're not repeatedly scanning + } + if (candidate_version.isLessThan(4, 1, 1, "")) continue; + switch (candidate_version.order(self.version)) { + .lt, .eq => continue, + .gt => {}, + } + + if (!try self.scanGCCForMultilibs(target, .{ dir_name, version_text }, needs_biarch_suffix)) continue; + + self.version = candidate_version; + self.gcc_triple = try allocator.dupe(u8, candidate_triple); + self.install_path = try std.fs.path.join(allocator, &.{ lib_dir, lib_suffix, version_text }); + self.parent_lib_path = try std.fs.path.join(allocator, &.{ self.install_path, "..", "..", ".." }); + self.is_valid = true; + } + } +} + +fn gccToolchainDir(tc: *const Toolchain) []const u8 { + const sysroot = tc.getSysroot(); + if (sysroot.len != 0) return ""; + return system_defaults.gcc_install_prefix; +} diff --git a/src/toolchains/GCCVersion.zig b/src/toolchains/GCCVersion.zig index 211db8a2..c4d6a65e 100644 --- a/src/toolchains/GCCVersion.zig +++ b/src/toolchains/GCCVersion.zig @@ -7,7 +7,8 @@ const GCCVersion = @This(); /// Raw version number text raw: []const u8 = "", -major: i32, +/// -1 indicates not present +major: i32 = -1, /// -1 indicates not present minor: i32 = -1, /// -1 indicates not present diff --git a/src/toolchains/Linux.zig b/src/toolchains/Linux.zig index 47176d55..4be1ebf6 100644 --- a/src/toolchains/Linux.zig +++ b/src/toolchains/Linux.zig @@ -1,6 +1,7 @@ const std = @import("std"); const mem = std.mem; const Compilation = @import("../Compilation.zig"); +const GCCDetector = @import("GCCDetector.zig"); const Toolchain = @import("../Toolchain.zig"); const Driver = @import("../Driver.zig"); const Distro = @import("../Distro.zig"); @@ -11,9 +12,14 @@ const Linux = @This(); distro: Distro.Tag = .unknown, extra_opts: std.ArrayListUnmanaged([]const u8) = .{}, +gcc_detector: GCCDetector = .{}, pub fn discover(self: *Linux, tc: *Toolchain) !void { self.distro = Distro.detect(tc.getTarget()); + try self.gcc_detector.discover(tc); + tc.selected_multilib = self.gcc_detector.selected; + + try self.gcc_detector.appendToolPath(tc); try self.buildExtraOpts(tc); try self.findPaths(tc); } @@ -56,22 +62,65 @@ fn buildExtraOpts(self: *Linux, tc: *const Toolchain) !void { } } +fn addMultiLibPaths(self: *Linux, tc: *Toolchain, sysroot: []const u8, os_lib_dir: []const u8) !void { + if (!self.gcc_detector.is_valid) return; + const gcc_triple = self.gcc_detector.gcc_triple; + const lib_path = self.gcc_detector.parent_lib_path; + + // Add lib/gcc/$triple/$version, with an optional /multilib suffix. + try tc.addPathIfExists(&.{ self.gcc_detector.install_path, tc.selected_multilib.gcc_suffix }, .file); + + // Add lib/gcc/$triple/$libdir + // For GCC built with --enable-version-specific-runtime-libs. + try tc.addPathIfExists(&.{ self.gcc_detector.install_path, "..", os_lib_dir }, .file); + + try tc.addPathIfExists(&.{ lib_path, "..", gcc_triple, "lib", "..", os_lib_dir, tc.selected_multilib.os_suffix }, .file); + + // If the GCC installation we found is inside of the sysroot, we want to + // prefer libraries installed in the parent prefix of the GCC installation. + // It is important to *not* use these paths when the GCC installation is + // outside of the system root as that can pick up unintended libraries. + // This usually happens when there is an external cross compiler on the + // host system, and a more minimal sysroot available that is the target of + // the cross. Note that GCC does include some of these directories in some + // configurations but this seems somewhere between questionable and simply + // a bug. + if (mem.startsWith(u8, lib_path, sysroot)) { + try tc.addPathIfExists(&.{ lib_path, "..", os_lib_dir }, .file); + } +} + +fn addMultiArchPaths(self: *Linux, tc: *Toolchain) !void { + if (!self.gcc_detector.is_valid) return; + const lib_path = self.gcc_detector.parent_lib_path; + const gcc_triple = self.gcc_detector.gcc_triple; + const multilib = self.gcc_detector.selected; + try tc.addPathIfExists(&.{ lib_path, "..", gcc_triple, "lib", multilib.os_suffix }, .file); +} + /// TODO: Very incomplete fn findPaths(self: *Linux, tc: *Toolchain) !void { - _ = self; const target = tc.getTarget(); const sysroot = tc.getSysroot(); const os_lib_dir = getOSLibDir(target); const multiarch_triple = getMultiarchTriple(target); + try self.addMultiLibPaths(tc, sysroot, os_lib_dir); + try tc.addPathIfExists(&.{ sysroot, "/lib", multiarch_triple }, .file); - try tc.addPathIfExists(&.{ sysroot, "/lib/..", os_lib_dir }, .file); - try tc.addPathIfExists(&.{ sysroot, "/usr/lib", multiarch_triple }, .file); - try tc.addPathIfExists(&.{ sysroot, "/usr/lib/..", os_lib_dir }, .file); + try tc.addPathIfExists(&.{ sysroot, "/lib", "..", os_lib_dir }, .file); + + if (target.isAndroid()) { + // TODO + } + try tc.addPathIfExists(&.{ sysroot, "/usr", "lib", multiarch_triple }, .file); + try tc.addPathIfExists(&.{ sysroot, "/usr", "lib", "..", os_lib_dir }, .file); + + try self.addMultiArchPaths(tc); try tc.addPathIfExists(&.{ sysroot, "/lib" }, .file); - try tc.addPathIfExists(&.{ sysroot, "/usr/lib" }, .file); + try tc.addPathIfExists(&.{ sysroot, "/usr", "lib" }, .file); } pub fn deinit(self: *Linux, allocator: std.mem.Allocator) void { diff --git a/src/toolchains/Multilib.zig b/src/toolchains/Multilib.zig new file mode 100644 index 00000000..b1962386 --- /dev/null +++ b/src/toolchains/Multilib.zig @@ -0,0 +1,67 @@ +const std = @import("std"); +const util = @import("../util.zig"); + +pub const Flags = std.BoundedArray([]const u8, 6); + +pub const Detected = struct { + multilibs: std.BoundedArray(Multilib, 4) = .{}, + selected: Multilib = .{}, + biarch_sibling: ?Multilib = null, + + pub fn filter(self: *Detected, multilib_filter: Filter) void { + var found_count: usize = 0; + for (self.multilibs.constSlice()) |multilib| { + if (multilib_filter.exists(multilib)) { + self.multilibs.set(found_count, multilib); + found_count += 1; + } + } + self.multilibs.resize(found_count) catch unreachable; + } + + pub fn select(self: *Detected, flags: Flags) bool { + var filtered: std.BoundedArray(Multilib, 4) = .{}; + for (self.multilibs.constSlice()) |multilib| { + for (multilib.flags.constSlice()) |multilib_flag| { + const matched = for (flags.constSlice()) |arg_flag| { + if (std.mem.eql(u8, arg_flag[1..], multilib_flag[1..])) break arg_flag; + } else multilib_flag; + if (matched[0] != multilib_flag[0]) break; + } else { + filtered.appendAssumeCapacity(multilib); + } + } + if (filtered.len == 0) return false; + if (filtered.len == 1) { + self.selected = filtered.get(0); + return true; + } + @panic("Got too many multilibs"); + } +}; + +pub const Filter = struct { + base: [2][]const u8, + file: []const u8, + pub fn exists(self: Filter, m: Multilib) bool { + return util.joinedExists(&.{ self.base[0], self.base[1], m.gcc_suffix, self.file }); + } +}; + +const Multilib = @This(); + +gcc_suffix: []const u8 = "", +os_suffix: []const u8 = "", +include_suffix: []const u8 = "", +flags: Flags = .{}, +priority: u32 = 0, + +pub fn init(gcc_suffix: []const u8, os_suffix: []const u8, flags: []const []const u8) Multilib { + var self: Multilib = .{ + .flags = Flags.init(0) catch unreachable, + .gcc_suffix = gcc_suffix, + .os_suffix = os_suffix, + }; + self.flags.appendSliceAssumeCapacity(flags); + return self; +} diff --git a/src/util.zig b/src/util.zig index 54030b00..7f33a24f 100644 --- a/src/util.zig +++ b/src/util.zig @@ -107,6 +107,13 @@ pub fn exists(path: []const u8) bool { return true; } +pub fn joinedExists(parts: []const []const u8) bool { + var buf: [std.fs.MAX_PATH_BYTES]u8 = undefined; + var fib = std.heap.FixedBufferAllocator.init(&buf); + const joined = std.fs.path.join(fib.allocator(), parts) catch return false; + return exists(joined); +} + /// TODO fn findProgramByNameWindows(allocator: std.mem.Allocator, name: []const u8, buf: []u8) ?[]const u8 { _ = buf; From afeebd3994491ee260e989fc879102949160f56c Mon Sep 17 00:00:00 2001 From: Evan Haas Date: Tue, 18 Jul 2023 08:50:23 -0700 Subject: [PATCH 23/50] Driver: move driver files into their own directory --- src/Toolchain.zig | 4 ++-- src/{ => driver}/Distro.zig | 2 +- src/{ => driver}/Driver.zig | 22 +++++++++++----------- src/{toolchains => driver}/GCCDetector.zig | 1 - src/{toolchains => driver}/GCCVersion.zig | 0 src/{toolchains => driver}/Multilib.zig | 0 src/lib.zig | 2 +- src/main.zig | 6 +++--- src/toolchains/Linux.zig | 6 +++--- 9 files changed, 21 insertions(+), 22 deletions(-) rename src/{ => driver}/Distro.zig (99%) rename src/{ => driver}/Driver.zig (97%) rename src/{toolchains => driver}/GCCDetector.zig (99%) rename src/{toolchains => driver}/GCCVersion.zig (100%) rename src/{toolchains => driver}/Multilib.zig (100%) diff --git a/src/Toolchain.zig b/src/Toolchain.zig index 0394cd11..853e9ea2 100644 --- a/src/Toolchain.zig +++ b/src/Toolchain.zig @@ -1,11 +1,11 @@ const std = @import("std"); -const Driver = @import("Driver.zig"); +const Driver = @import("driver/Driver.zig"); const Compilation = @import("Compilation.zig"); const util = @import("util.zig"); const mem = std.mem; const system_defaults = @import("system_defaults"); const Linux = @import("toolchains/Linux.zig"); -const Multilib = @import("toolchains/Multilib.zig"); +const Multilib = @import("driver/Multilib.zig"); const Toolchain = @This(); diff --git a/src/Distro.zig b/src/driver/Distro.zig similarity index 99% rename from src/Distro.zig rename to src/driver/Distro.zig index 9e35e968..69d39b55 100644 --- a/src/Distro.zig +++ b/src/driver/Distro.zig @@ -2,7 +2,7 @@ const std = @import("std"); const mem = std.mem; -const util = @import("util.zig"); +const util = @import("../util.zig"); const MAX_BYTES = 1024; // TODO: Can we assume 1024 bytes enough for the info we need? diff --git a/src/Driver.zig b/src/driver/Driver.zig similarity index 97% rename from src/Driver.zig rename to src/driver/Driver.zig index 3a0be6cf..f024f49f 100644 --- a/src/Driver.zig +++ b/src/driver/Driver.zig @@ -2,15 +2,15 @@ const std = @import("std"); const mem = std.mem; const Allocator = mem.Allocator; const process = std.process; -const Codegen = @import("Codegen_legacy.zig"); -const Compilation = @import("Compilation.zig"); -const LangOpts = @import("LangOpts.zig"); -const Preprocessor = @import("Preprocessor.zig"); -const Parser = @import("Parser.zig"); -const Source = @import("Source.zig"); -const Toolchain = @import("Toolchain.zig"); -const util = @import("util.zig"); -const target_util = @import("target.zig"); +const Codegen = @import("../Codegen_legacy.zig"); +const Compilation = @import("../Compilation.zig"); +const LangOpts = @import("../LangOpts.zig"); +const Preprocessor = @import("../Preprocessor.zig"); +const Parser = @import("../Parser.zig"); +const Source = @import("../Source.zig"); +const Toolchain = @import("../Toolchain.zig"); +const util = @import("../util.zig"); +const target_util = @import("../target.zig"); const Driver = @This(); @@ -170,7 +170,7 @@ pub fn parseArgs( }; return true; } else if (mem.eql(u8, arg, "-v") or mem.eql(u8, arg, "--version")) { - std_out.writeAll(@import("lib.zig").version_str ++ "\n") catch |er| { + std_out.writeAll(@import("../lib.zig").version_str ++ "\n") catch |er| { return d.fatal("unable to print version: {s}", .{util.errorDescription(er)}); }; return true; @@ -551,7 +551,7 @@ fn processSource( } if (d.verbose_ir) { - try @import("CodeGen.zig").generateTree(d.comp, tree); + try @import("../CodeGen.zig").generateTree(d.comp, tree); } const obj = try Codegen.generateTree(d.comp, tree); diff --git a/src/toolchains/GCCDetector.zig b/src/driver/GCCDetector.zig similarity index 99% rename from src/toolchains/GCCDetector.zig rename to src/driver/GCCDetector.zig index 15325a9d..fdb5afc5 100644 --- a/src/toolchains/GCCDetector.zig +++ b/src/driver/GCCDetector.zig @@ -1,5 +1,4 @@ const std = @import("std"); -const Driver = @import("../Driver.zig"); const Toolchain = @import("../Toolchain.zig"); const target_util = @import("../target.zig"); const system_defaults = @import("system_defaults"); diff --git a/src/toolchains/GCCVersion.zig b/src/driver/GCCVersion.zig similarity index 100% rename from src/toolchains/GCCVersion.zig rename to src/driver/GCCVersion.zig diff --git a/src/toolchains/Multilib.zig b/src/driver/Multilib.zig similarity index 100% rename from src/toolchains/Multilib.zig rename to src/driver/Multilib.zig diff --git a/src/lib.zig b/src/lib.zig index e19becb7..61f3f949 100644 --- a/src/lib.zig +++ b/src/lib.zig @@ -3,7 +3,7 @@ pub const Codegen = @import("Codegen_legacy.zig"); pub const CodeGen = @import("CodeGen.zig"); pub const Compilation = @import("Compilation.zig"); pub const Diagnostics = @import("Diagnostics.zig"); -pub const Driver = @import("Driver.zig"); +pub const Driver = @import("driver/Driver.zig"); pub const Interner = @import("Interner.zig"); pub const Ir = @import("Ir.zig"); pub const Object = @import("Object.zig"); diff --git a/src/main.zig b/src/main.zig index c77ab120..3659990a 100644 --- a/src/main.zig +++ b/src/main.zig @@ -3,7 +3,7 @@ const mem = std.mem; const Allocator = mem.Allocator; const process = std.process; const Compilation = @import("Compilation.zig"); -const Driver = @import("Driver.zig"); +const Driver = @import("driver/Driver.zig"); const target_util = @import("target.zig"); const Toolchain = @import("Toolchain.zig"); @@ -84,7 +84,7 @@ test { _ = @import("Codegen_legacy.zig"); _ = @import("Compilation.zig"); _ = @import("Diagnostics.zig"); - _ = @import("Distro.zig"); + _ = @import("driver/Distro.zig"); _ = @import("InitList.zig"); _ = @import("LangOpts.zig"); _ = @import("Parser.zig"); @@ -92,7 +92,7 @@ test { _ = @import("Preprocessor.zig"); _ = @import("Source.zig"); _ = @import("Tokenizer.zig"); - _ = @import("toolchains/GCCVersion.zig"); + _ = @import("driver/GCCVersion.zig"); _ = @import("Tree.zig"); _ = @import("Type.zig"); _ = @import("target.zig"); diff --git a/src/toolchains/Linux.zig b/src/toolchains/Linux.zig index 4be1ebf6..be92722f 100644 --- a/src/toolchains/Linux.zig +++ b/src/toolchains/Linux.zig @@ -1,10 +1,10 @@ const std = @import("std"); const mem = std.mem; const Compilation = @import("../Compilation.zig"); -const GCCDetector = @import("GCCDetector.zig"); +const GCCDetector = @import("../driver/GCCDetector.zig"); const Toolchain = @import("../Toolchain.zig"); -const Driver = @import("../Driver.zig"); -const Distro = @import("../Distro.zig"); +const Driver = @import("../driver/Driver.zig"); +const Distro = @import("../driver/Distro.zig"); const target_util = @import("../target.zig"); const system_defaults = @import("system_defaults"); From bd31e758501dc44a9df0b83da60af1bdf8cfd24a Mon Sep 17 00:00:00 2001 From: Evan Haas Date: Tue, 18 Jul 2023 09:41:37 -0700 Subject: [PATCH 24/50] Toolchain: add filesystem abstraction to ease testing --- src/Toolchain.zig | 24 ++++---- src/driver/Distro.zig | 5 +- src/driver/Filesystem.zig | 116 +++++++++++++++++++++++++++++++++++++ src/driver/GCCDetector.zig | 46 +++++++-------- src/driver/Multilib.zig | 9 +-- src/main.zig | 1 + src/toolchains/Linux.zig | 2 +- src/util.zig | 71 ----------------------- 8 files changed, 162 insertions(+), 112 deletions(-) create mode 100644 src/driver/Filesystem.zig diff --git a/src/Toolchain.zig b/src/Toolchain.zig index 853e9ea2..11e4c4f7 100644 --- a/src/Toolchain.zig +++ b/src/Toolchain.zig @@ -6,6 +6,7 @@ const mem = std.mem; const system_defaults = @import("system_defaults"); const Linux = @import("toolchains/Linux.zig"); const Multilib = @import("driver/Multilib.zig"); +const Filesystem = @import("driver/Filesystem.zig").Filesystem; const Toolchain = @This(); @@ -23,6 +24,7 @@ const Inner = union(enum) { } }; +filesystem: Filesystem = .{ .real = {} }, driver: *Driver, arena: mem.Allocator, @@ -98,7 +100,7 @@ pub fn getLinkerPath(self: *const Toolchain, buf: []u8) ![]const u8 { if (std.fs.path.dirname(path) == null) { path = self.getProgramPath(path, buf); } - if (util.canExecute(path)) { + if (self.filesystem.canExecute(path)) { return path; } } @@ -125,7 +127,7 @@ pub fn getLinkerPath(self: *const Toolchain, buf: []u8) ![]const u8 { } if (std.fs.path.isAbsolute(use_linker)) { - if (util.canExecute(use_linker)) { + if (self.filesystem.canExecute(use_linker)) { return use_linker; } } else { @@ -138,7 +140,7 @@ pub fn getLinkerPath(self: *const Toolchain, buf: []u8) ![]const u8 { } linker_name.appendSliceAssumeCapacity(use_linker); const linker_path = self.getProgramPath(linker_name.items, buf); - if (util.canExecute(linker_path)) { + if (self.filesystem.canExecute(linker_path)) { return linker_path; } } @@ -206,12 +208,12 @@ fn getProgramPath(tc: *const Toolchain, name: []const u8, buf: []u8) []const u8 const candidate = std.fs.path.join(fib.allocator(), &.{ program_path, tool_name }) catch continue; - if (util.canExecute(candidate) and candidate.len <= buf.len) { + if (tc.filesystem.canExecute(candidate) and candidate.len <= buf.len) { @memcpy(buf[0..candidate.len], candidate); return buf[0..candidate.len]; } } - return util.findProgramByName(tc.driver.comp.gpa, name, buf) orelse continue; + return tc.filesystem.findProgramByName(tc.driver.comp.gpa, name, buf) orelse continue; } @memcpy(buf[0..name.len], name); return buf[0..name.len]; @@ -234,17 +236,17 @@ pub fn getFilePath(tc: *const Toolchain, name: []const u8) ![]const u8 { // todo check compiler RT path const candidate = try std.fs.path.join(allocator, &.{ tc.driver.aro_dir, "..", name }); - if (util.exists(candidate)) { + if (tc.filesystem.exists(candidate)) { return tc.arena.dupe(u8, candidate); } fib.reset(); - if (searchPaths(allocator, sysroot, tc.library_paths.items, name)) |path| { + if (tc.searchPaths(allocator, sysroot, tc.library_paths.items, name)) |path| { return tc.arena.dupe(u8, path); } fib.reset(); - if (searchPaths(allocator, sysroot, tc.file_paths.items, name)) |path| { + if (tc.searchPaths(allocator, sysroot, tc.file_paths.items, name)) |path| { return try tc.arena.dupe(u8, path); } @@ -253,7 +255,7 @@ pub fn getFilePath(tc: *const Toolchain, name: []const u8) ![]const u8 { /// Search a list of `path_prefixes` for the existence `name` /// Assumes that `fba` is a fixed-buffer allocator, so does not free joined path candidates -fn searchPaths(fba: mem.Allocator, sysroot: []const u8, path_prefixes: []const []const u8, name: []const u8) ?[]const u8 { +fn searchPaths(tc: *const Toolchain, fba: mem.Allocator, sysroot: []const u8, path_prefixes: []const []const u8, name: []const u8) ?[]const u8 { for (path_prefixes) |path| { if (path.len == 0) continue; @@ -262,7 +264,7 @@ fn searchPaths(fba: mem.Allocator, sysroot: []const u8, path_prefixes: []const [ else std.fs.path.join(fba, &.{ path, name }) catch continue; - if (util.exists(candidate)) { + if (tc.filesystem.exists(candidate)) { return candidate; } } @@ -283,7 +285,7 @@ pub fn addPathIfExists(self: *Toolchain, components: []const []const u8, dest_ki const candidate = try std.fs.path.join(fib.allocator(), components); - if (util.exists(candidate)) { + if (self.filesystem.exists(candidate)) { const duped = try self.arena.dupe(u8, candidate); const dest = switch (dest_kind) { .library => &self.library_paths, diff --git a/src/driver/Distro.zig b/src/driver/Distro.zig index 69d39b55..8b134e3b 100644 --- a/src/driver/Distro.zig +++ b/src/driver/Distro.zig @@ -3,6 +3,7 @@ const std = @import("std"); const mem = std.mem; const util = @import("../util.zig"); +const Filesystem = @import("Filesystem.zig").Filesystem; const MAX_BYTES = 1024; // TODO: Can we assume 1024 bytes enough for the info we need? @@ -286,7 +287,7 @@ fn detectDebian() ?Tag { return scanForDebian(data); } -pub fn detect(target: std.Target) Tag { +pub fn detect(target: std.Target, fs: Filesystem) Tag { if (target.os.tag != .linux) return .unknown; if (detectOsRelease()) |tag| return tag; @@ -294,7 +295,7 @@ pub fn detect(target: std.Target) Tag { if (detectRedhat()) |tag| return tag; if (detectDebian()) |tag| return tag; - if (util.exists("/etc/gentoo-release")) return .gentoo; + if (fs.exists("/etc/gentoo-release")) return .gentoo; return .unknown; } diff --git a/src/driver/Filesystem.zig b/src/driver/Filesystem.zig new file mode 100644 index 00000000..fd49ab8f --- /dev/null +++ b/src/driver/Filesystem.zig @@ -0,0 +1,116 @@ +const std = @import("std"); +const mem = std.mem; +const builtin = @import("builtin"); +const is_windows = builtin.os.tag == .windows; + +fn findProgramByNameFake(allocator: std.mem.Allocator, name: []const u8, buf: []u8) ?[]const u8 { + _ = buf; + _ = name; + _ = allocator; + @panic("TODO"); +} + +fn canExecuteFake(paths: []const []const u8, path: []const u8) bool { + _ = path; + _ = paths; + @setCold(true); + @panic("TODO"); +} + +fn existsFake(paths: []const []const u8, path: []const u8) bool { + var buf: [std.fs.MAX_PATH_BYTES]u8 = undefined; + var fib = std.heap.FixedBufferAllocator.init(&buf); + const resolved = std.fs.path.resolvePosix(fib.allocator(), &.{path}) catch return false; + for (paths) |fakepath| { + if (mem.eql(u8, fakepath, resolved)) return true; + } + return false; +} + +fn canExecutePosix(path: []const u8) bool { + std.os.access(path, std.os.X_OK) catch return false; + // Todo: ensure path is not a directory + return true; +} + +/// TODO +fn canExecuteWindows(path: []const u8) bool { + _ = path; + return true; +} + +/// TODO +fn findProgramByNameWindows(allocator: std.mem.Allocator, name: []const u8, buf: []u8) ?[]const u8 { + _ = buf; + _ = name; + _ = allocator; + return null; +} + +/// TODO: does WASI need special handling? +fn findProgramByNamePosix(name: []const u8, buf: []u8) ?[]const u8 { + if (mem.indexOfScalar(u8, name, '/') != null) { + @memcpy(buf[0..name.len], name); + return buf[0..name.len]; + } + const path_env = std.os.getenvZ("PATH") orelse return null; + var fib = std.heap.FixedBufferAllocator.init(buf); + + var it = mem.tokenizeScalar(u8, path_env, ':'); + while (it.next()) |path_dir| { + defer fib.reset(); + const full_path = std.fs.path.join(fib.allocator(), &.{ path_dir, name }) catch continue; + if (canExecutePosix(full_path)) return full_path; + } + + return null; +} + +pub const Filesystem = union(enum) { + real: void, + fake: []const []const u8, + + pub fn exists(fs: Filesystem, path: []const u8) bool { + switch (fs) { + .real => { + std.os.access(path, std.os.F_OK) catch return false; + return true; + }, + .fake => |paths| return existsFake(paths, path), + } + } + + pub fn joinedExists(fs: Filesystem, parts: []const []const u8) bool { + var buf: [std.fs.MAX_PATH_BYTES]u8 = undefined; + var fib = std.heap.FixedBufferAllocator.init(&buf); + const joined = std.fs.path.join(fib.allocator(), parts) catch return false; + return fs.exists(joined); + } + + pub fn canExecute(fs: Filesystem, path: []const u8) bool { + return switch (fs) { + .real => if (is_windows) canExecuteWindows(path) else canExecutePosix(path), + .fake => |paths| canExecuteFake(paths, path), + }; + } + + /// Search for an executable named `name` using platform-specific logic + /// If it's found, write the full path to `buf` and return a slice of it + /// Otherwise retun null + pub fn findProgramByName(fs: Filesystem, allocator: std.mem.Allocator, name: []const u8, buf: []u8) ?[]const u8 { + std.debug.assert(name.len > 0); + return switch (fs) { + .real => if (is_windows) findProgramByNameWindows(allocator, name, buf) else findProgramByNamePosix(name, buf), + .fake => findProgramByNameFake(allocator, name, buf), + }; + } +}; + +test "Fake filesystem" { + const fs: Filesystem = .{ .fake = &.{ + "/usr/bin", + } }; + try std.testing.expect(fs.exists("/usr/bin")); + try std.testing.expect(fs.exists("/usr/bin/foo/..")); + try std.testing.expect(!fs.exists("/usr/bin/bar")); +} diff --git a/src/driver/GCCDetector.zig b/src/driver/GCCDetector.zig index fdb5afc5..f8270b8c 100644 --- a/src/driver/GCCDetector.zig +++ b/src/driver/GCCDetector.zig @@ -32,7 +32,7 @@ pub fn appendToolPath(self: *const GCCDetector, tc: *Toolchain) !void { fn addDefaultGCCPrefixes(prefixes: *PathPrefixes, tc: *const Toolchain) !void { const sysroot = tc.getSysroot(); const target = tc.getTarget(); - if (sysroot.len == 0 and target.os.tag == .linux and util.exists("/opt/rh")) { + if (sysroot.len == 0 and target.os.tag == .linux and tc.filesystem.exists("/opt/rh")) { prefixes.appendAssumeCapacity("/opt/rh/gcc-toolset-12/root/usr"); prefixes.appendAssumeCapacity("/opt/rh/gcc-toolset-11/root/usr"); prefixes.appendAssumeCapacity("/opt/rh/gcc-toolset-10/root/usr"); @@ -406,36 +406,36 @@ pub fn discover(self: *GCCDetector, tc: *Toolchain) !void { const v0 = GCCVersion.parse("0.0.0"); for (prefixes.constSlice()) |prefix| { - if (!util.exists(prefix)) continue; + if (!tc.filesystem.exists(prefix)) continue; for (candidate_lib_dirs.constSlice()) |suffix| { defer fib.reset(); const lib_dir = std.fs.path.join(fib.allocator(), &.{ prefix, suffix }) catch continue; - if (!util.exists(lib_dir)) continue; + if (!tc.filesystem.exists(lib_dir)) continue; - const gcc_dir_exists = util.joinedExists(&.{ lib_dir, "/gcc" }); - const gcc_cross_dir_exists = util.joinedExists(&.{ lib_dir, "/gcc-cross" }); + const gcc_dir_exists = tc.filesystem.joinedExists(&.{ lib_dir, "/gcc" }); + const gcc_cross_dir_exists = tc.filesystem.joinedExists(&.{ lib_dir, "/gcc-cross" }); - try self.scanLibDirForGCCTriple(tc.arena, target, lib_dir, triple_str, false, gcc_dir_exists, gcc_cross_dir_exists); + try self.scanLibDirForGCCTriple(tc, target, lib_dir, triple_str, false, gcc_dir_exists, gcc_cross_dir_exists); for (candidate_triple_aliases.constSlice()) |candidate| { - try self.scanLibDirForGCCTriple(tc.arena, target, lib_dir, candidate, false, gcc_dir_exists, gcc_cross_dir_exists); + try self.scanLibDirForGCCTriple(tc, target, lib_dir, candidate, false, gcc_dir_exists, gcc_cross_dir_exists); } } for (candidate_biarch_lib_dirs.constSlice()) |suffix| { const lib_dir = std.fs.path.join(fib.allocator(), &.{ prefix, suffix }) catch continue; - if (!util.exists(lib_dir)) continue; + if (!tc.filesystem.exists(lib_dir)) continue; - const gcc_dir_exists = util.joinedExists(&.{ lib_dir, "/gcc" }); - const gcc_cross_dir_exists = util.joinedExists(&.{ lib_dir, "/gcc-cross" }); + const gcc_dir_exists = tc.filesystem.joinedExists(&.{ lib_dir, "/gcc" }); + const gcc_cross_dir_exists = tc.filesystem.joinedExists(&.{ lib_dir, "/gcc-cross" }); for (candidate_biarch_triple_aliases.constSlice()) |candidate| { - try self.scanLibDirForGCCTriple(tc.arena, target, lib_dir, candidate, true, gcc_dir_exists, gcc_cross_dir_exists); + try self.scanLibDirForGCCTriple(tc, target, lib_dir, candidate, true, gcc_dir_exists, gcc_cross_dir_exists); } } if (self.version.order(v0) == .gt) break; } } -fn findBiarchMultilibs(self: *GCCDetector, result: *Multilib.Detected, target: std.Target, path: [2][]const u8, needs_biarch_suffix: bool) !bool { +fn findBiarchMultilibs(self: *GCCDetector, tc: *const Toolchain, result: *Multilib.Detected, target: std.Target, path: [2][]const u8, needs_biarch_suffix: bool) !bool { const suff64 = if (target.os.tag == .solaris) switch (target.cpu.arch) { .x86, .x86_64 => "/amd64", .sparc => "/sparcv9", @@ -459,11 +459,11 @@ fn findBiarchMultilibs(self: *GCCDetector, result: *Multilib.Detected, target: s }; const is_x32 = target.abi == .gnux32 or target.abi == .muslx32; const target_ptr_width = target.ptrBitWidth(); - const want: Want = if (target_ptr_width == 32 and multilib_filter.exists(alt_32)) + const want: Want = if (target_ptr_width == 32 and multilib_filter.exists(alt_32, tc.filesystem)) .want64 - else if (target_ptr_width == 64 and is_x32 and multilib_filter.exists(alt_x32)) + else if (target_ptr_width == 64 and is_x32 and multilib_filter.exists(alt_x32, tc.filesystem)) .want64 - else if (target_ptr_width == 64 and !is_x32 and multilib_filter.exists(alt_64)) + else if (target_ptr_width == 64 and !is_x32 and multilib_filter.exists(alt_64, tc.filesystem)) .want32 else if (target_ptr_width == 32) if (needs_biarch_suffix) .want64 else .want32 @@ -482,7 +482,7 @@ fn findBiarchMultilibs(self: *GCCDetector, result: *Multilib.Detected, target: s alt_32, alt_x32, }); - result.filter(multilib_filter); + result.filter(multilib_filter, tc.filesystem); var flags: Multilib.Flags = .{}; flags.appendAssumeCapacity(if (target_ptr_width == 64 and !is_x32) "+m64" else "-m64"); flags.appendAssumeCapacity(if (target_ptr_width == 32) "+m32" else "-m32"); @@ -494,7 +494,7 @@ fn findBiarchMultilibs(self: *GCCDetector, result: *Multilib.Detected, target: s return true; } -fn scanGCCForMultilibs(self: *GCCDetector, target: std.Target, path: [2][]const u8, needs_biarch_suffix: bool) !bool { +fn scanGCCForMultilibs(self: *GCCDetector, tc: *const Toolchain, target: std.Target, path: [2][]const u8, needs_biarch_suffix: bool) !bool { var detected: Multilib.Detected = .{}; if (target.cpu.arch == .csky) { // TODO @@ -506,7 +506,7 @@ fn scanGCCForMultilibs(self: *GCCDetector, target: std.Target, path: [2][]const // TODO } else if (target.cpu.arch == .avr) { // No multilibs - } else if (!try self.findBiarchMultilibs(&detected, target, path, needs_biarch_suffix)) { + } else if (!try self.findBiarchMultilibs(tc, &detected, target, path, needs_biarch_suffix)) { return false; } self.selected = detected.selected; @@ -516,7 +516,7 @@ fn scanGCCForMultilibs(self: *GCCDetector, target: std.Target, path: [2][]const fn scanLibDirForGCCTriple( self: *GCCDetector, - allocator: std.mem.Allocator, + tc: *const Toolchain, target: std.Target, lib_dir: []const u8, candidate_triple: []const u8, @@ -557,12 +557,12 @@ fn scanLibDirForGCCTriple( .gt => {}, } - if (!try self.scanGCCForMultilibs(target, .{ dir_name, version_text }, needs_biarch_suffix)) continue; + if (!try self.scanGCCForMultilibs(tc, target, .{ dir_name, version_text }, needs_biarch_suffix)) continue; self.version = candidate_version; - self.gcc_triple = try allocator.dupe(u8, candidate_triple); - self.install_path = try std.fs.path.join(allocator, &.{ lib_dir, lib_suffix, version_text }); - self.parent_lib_path = try std.fs.path.join(allocator, &.{ self.install_path, "..", "..", ".." }); + self.gcc_triple = try tc.arena.dupe(u8, candidate_triple); + self.install_path = try std.fs.path.join(tc.arena, &.{ lib_dir, lib_suffix, version_text }); + self.parent_lib_path = try std.fs.path.join(tc.arena, &.{ self.install_path, "..", "..", ".." }); self.is_valid = true; } } diff --git a/src/driver/Multilib.zig b/src/driver/Multilib.zig index b1962386..4548b5e0 100644 --- a/src/driver/Multilib.zig +++ b/src/driver/Multilib.zig @@ -1,5 +1,6 @@ const std = @import("std"); const util = @import("../util.zig"); +const Filesystem = @import("Filesystem.zig").Filesystem; pub const Flags = std.BoundedArray([]const u8, 6); @@ -8,10 +9,10 @@ pub const Detected = struct { selected: Multilib = .{}, biarch_sibling: ?Multilib = null, - pub fn filter(self: *Detected, multilib_filter: Filter) void { + pub fn filter(self: *Detected, multilib_filter: Filter, fs: Filesystem) void { var found_count: usize = 0; for (self.multilibs.constSlice()) |multilib| { - if (multilib_filter.exists(multilib)) { + if (multilib_filter.exists(multilib, fs)) { self.multilibs.set(found_count, multilib); found_count += 1; } @@ -43,8 +44,8 @@ pub const Detected = struct { pub const Filter = struct { base: [2][]const u8, file: []const u8, - pub fn exists(self: Filter, m: Multilib) bool { - return util.joinedExists(&.{ self.base[0], self.base[1], m.gcc_suffix, self.file }); + pub fn exists(self: Filter, m: Multilib, fs: Filesystem) bool { + return fs.joinedExists(&.{ self.base[0], self.base[1], m.gcc_suffix, self.file }); } }; diff --git a/src/main.zig b/src/main.zig index 3659990a..2b2a4674 100644 --- a/src/main.zig +++ b/src/main.zig @@ -85,6 +85,7 @@ test { _ = @import("Compilation.zig"); _ = @import("Diagnostics.zig"); _ = @import("driver/Distro.zig"); + _ = @import("driver/Filesystem.zig"); _ = @import("InitList.zig"); _ = @import("LangOpts.zig"); _ = @import("Parser.zig"); diff --git a/src/toolchains/Linux.zig b/src/toolchains/Linux.zig index be92722f..1eb4124a 100644 --- a/src/toolchains/Linux.zig +++ b/src/toolchains/Linux.zig @@ -15,7 +15,7 @@ extra_opts: std.ArrayListUnmanaged([]const u8) = .{}, gcc_detector: GCCDetector = .{}, pub fn discover(self: *Linux, tc: *Toolchain) !void { - self.distro = Distro.detect(tc.getTarget()); + self.distro = Distro.detect(tc.getTarget(), tc.filesystem); try self.gcc_detector.discover(tc); tc.selected_multilib = self.gcc_detector.selected; diff --git a/src/util.zig b/src/util.zig index 7f33a24f..3f92f36a 100644 --- a/src/util.zig +++ b/src/util.zig @@ -81,74 +81,3 @@ pub fn errorDescription(err: anyerror) []const u8 { else => @errorName(err), }; } - -fn canExecutePosix(path: []const u8) bool { - std.os.access(path, std.os.X_OK) catch return false; - // Todo: ensure path is not a directory - return true; -} - -/// TODO -fn canExecuteWindows(path: []const u8) bool { - _ = path; - return true; -} - -pub fn canExecute(path: []const u8) bool { - if (is_windows) { - return canExecuteWindows(path); - } else { - return canExecutePosix(path); - } -} - -pub fn exists(path: []const u8) bool { - std.os.access(path, std.os.F_OK) catch return false; - return true; -} - -pub fn joinedExists(parts: []const []const u8) bool { - var buf: [std.fs.MAX_PATH_BYTES]u8 = undefined; - var fib = std.heap.FixedBufferAllocator.init(&buf); - const joined = std.fs.path.join(fib.allocator(), parts) catch return false; - return exists(joined); -} - -/// TODO -fn findProgramByNameWindows(allocator: std.mem.Allocator, name: []const u8, buf: []u8) ?[]const u8 { - _ = buf; - _ = name; - _ = allocator; - return null; -} - -/// TODO: does WASI need special handling? -fn findProgramByNamePosix(name: []const u8, buf: []u8) ?[]const u8 { - if (mem.indexOfScalar(u8, name, '/') != null) { - @memcpy(buf[0..name.len], name); - return buf[0..name.len]; - } - const path_env = std.os.getenvZ("PATH") orelse return null; - var fib = std.heap.FixedBufferAllocator.init(buf); - - var it = mem.tokenizeScalar(u8, path_env, ':'); - while (it.next()) |path_dir| { - defer fib.reset(); - const full_path = std.fs.path.join(fib.allocator(), &.{ path_dir, name }) catch continue; - if (canExecute(full_path)) return full_path; - } - - return null; -} - -/// Search for an executable named `name` using platform-specific logic -/// If it's found, write the full path to `buf` and return a slice of it -/// Otherwise retun null -pub fn findProgramByName(allocator: std.mem.Allocator, name: []const u8, buf: []u8) ?[]const u8 { - std.debug.assert(name.len > 0); - if (is_windows) { - return findProgramByNameWindows(allocator, name, buf); - } else { - return findProgramByNamePosix(name, buf); - } -} From 33e53eb8742403b156ec911d055870f249a0fec4 Mon Sep 17 00:00:00 2001 From: Evan Haas Date: Thu, 20 Jul 2023 23:51:34 -0700 Subject: [PATCH 25/50] Toolchain: add crt begin/end files --- build.zig | 2 + src/Diagnostics.zig | 5 +++ src/Toolchain.zig | 37 ++++++++++++++++++ src/driver/Driver.zig | 8 ++++ src/driver_test.zig | 21 ++++++++++ src/main.zig | 1 + src/toolchains/Linux.zig | 84 +++++++++++++++++++++++++++------------- 7 files changed, 132 insertions(+), 26 deletions(-) create mode 100644 src/driver_test.zig diff --git a/build.zig b/build.zig index 81e28b83..96d13b4b 100644 --- a/build.zig +++ b/build.zig @@ -44,6 +44,7 @@ pub fn build(b: *Build) !void { const default_linker = b.option([]const u8, "default-linker", "Default linker aro will use if none is supplied via -fuse-ld") orelse "ld"; const default_sysroot = b.option([]const u8, "default-sysroot", "Default to all compiler invocations for --sysroot=.") orelse ""; const gcc_install_prefix = b.option([]const u8, "gcc-install-prefix", "Directory where gcc is installed.") orelse ""; + const default_rtlib = b.option([]const u8, "default-rtlib", "Default compiler runtime library if --rtlib is not specified") orelse ""; const test_all_allocation_failures = b.option(bool, "test-all-allocation-failures", "Test all allocation failures") orelse false; const link_libc = b.option(bool, "link-libc", "Force self-hosted compiler to link libc") orelse (mode != .Debug); const tracy = b.option([]const u8, "tracy", "Enable Tracy integration. Supply path to Tracy source"); @@ -77,6 +78,7 @@ pub fn build(b: *Build) !void { system_defaults.addOption([]const u8, "linker", default_linker); system_defaults.addOption([]const u8, "sysroot", default_sysroot); system_defaults.addOption([]const u8, "gcc_install_prefix", gcc_install_prefix); + system_defaults.addOption([]const u8, "rtlib", default_rtlib); exe_options.addOption(bool, "enable_tracy", tracy != null); exe_options.addOption(bool, "enable_tracy_callstack", tracy_callstack); diff --git a/src/Diagnostics.zig b/src/Diagnostics.zig index 883fa381..3eb42d38 100644 --- a/src/Diagnostics.zig +++ b/src/Diagnostics.zig @@ -2357,6 +2357,11 @@ const messages = struct { const kind = .off; const opt = "fuse-ld-path"; }; + pub const invalid_rtlib = struct { + const msg = "invalid runtime library name '{s}'"; + const kind = .@"error"; + const extra = .str; + }; }; list: std.ArrayListUnmanaged(Message) = .{}, diff --git a/src/Toolchain.zig b/src/Toolchain.zig index 11e4c4f7..e1538672 100644 --- a/src/Toolchain.zig +++ b/src/Toolchain.zig @@ -4,6 +4,7 @@ const Compilation = @import("Compilation.zig"); const util = @import("util.zig"); const mem = std.mem; const system_defaults = @import("system_defaults"); +const target_util = @import("target.zig"); const Linux = @import("toolchains/Linux.zig"); const Multilib = @import("driver/Multilib.zig"); const Filesystem = @import("driver/Filesystem.zig").Filesystem; @@ -12,6 +13,17 @@ const Toolchain = @This(); pub const PathList = std.ArrayListUnmanaged([]const u8); +pub const RuntimeLibKind = enum { + compiler_rt, + libgcc, +}; + +pub const FileKind = enum { + object, + static, + shared, +}; + const Inner = union(enum) { linux: Linux, unknown: void, @@ -314,3 +326,28 @@ pub fn buildLinkerArgs(self: *Toolchain, argv: *std.ArrayList([]const u8)) !void .unknown => @panic("This toolchain does not support linking yet"), }; } + +fn getDefaultRuntimeLibKind(self: *const Toolchain) RuntimeLibKind { + if (self.getTarget().isAndroid()) { + return .compiler_rt; + } + return .libgcc; +} + +pub fn getRuntimeLibType(self: *const Toolchain) RuntimeLibKind { + const libname = self.driver.rtlib orelse system_defaults.rtlib; + if (mem.eql(u8, libname, "compiler-rt")) + return .compiler_rt + else if (mem.eql(u8, libname, "libgcc")) + return .libgcc + else + return self.getDefaultRuntimeLibKind(); +} + +/// TODO +pub fn getCompilerRt(tc: *const Toolchain, component: []const u8, file_kind: FileKind) ![]const u8 { + _ = file_kind; + _ = component; + _ = tc; + return ""; +} diff --git a/src/driver/Driver.zig b/src/driver/Driver.zig index f024f49f..22ab8efb 100644 --- a/src/driver/Driver.zig +++ b/src/driver/Driver.zig @@ -55,6 +55,7 @@ nostdlib: bool = false, pie: ?bool = null, rdynamic: bool = false, relocatable: bool = false, +rtlib: ?[]const u8 = null, shared: bool = false, static: bool = false, static_pie: bool = false, @@ -113,6 +114,7 @@ pub const usage = \\ Select which C compiler to emulate (default clang) \\ -o Write output to \\ -pedantic Warn on language extensions + \\ --rtlib= Compiler runtime library to use (libgcc or compiler-rt) \\ -std= Specify language standard \\ -S, --assemble Only run preprocess and compilation steps \\ --sysroot= Use dir as the logical root directory for headers and libraries (not fully implemented) @@ -309,6 +311,12 @@ pub fn parseArgs( d.sysroot = sysroot; } else if (mem.eql(u8, arg, "-pedantic")) { d.comp.diag.options.pedantic = .warning; + } else if (option(arg, "--rtlib=")) |rtlib| { + if (mem.eql(u8, rtlib, "compiler-rt") or mem.eql(u8, rtlib, "libgcc") or mem.eql(u8, rtlib, "platform")) { + d.rtlib = rtlib; + } else { + try d.comp.diag.add(.{ .tag = .invalid_rtlib, .extra = .{ .str = rtlib } }, &.{}); + } } else if (option(arg, "-Werror=")) |err_name| { try d.comp.diag.set(err_name, .@"error"); } else if (mem.eql(u8, arg, "-Wno-fatal-errors")) { diff --git a/src/driver_test.zig b/src/driver_test.zig new file mode 100644 index 00000000..1046ddbf --- /dev/null +++ b/src/driver_test.zig @@ -0,0 +1,21 @@ +const std = @import("std"); +const Driver = @import("driver/Driver.zig"); +const Compilation = @import("Compilation.zig"); +const Toolchain = @import("Toolchain.zig"); + +test Driver { + var arena_instance = std.heap.ArenaAllocator.init(std.testing.allocator); + defer arena_instance.deinit(); + const arena = arena_instance.allocator(); + + var comp = Compilation.init(std.testing.allocator); + defer comp.deinit(); + + var driver: Driver = .{ .comp = &comp }; + defer driver.deinit(); + + var toolchain: Toolchain = .{ .driver = &driver, .arena = arena }; + defer toolchain.deinit(); + + try std.testing.expect(1 == 1); +} diff --git a/src/main.zig b/src/main.zig index 2b2a4674..03930284 100644 --- a/src/main.zig +++ b/src/main.zig @@ -86,6 +86,7 @@ test { _ = @import("Diagnostics.zig"); _ = @import("driver/Distro.zig"); _ = @import("driver/Filesystem.zig"); + _ = @import("driver_test.zig"); _ = @import("InitList.zig"); _ = @import("LangOpts.zig"); _ = @import("Parser.zig"); diff --git a/src/toolchains/Linux.zig b/src/toolchains/Linux.zig index 1eb4124a..c1ed8d60 100644 --- a/src/toolchains/Linux.zig +++ b/src/toolchains/Linux.zig @@ -162,23 +162,21 @@ pub fn getDefaultLinker(self: *const Linux, target: std.Target) []const u8 { pub fn buildLinkerArgs(self: *const Linux, tc: *const Toolchain, argv: *std.ArrayList([]const u8)) Compilation.Error!void { const d = tc.driver; + const target = tc.getTarget(); const is_pie = self.getPIE(d); const is_static_pie = try self.getStaticPIE(d); const is_static = self.getStatic(d); - const is_android = d.comp.target.isAndroid(); - const is_iamcu = d.comp.target.os.tag == .elfiamcu; + const is_android = target.isAndroid(); + const is_iamcu = target.os.tag == .elfiamcu; + const is_ve = target.cpu.arch == .ve; + const has_crt_begin_end_files = target.abi != .none; // TODO: clang checks for MIPS vendor if (is_pie) { try argv.append("-pie"); } if (is_static_pie) { - try argv.ensureUnusedCapacity(5); - argv.appendAssumeCapacity("-static"); - argv.appendAssumeCapacity("-pie"); - argv.appendAssumeCapacity("--no-dynamic-linker"); - argv.appendAssumeCapacity("-z"); - argv.appendAssumeCapacity("text"); + try argv.appendSlice(&.{ "-static", "-pie", "--no-dynamic-linker", "-z", "text" }); } if (d.rdynamic) { @@ -189,16 +187,12 @@ pub fn buildLinkerArgs(self: *const Linux, tc: *const Toolchain, argv: *std.Arra try argv.append("-s"); } - try argv.ensureUnusedCapacity(self.extra_opts.items.len); - argv.appendSliceAssumeCapacity(self.extra_opts.items); - + try argv.appendSlice(self.extra_opts.items); try argv.append("--eh-frame-hdr"); // Todo: Driver should parse `-EL`/`-EB` for arm to set endianness for arm targets if (target_util.ldEmulationOption(d.comp.target, null)) |emulation| { - try argv.ensureUnusedCapacity(2); - argv.appendAssumeCapacity("-m"); - argv.appendAssumeCapacity(emulation); + try argv.appendSlice(&.{ "-m", emulation }); } else { try d.err("Unknown target triple"); return; @@ -219,18 +213,14 @@ pub fn buildLinkerArgs(self: *const Linux, tc: *const Toolchain, argv: *std.Arra const dynamic_linker = d.comp.target.standardDynamicLinkerPath(); // todo: check for --dyld-prefix if (dynamic_linker.get()) |path| { - try argv.ensureUnusedCapacity(2); - argv.appendAssumeCapacity("-dynamic-linker"); - argv.appendAssumeCapacity(try tc.arena.dupe(u8, path)); + try argv.appendSlice(&.{ "-dynamic-linker", try tc.arena.dupe(u8, path) }); } else { try d.err("Could not find dynamic linker path"); } } } - try argv.ensureUnusedCapacity(2); - argv.appendAssumeCapacity("-o"); - argv.appendAssumeCapacity(d.output_name orelse "a.out"); + try argv.appendSlice(&.{ "-o", d.output_name orelse "a.out" }); if (!d.nostdlib and !d.nostartfiles and !d.relocatable) { if (!is_android and !is_iamcu) { @@ -245,6 +235,32 @@ pub fn buildLinkerArgs(self: *const Linux, tc: *const Toolchain, argv: *std.Arra } try argv.append(try tc.getFilePath("crti.o")); } + if (is_ve) { + try argv.appendSlice(&.{ "-z", "max-page-size=0x4000000" }); + } + + if (is_iamcu) { + try argv.append(try tc.getFilePath("crt0.o")); + } else if (has_crt_begin_end_files) { + var path: []const u8 = ""; + if (tc.getRuntimeLibType() == .compiler_rt and !is_android) { + const crt_begin = try tc.getCompilerRt("crtbegin", .object); + if (tc.filesystem.exists(crt_begin)) { + path = crt_begin; + } + } + if (path.len == 0) { + const crt_begin = if (tc.driver.shared) + if (is_android) "crtbegin_so.o" else "crtbeginS.o" + else if (is_static) + if (is_android) "crtbegin_static.o" else "crtbeginT.o" + else if (is_pie or is_static_pie) + if (is_android) "crtbegin_dynamic.o" else "crtbeginS.o" + else if (is_android) "crtbegin_dynamic.o" else "crtbegin.o"; + path = try tc.getFilePath(crt_begin); + } + try argv.append(path); + } } // TODO add -L opts @@ -270,16 +286,32 @@ pub fn buildLinkerArgs(self: *const Linux, tc: *const Toolchain, argv: *std.Arra } if (is_static or is_static_pie) { try argv.append("--end-group"); - } else {} + } else { + // Add runtime libs + } if (is_iamcu) { - try argv.ensureUnusedCapacity(3); - argv.appendAssumeCapacity("--as-needed"); - argv.appendAssumeCapacity("-lsoftfp"); - argv.appendAssumeCapacity("--no-as-needed"); + try argv.appendSlice(&.{ "--as-needed", "-lsoftfp", "--no-as-needed" }); } } if (!d.nostartfiles and !is_iamcu) { - // TODO: handle CRT begin/end files + if (has_crt_begin_end_files) { + var path: []const u8 = ""; + if (tc.getRuntimeLibType() == .compiler_rt and !is_android) { + const crt_end = try tc.getCompilerRt("crtend", .object); + if (tc.filesystem.exists(crt_end)) { + path = crt_end; + } + } + if (path.len == 0) { + const crt_end = if (d.shared) + if (is_android) "crtend_so.o" else "crtendS.o" + else if (is_pie or is_static_pie) + if (is_android) "crtend_android.o" else "crtendS.o" + else if (is_android) "crtend_android.o" else "crtend.o"; + path = try tc.getFilePath(crt_end); + } + try argv.append(path); + } if (!is_android) { try argv.append(try tc.getFilePath("crtn.o")); } From fddb561bddce077a7e83d2473217c3b4fef6dac7 Mon Sep 17 00:00:00 2001 From: Evan Haas Date: Wed, 26 Jul 2023 17:20:53 -0700 Subject: [PATCH 26/50] Toolchain: add unwind lib args --- build.zig | 3 + src/Diagnostics.zig | 14 ++++ src/Toolchain.zig | 135 ++++++++++++++++++++++++++++++++++++++- src/driver/Driver.zig | 20 ++++++ src/target.zig | 12 ++++ src/toolchains/Linux.zig | 8 ++- 6 files changed, 188 insertions(+), 4 deletions(-) diff --git a/build.zig b/build.zig index 96d13b4b..21a9b97c 100644 --- a/build.zig +++ b/build.zig @@ -45,6 +45,8 @@ pub fn build(b: *Build) !void { const default_sysroot = b.option([]const u8, "default-sysroot", "Default to all compiler invocations for --sysroot=.") orelse ""; const gcc_install_prefix = b.option([]const u8, "gcc-install-prefix", "Directory where gcc is installed.") orelse ""; const default_rtlib = b.option([]const u8, "default-rtlib", "Default compiler runtime library if --rtlib is not specified") orelse ""; + const default_unwindlib = b.option([]const u8, "default-unwindlib", "Default unwind library to use (\"none\" \"libgcc\" or \"libunwind\", empty to match runtime library.)") orelse + if (std.mem.eql(u8, default_rtlib, "libgcc")) "libgcc" else ""; const test_all_allocation_failures = b.option(bool, "test-all-allocation-failures", "Test all allocation failures") orelse false; const link_libc = b.option(bool, "link-libc", "Force self-hosted compiler to link libc") orelse (mode != .Debug); const tracy = b.option([]const u8, "tracy", "Enable Tracy integration. Supply path to Tracy source"); @@ -79,6 +81,7 @@ pub fn build(b: *Build) !void { system_defaults.addOption([]const u8, "sysroot", default_sysroot); system_defaults.addOption([]const u8, "gcc_install_prefix", gcc_install_prefix); system_defaults.addOption([]const u8, "rtlib", default_rtlib); + system_defaults.addOption([]const u8, "unwindlib", default_unwindlib); exe_options.addOption(bool, "enable_tracy", tracy != null); exe_options.addOption(bool, "enable_tracy_callstack", tracy_callstack); diff --git a/src/Diagnostics.zig b/src/Diagnostics.zig index 3eb42d38..36807828 100644 --- a/src/Diagnostics.zig +++ b/src/Diagnostics.zig @@ -2362,6 +2362,20 @@ const messages = struct { const kind = .@"error"; const extra = .str; }; + pub const unsupported_rtlib_gcc = struct { + const msg = "unsupported runtime library 'libgcc' for platform '{s}'"; + const kind = .@"error"; + const extra = .str; + }; + pub const invalid_unwindlib = struct { + const msg = "invalid unwind library name '{s}'"; + const kind = .@"error"; + const extra = .str; + }; + pub const incompatible_unwindlib = struct { + const msg = "--rtlib=libgcc requires --unwindlib=libgcc"; + const kind = .@"error"; + }; }; list: std.ArrayListUnmanaged(Message) = .{}, diff --git a/src/Toolchain.zig b/src/Toolchain.zig index e1538672..9afc14f8 100644 --- a/src/Toolchain.zig +++ b/src/Toolchain.zig @@ -24,6 +24,18 @@ pub const FileKind = enum { shared, }; +pub const LibGCCKind = enum { + unspecified, + static, + shared, +}; + +pub const UnwindLibKind = enum { + none, + compiler_rt, + libgcc, +}; + const Inner = union(enum) { linux: Linux, unknown: void, @@ -334,7 +346,7 @@ fn getDefaultRuntimeLibKind(self: *const Toolchain) RuntimeLibKind { return .libgcc; } -pub fn getRuntimeLibType(self: *const Toolchain) RuntimeLibKind { +pub fn getRuntimeLibKind(self: *const Toolchain) RuntimeLibKind { const libname = self.driver.rtlib orelse system_defaults.rtlib; if (mem.eql(u8, libname, "compiler-rt")) return .compiler_rt @@ -351,3 +363,124 @@ pub fn getCompilerRt(tc: *const Toolchain, component: []const u8, file_kind: Fil _ = tc; return ""; } + +fn getLibGCCKind(tc: *const Toolchain) LibGCCKind { + const target = tc.getTarget(); + if (tc.driver.static_libgcc or tc.driver.static or tc.driver.static_pie or target.isAndroid()) { + return .static; + } + if (tc.driver.shared_libgcc) { + return .shared; + } + return .unspecified; +} + +fn getUnwindLibKind(tc: *const Toolchain) !UnwindLibKind { + const libname = tc.driver.unwindlib orelse system_defaults.unwindlib; + if (libname.len == 0 or mem.eql(u8, libname, "platform")) { + switch (tc.getRuntimeLibKind()) { + .compiler_rt => { + const target = tc.getTarget(); + if (target.isAndroid() or target.os.tag == .aix) { + return .compiler_rt; + } else { + return .none; + } + }, + .libgcc => return .libgcc, + } + } else if (mem.eql(u8, libname, "none")) { + return .none; + } else if (mem.eql(u8, libname, "libgcc")) { + return .libgcc; + } else if (mem.eql(u8, libname, "libunwind")) { + if (tc.getRuntimeLibKind() == .libgcc) { + try tc.driver.comp.diag.add(.{ .tag = .incompatible_unwindlib }, &.{}); + } + return .compiler_rt; + } else { + unreachable; + } +} + +fn getAsNeededOption(is_solaris: bool, needed: bool) []const u8 { + if (is_solaris) { + return if (needed) "-zignore" else "-zrecord"; + } else { + return if (needed) "--as-needed" else "--no-as-needed"; + } +} + +fn addUnwindLibrary(tc: *const Toolchain, argv: *std.ArrayList([]const u8)) !void { + const unw = try tc.getUnwindLibKind(); + const target = tc.getTarget(); + if ((target.isAndroid() and unw == .libgcc) or + target.os.tag == .elfiamcu or + target.ofmt == .wasm or + target_util.isWindowsMSVCEnvironment(target) or + unw == .none) return; + + const lgk = tc.getLibGCCKind(); + const as_needed = lgk == .unspecified and !target.isAndroid() and !target_util.isCygwinMinGW(target) and target.os.tag != .aix; + if (as_needed) { + try argv.append(getAsNeededOption(target.os.tag == .solaris, true)); + } + switch (unw) { + .none => return, + .libgcc => if (lgk == .static) try argv.append("-lgcc_eh") else try argv.append("-lgcc_s"), + .compiler_rt => if (target.os.tag == .aix) { + if (lgk != .static) { + try argv.append("-lunwind"); + } + } else if (lgk == .static) { + try argv.append("-l:libunwind.a"); + } else if (lgk == .shared) { + if (target_util.isCygwinMinGW(target)) { + try argv.append("-l:libunwind.dll.a"); + } else { + try argv.append("-l:libunwind.so"); + } + } else { + try argv.append("-lunwind"); + }, + } + + if (as_needed) { + try argv.append(getAsNeededOption(target.os.tag == .solaris, false)); + } +} + +fn addLibGCC(tc: *const Toolchain, argv: *std.ArrayList([]const u8)) !void { + const libgcc_kind = tc.getLibGCCKind(); + if (libgcc_kind == .static or libgcc_kind == .unspecified) { + try argv.append("-lgcc"); + } + try tc.addUnwindLibrary(argv); + if (libgcc_kind == .shared) { + try argv.append("-lgcc"); + } +} + +pub fn addRuntimeLibs(tc: *const Toolchain, argv: *std.ArrayList([]const u8)) !void { + const target = tc.getTarget(); + const rlt = tc.getRuntimeLibKind(); + switch (rlt) { + .compiler_rt => { + // TODO + }, + .libgcc => { + if (target_util.isKnownWindowsMSVCEnvironment(target)) { + const rtlib_str = tc.driver.rtlib orelse system_defaults.rtlib; + if (!mem.eql(u8, rtlib_str, "platform")) { + try tc.driver.comp.diag.add(.{ .tag = .unsupported_rtlib_gcc, .extra = .{ .str = "MSVC" } }, &.{}); + } + } else { + try tc.addLibGCC(argv); + } + }, + } + + if (target.isAndroid() and !tc.driver.static and !tc.driver.static_pie) { + try argv.append("-ldl"); + } +} diff --git a/src/driver/Driver.zig b/src/driver/Driver.zig index 22ab8efb..9e24ea0a 100644 --- a/src/driver/Driver.zig +++ b/src/driver/Driver.zig @@ -57,9 +57,12 @@ rdynamic: bool = false, relocatable: bool = false, rtlib: ?[]const u8 = null, shared: bool = false, +shared_libgcc: bool = false, static: bool = false, +static_libgcc: bool = false, static_pie: bool = false, strip: bool = false, +unwindlib: ?[]const u8 = null, pub fn deinit(d: *Driver) void { for (d.link_objects.items[d.link_objects.items.len - d.temp_file_count ..]) |obj| { @@ -138,8 +141,11 @@ pub const usage = \\ -rdynamic Pass the flag -export-dynamic to the ELF linker, on targets that support it. \\ -s Remove all symbol table and relocation information from the executable. \\ -shared Produce a shared object which can then be linked with other objects to form an executable. + \\ -shared-libgcc On systems that provide libgcc as a shared library, force the use of the shared version \\ -static On systems that support dynamic linking, this overrides -pie and prevents linking with the shared libraries. + \\ -static-libgcc On systems that provide libgcc as a shared library, force the use of the static version \\ -static-pie Produce a static position independent executable on targets that support it. + \\ --unwindlib= Unwind library to use ("none", "libgcc", or "libunwind") If not specified, will match runtime library \\ \\Debug options: \\ --verbose-ast Dump produced AST to stdout @@ -358,8 +364,12 @@ pub fn parseArgs( d.relocatable = true; } else if (mem.eql(u8, arg, "-shared")) { d.shared = true; + } else if (mem.eql(u8, arg, "-shared-libgcc")) { + d.shared_libgcc = true; } else if (mem.eql(u8, arg, "-static")) { d.static = true; + } else if (mem.eql(u8, arg, "-static-libgcc")) { + d.static_libgcc = true; } else if (mem.eql(u8, arg, "-static-pie")) { d.static_pie = true; } else if (mem.eql(u8, arg, "-pie")) { @@ -378,6 +388,16 @@ pub fn parseArgs( d.nostdlib = true; } else if (mem.eql(u8, arg, "-nostartfiles")) { d.nostartfiles = true; + } else if (option(arg, "--unwindlib=")) |unwindlib| { + const valid_unwindlibs: [5][]const u8 = .{ "", "none", "platform", "libunwind", "libgcc" }; + for (valid_unwindlibs) |name| { + if (mem.eql(u8, name, unwindlib)) { + d.unwindlib = unwindlib; + break; + } + } else { + try d.comp.diag.add(.{ .tag = .invalid_unwindlib, .extra = .{ .str = unwindlib } }, &.{}); + } } else { try d.comp.diag.add(.{ .tag = .cli_unknown_arg, .extra = .{ .str = arg } }, &.{}); } diff --git a/src/target.zig b/src/target.zig index a4aeea12..d82bd346 100644 --- a/src/target.zig +++ b/src/target.zig @@ -336,6 +336,18 @@ pub fn isLP64(target: std.Target) bool { return target.c_type_bit_size(.int) == 32 and target.ptrBitWidth() == 64; } +pub fn isKnownWindowsMSVCEnvironment(target: std.Target) bool { + return target.os.tag == .windows and target.abi == .msvc; +} + +pub fn isWindowsMSVCEnvironment(target: std.Target) bool { + return target.os.tag == .windows and (target.abi == .msvc or target.abi == .none); +} + +pub fn isCygwinMinGW(target: std.Target) bool { + return target.os.tag == .windows and (target.abi == .gnu or target.abi == .cygnus); +} + pub fn builtinEnabled(target: std.Target, enabled_for: TargetSet) bool { var copy = enabled_for; var it = copy.iterator(); diff --git a/src/toolchains/Linux.zig b/src/toolchains/Linux.zig index c1ed8d60..8d99b382 100644 --- a/src/toolchains/Linux.zig +++ b/src/toolchains/Linux.zig @@ -243,7 +243,7 @@ pub fn buildLinkerArgs(self: *const Linux, tc: *const Toolchain, argv: *std.Arra try argv.append(try tc.getFilePath("crt0.o")); } else if (has_crt_begin_end_files) { var path: []const u8 = ""; - if (tc.getRuntimeLibType() == .compiler_rt and !is_android) { + if (tc.getRuntimeLibKind() == .compiler_rt and !is_android) { const crt_begin = try tc.getCompilerRt("crtbegin", .object); if (tc.filesystem.exists(crt_begin)) { path = crt_begin; @@ -277,6 +277,8 @@ pub fn buildLinkerArgs(self: *const Linux, tc: *const Toolchain, argv: *std.Arra if (is_static or is_static_pie) { try argv.append("--start-group"); } + try tc.addRuntimeLibs(argv); + // TODO: add pthread if needed if (!d.nolibc) { try argv.append("-lc"); @@ -287,7 +289,7 @@ pub fn buildLinkerArgs(self: *const Linux, tc: *const Toolchain, argv: *std.Arra if (is_static or is_static_pie) { try argv.append("--end-group"); } else { - // Add runtime libs + try tc.addRuntimeLibs(argv); } if (is_iamcu) { try argv.appendSlice(&.{ "--as-needed", "-lsoftfp", "--no-as-needed" }); @@ -296,7 +298,7 @@ pub fn buildLinkerArgs(self: *const Linux, tc: *const Toolchain, argv: *std.Arra if (!d.nostartfiles and !is_iamcu) { if (has_crt_begin_end_files) { var path: []const u8 = ""; - if (tc.getRuntimeLibType() == .compiler_rt and !is_android) { + if (tc.getRuntimeLibKind() == .compiler_rt and !is_android) { const crt_end = try tc.getCompilerRt("crtend", .object); if (tc.filesystem.exists(crt_end)) { path = crt_end; From f02ecb7a0ea7c235ede226d51d8ceb7dfdcd6d4d Mon Sep 17 00:00:00 2001 From: Evan Haas Date: Thu, 27 Jul 2023 22:09:52 -0700 Subject: [PATCH 27/50] Toolchain: use tc for self arg for consistency --- src/Toolchain.zig | 110 +++++++++++++++++++++++----------------------- 1 file changed, 55 insertions(+), 55 deletions(-) diff --git a/src/Toolchain.zig b/src/Toolchain.zig index 9afc14f8..6526a949 100644 --- a/src/Toolchain.zig +++ b/src/Toolchain.zig @@ -65,21 +65,21 @@ selected_multilib: Multilib = .{}, inner: Inner = .{ .unknown = {} }, -pub fn getTarget(self: *const Toolchain) std.Target { - return self.driver.comp.target; +pub fn getTarget(tc: *const Toolchain) std.Target { + return tc.driver.comp.target; } -fn getDefaultLinker(self: *const Toolchain) []const u8 { - return switch (self.inner) { - .linux => |linux| linux.getDefaultLinker(self.getTarget()), +fn getDefaultLinker(tc: *const Toolchain) []const u8 { + return switch (tc.inner) { + .linux => |linux| linux.getDefaultLinker(tc.getTarget()), .unknown => "ld", }; } /// Call this after driver has finished parsing command line arguments to find the toolchain -pub fn discover(self: *Toolchain) !void { - const target = self.getTarget(); - self.inner = switch (target.os.tag) { +pub fn discover(tc: *Toolchain) !void { + const target = tc.getTarget(); + tc.inner = switch (target.os.tag) { .elfiamcu, .linux, => if (target.cpu.arch == .hexagon) @@ -94,41 +94,41 @@ pub fn discover(self: *Toolchain) !void { .{ .linux = .{} }, else => .{ .unknown = {} }, // TODO }; - return switch (self.inner) { - .linux => |*linux| linux.discover(self), + return switch (tc.inner) { + .linux => |*linux| linux.discover(tc), .unknown => {}, }; } -pub fn deinit(self: *Toolchain) void { - const gpa = self.driver.comp.gpa; - self.inner.deinit(gpa); +pub fn deinit(tc: *Toolchain) void { + const gpa = tc.driver.comp.gpa; + tc.inner.deinit(gpa); - self.library_paths.deinit(gpa); - self.file_paths.deinit(gpa); - self.program_paths.deinit(gpa); + tc.library_paths.deinit(gpa); + tc.file_paths.deinit(gpa); + tc.program_paths.deinit(gpa); } /// Write linker path to `buf` and return a slice of it -pub fn getLinkerPath(self: *const Toolchain, buf: []u8) ![]const u8 { +pub fn getLinkerPath(tc: *const Toolchain, buf: []u8) ![]const u8 { // --ld-path= takes precedence over -fuse-ld= and specifies the executable // name. -B, COMPILER_PATH and PATH are consulted if the value does not // contain a path component separator. // -fuse-ld=lld can be used with --ld-path= to indicate that the binary // that --ld-path= points to is lld. - const use_linker = self.driver.use_linker orelse system_defaults.linker; + const use_linker = tc.driver.use_linker orelse system_defaults.linker; - if (self.driver.linker_path) |ld_path| { + if (tc.driver.linker_path) |ld_path| { var path = ld_path; if (path.len > 0) { if (std.fs.path.dirname(path) == null) { - path = self.getProgramPath(path, buf); + path = tc.getProgramPath(path, buf); } - if (self.filesystem.canExecute(path)) { + if (tc.filesystem.canExecute(path)) { return path; } } - return self.driver.fatal( + return tc.driver.fatal( "invalid linker name in argument '--ld-path={s}'", .{path}, ); @@ -137,9 +137,9 @@ pub fn getLinkerPath(self: *const Toolchain, buf: []u8) ![]const u8 { // If we're passed -fuse-ld= with no argument, or with the argument ld, // then use whatever the default system linker is. if (use_linker.len == 0 or mem.eql(u8, use_linker, "ld")) { - const default = self.getDefaultLinker(); + const default = tc.getDefaultLinker(); if (std.fs.path.isAbsolute(default)) return default; - return self.getProgramPath(default, buf); + return tc.getProgramPath(default, buf); } // Extending -fuse-ld= to an absolute or relative path is unexpected. Checking @@ -147,36 +147,36 @@ pub fn getLinkerPath(self: *const Toolchain, buf: []u8) ![]const u8 { // to a relative path is surprising. This is more complex due to priorities // among -B, COMPILER_PATH and PATH. --ld-path= should be used instead. if (mem.indexOfScalar(u8, use_linker, '/') != null) { - try self.driver.comp.diag.add(.{ .tag = .fuse_ld_path }, &.{}); + try tc.driver.comp.diag.add(.{ .tag = .fuse_ld_path }, &.{}); } if (std.fs.path.isAbsolute(use_linker)) { - if (self.filesystem.canExecute(use_linker)) { + if (tc.filesystem.canExecute(use_linker)) { return use_linker; } } else { - var linker_name = try std.ArrayList(u8).initCapacity(self.driver.comp.gpa, 5 + use_linker.len); // "ld64." ++ use_linker + var linker_name = try std.ArrayList(u8).initCapacity(tc.driver.comp.gpa, 5 + use_linker.len); // "ld64." ++ use_linker defer linker_name.deinit(); - if (self.getTarget().isDarwin()) { + if (tc.getTarget().isDarwin()) { linker_name.appendSliceAssumeCapacity("ld64."); } else { linker_name.appendSliceAssumeCapacity("ld."); } linker_name.appendSliceAssumeCapacity(use_linker); - const linker_path = self.getProgramPath(linker_name.items, buf); - if (self.filesystem.canExecute(linker_path)) { + const linker_path = tc.getProgramPath(linker_name.items, buf); + if (tc.filesystem.canExecute(linker_path)) { return linker_path; } } - if (self.driver.use_linker) |linker| { - return self.driver.fatal( + if (tc.driver.use_linker) |linker| { + return tc.driver.fatal( "invalid linker name in argument '-fuse-ld={s}'", .{linker}, ); } - const default_linker = self.getDefaultLinker(); - return self.getProgramPath(default_linker, buf); + const default_linker = tc.getDefaultLinker(); + return tc.getProgramPath(default_linker, buf); } const TargetSpecificToolName = std.BoundedArray(u8, 64); @@ -303,57 +303,57 @@ const PathKind = enum { /// Join `components` into a path. If the path exists, dupe it into the toolchain arena and /// add it to the specified path list. -pub fn addPathIfExists(self: *Toolchain, components: []const []const u8, dest_kind: PathKind) !void { +pub fn addPathIfExists(tc: *Toolchain, components: []const []const u8, dest_kind: PathKind) !void { var path_buf: [std.fs.MAX_PATH_BYTES]u8 = undefined; var fib = std.heap.FixedBufferAllocator.init(&path_buf); const candidate = try std.fs.path.join(fib.allocator(), components); - if (self.filesystem.exists(candidate)) { - const duped = try self.arena.dupe(u8, candidate); + if (tc.filesystem.exists(candidate)) { + const duped = try tc.arena.dupe(u8, candidate); const dest = switch (dest_kind) { - .library => &self.library_paths, - .file => &self.file_paths, - .program => &self.program_paths, + .library => &tc.library_paths, + .file => &tc.file_paths, + .program => &tc.program_paths, }; - try dest.append(self.driver.comp.gpa, duped); + try dest.append(tc.driver.comp.gpa, duped); } } /// Join `components` using the toolchain arena and add the resulting path to `dest_kind`. Does not check /// whether the path actually exists -pub fn addPathFromComponents(self: *Toolchain, components: []const []const u8, dest_kind: PathKind) !void { - const full_path = try std.fs.path.join(self.arena, components); +pub fn addPathFromComponents(tc: *Toolchain, components: []const []const u8, dest_kind: PathKind) !void { + const full_path = try std.fs.path.join(tc.arena, components); const dest = switch (dest_kind) { - .library => &self.library_paths, - .file => &self.file_paths, - .program => &self.program_paths, + .library => &tc.library_paths, + .file => &tc.file_paths, + .program => &tc.program_paths, }; - try dest.append(self.driver.comp.gpa, full_path); + try dest.append(tc.driver.comp.gpa, full_path); } -pub fn buildLinkerArgs(self: *Toolchain, argv: *std.ArrayList([]const u8)) !void { - return switch (self.inner) { - .linux => |*linux| linux.buildLinkerArgs(self, argv), +pub fn buildLinkerArgs(tc: *Toolchain, argv: *std.ArrayList([]const u8)) !void { + return switch (tc.inner) { + .linux => |*linux| linux.buildLinkerArgs(tc, argv), .unknown => @panic("This toolchain does not support linking yet"), }; } -fn getDefaultRuntimeLibKind(self: *const Toolchain) RuntimeLibKind { - if (self.getTarget().isAndroid()) { +fn getDefaultRuntimeLibKind(tc: *const Toolchain) RuntimeLibKind { + if (tc.getTarget().isAndroid()) { return .compiler_rt; } return .libgcc; } -pub fn getRuntimeLibKind(self: *const Toolchain) RuntimeLibKind { - const libname = self.driver.rtlib orelse system_defaults.rtlib; +pub fn getRuntimeLibKind(tc: *const Toolchain) RuntimeLibKind { + const libname = tc.driver.rtlib orelse system_defaults.rtlib; if (mem.eql(u8, libname, "compiler-rt")) return .compiler_rt else if (mem.eql(u8, libname, "libgcc")) return .libgcc else - return self.getDefaultRuntimeLibKind(); + return tc.getDefaultRuntimeLibKind(); } /// TODO From 760d679642fb857ddb9164cebae3a99500175f5e Mon Sep 17 00:00:00 2001 From: Evan Haas Date: Thu, 27 Jul 2023 22:12:13 -0700 Subject: [PATCH 28/50] Toolchain: Add a doc comment for buildLinkerArgs --- src/Toolchain.zig | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Toolchain.zig b/src/Toolchain.zig index 6526a949..d518a142 100644 --- a/src/Toolchain.zig +++ b/src/Toolchain.zig @@ -332,6 +332,8 @@ pub fn addPathFromComponents(tc: *Toolchain, components: []const []const u8, des try dest.append(tc.driver.comp.gpa, full_path); } +/// Add linker args to `argv`. Does not add path to linker executable as first item; that must be handled separately +/// Items added to `argv` will be string literals or owned by `tc.arena` so they must not be individually freed pub fn buildLinkerArgs(tc: *Toolchain, argv: *std.ArrayList([]const u8)) !void { return switch (tc.inner) { .linux => |*linux| linux.buildLinkerArgs(tc, argv), From 2ea408c87ef08a256922a1f496a60b9ecb457fec Mon Sep 17 00:00:00 2001 From: Evan Haas Date: Thu, 27 Jul 2023 22:39:54 -0700 Subject: [PATCH 29/50] target: implement get64BitArchVariant --- src/target.zig | 71 ++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 69 insertions(+), 2 deletions(-) diff --git a/src/target.zig b/src/target.zig index d82bd346..a5ee8bae 100644 --- a/src/target.zig +++ b/src/target.zig @@ -498,8 +498,75 @@ pub fn get32BitArchVariant(target: std.Target) ?std.Target { } pub fn get64BitArchVariant(target: std.Target) ?std.Target { - // TODO: do the right thing - return get32BitArchVariant(target); + var copy = target; + switch (target.cpu.arch) { + .arc, + .avr, + .csky, + .dxil, + .hexagon, + .kalimba, + .lanai, + .m68k, + .msp430, + .r600, + .shave, + .sparcel, + .spu_2, + .tce, + .tcele, + .xcore, + .xtensa, + => return null, + + .aarch64, + .aarch64_be, + .amdgcn, + .bpfeb, + .bpfel, + .le64, + .amdil64, + .nvptx64, + .wasm64, + .hsail64, + .spir64, + .spirv64, + .renderscript64, + .loongarch64, + .mips64, + .mips64el, + .powerpc64, + .powerpc64le, + .riscv64, + .s390x, + .sparc64, + .ve, + .x86_64, + => {}, // Already 64 bit + + .aarch64_32 => copy.cpu.arch = .aarch64, + .amdil => copy.cpu.arch = .amdil64, + .arm => copy.cpu.arch = .aarch64, + .armeb => copy.cpu.arch = .aarch64_be, + .hsail => copy.cpu.arch = .hsail64, + .le32 => copy.cpu.arch = .le64, + .loongarch32 => copy.cpu.arch = .loongarch64, + .mips => copy.cpu.arch = .mips64, + .mipsel => copy.cpu.arch = .mips64el, + .nvptx => copy.cpu.arch = .nvptx64, + .powerpc => copy.cpu.arch = .powerpc64, + .powerpcle => copy.cpu.arch = .powerpc64le, + .renderscript32 => copy.cpu.arch = .renderscript64, + .riscv32 => copy.cpu.arch = .riscv64, + .sparc => copy.cpu.arch = .sparc64, + .spir => copy.cpu.arch = .spir64, + .spirv32 => copy.cpu.arch = .spirv64, + .thumb => copy.cpu.arch = .aarch64, + .thumbeb => copy.cpu.arch = .aarch64_be, + .wasm32 => copy.cpu.arch = .wasm64, + .x86 => copy.cpu.arch = .x86_64, + } + return copy; } /// Adapted from Zig's src/codegen/llvm.zig From 05ec9bf6f2c92f0d4afa43e4642249f14305cc22 Mon Sep 17 00:00:00 2001 From: Evan Haas Date: Fri, 28 Jul 2023 22:23:29 -0700 Subject: [PATCH 30/50] Driver: dump linker args with quoted+escaped arguments Also handle errors properly --- src/driver/Driver.zig | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/driver/Driver.zig b/src/driver/Driver.zig index 9e24ea0a..067c27f0 100644 --- a/src/driver/Driver.zig +++ b/src/driver/Driver.zig @@ -634,6 +634,15 @@ fn processSource( } } +fn dumpLinkerArgs(items: []const []const u8) !void { + const stdout = std.io.getStdOut().writer(); + for (items, 0..) |item, i| { + if (i > 0) try stdout.writeByte(' '); + try stdout.print("\"{}\"", .{std.zig.fmtEscapes(item)}); + } + try stdout.writeByte('\n'); +} + pub fn invokeLinker(d: *Driver, tc: *Toolchain) !void { var argv = std.ArrayList([]const u8).init(d.comp.gpa); defer argv.deinit(); @@ -645,8 +654,9 @@ pub fn invokeLinker(d: *Driver, tc: *Toolchain) !void { try tc.buildLinkerArgs(&argv); if (d.verbose_linker_args) { - const stdout = std.io.getStdOut().writer(); - try stdout.print("{s}\n", .{argv.items}); + dumpLinkerArgs(argv.items) catch |er| { + return d.fatal("unable to dump linker args: {s}", .{util.errorDescription(er)}); + }; } var child = std.ChildProcess.init(argv.items, d.comp.gpa); // TODO handle better From eab7ab557a9912869476ca383a061032067c10b1 Mon Sep 17 00:00:00 2001 From: Evan Haas Date: Fri, 28 Jul 2023 22:28:49 -0700 Subject: [PATCH 31/50] Driver: only perform toolchain discovery if linking Add an explicit uninitialized state so that discovery is only performed once but can be delayed until actually needed, so that errors during discovery (which is only needed for linking) do not affect earlier stages of the pipeline. --- src/Toolchain.zig | 10 ++++++++-- src/driver/Driver.zig | 4 +++- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/src/Toolchain.zig b/src/Toolchain.zig index d518a142..1ba8b1ee 100644 --- a/src/Toolchain.zig +++ b/src/Toolchain.zig @@ -37,13 +37,14 @@ pub const UnwindLibKind = enum { }; const Inner = union(enum) { + uninitialized, linux: Linux, unknown: void, fn deinit(self: *Inner, allocator: mem.Allocator) void { switch (self.*) { .linux => |*linux| linux.deinit(allocator), - .unknown => {}, + .uninitialized, .unknown => {}, } } }; @@ -63,7 +64,7 @@ program_paths: PathList = .{}, selected_multilib: Multilib = .{}, -inner: Inner = .{ .unknown = {} }, +inner: Inner = .{ .uninitialized = {} }, pub fn getTarget(tc: *const Toolchain) std.Target { return tc.driver.comp.target; @@ -71,6 +72,7 @@ pub fn getTarget(tc: *const Toolchain) std.Target { fn getDefaultLinker(tc: *const Toolchain) []const u8 { return switch (tc.inner) { + .uninitialized => unreachable, .linux => |linux| linux.getDefaultLinker(tc.getTarget()), .unknown => "ld", }; @@ -78,6 +80,8 @@ fn getDefaultLinker(tc: *const Toolchain) []const u8 { /// Call this after driver has finished parsing command line arguments to find the toolchain pub fn discover(tc: *Toolchain) !void { + if (tc.inner != .uninitialized) return; + const target = tc.getTarget(); tc.inner = switch (target.os.tag) { .elfiamcu, @@ -95,6 +99,7 @@ pub fn discover(tc: *Toolchain) !void { else => .{ .unknown = {} }, // TODO }; return switch (tc.inner) { + .uninitialized => unreachable, .linux => |*linux| linux.discover(tc), .unknown => {}, }; @@ -336,6 +341,7 @@ pub fn addPathFromComponents(tc: *Toolchain, components: []const []const u8, des /// Items added to `argv` will be string literals or owned by `tc.arena` so they must not be individually freed pub fn buildLinkerArgs(tc: *Toolchain, argv: *std.ArrayList([]const u8)) !void { return switch (tc.inner) { + .uninitialized => unreachable, .linux => |*linux| linux.buildLinkerArgs(tc, argv), .unknown => @panic("This toolchain does not support linking yet"), }; diff --git a/src/driver/Driver.zig b/src/driver/Driver.zig index 067c27f0..061f4bbe 100644 --- a/src/driver/Driver.zig +++ b/src/driver/Driver.zig @@ -452,9 +452,9 @@ pub fn main(d: *Driver, tc: *Toolchain, args: []const []const u8) !void { const std_out = std.io.getStdOut().writer(); if (try parseArgs(d, std_out, macro_buf.writer(), args)) return; - try tc.discover(); const linking = !(d.only_preprocess or d.only_syntax or d.only_compile or d.only_preprocess_and_compile); + if (d.inputs.items.len == 0) { return d.fatal("no input files", .{}); } else if (d.inputs.items.len != 1 and d.output_name != null and !linking) { @@ -644,6 +644,8 @@ fn dumpLinkerArgs(items: []const []const u8) !void { } pub fn invokeLinker(d: *Driver, tc: *Toolchain) !void { + try tc.discover(); + var argv = std.ArrayList([]const u8).init(d.comp.gpa); defer argv.deinit(); From d6e7ca5bd8dbb4312e475d2c7f2105b60ff4958b Mon Sep 17 00:00:00 2001 From: Evan Haas Date: Sun, 13 Aug 2023 00:11:30 -0700 Subject: [PATCH 32/50] Toolchain: cleanup BoundedArray usage Use default initializer instead of init; use constSlice if possible --- src/Toolchain.zig | 8 ++++---- src/driver/GCCDetector.zig | 10 +++++----- src/driver/Multilib.zig | 1 - 3 files changed, 9 insertions(+), 10 deletions(-) diff --git a/src/Toolchain.zig b/src/Toolchain.zig index 1ba8b1ee..a09b4b1e 100644 --- a/src/Toolchain.zig +++ b/src/Toolchain.zig @@ -191,11 +191,11 @@ const TargetSpecificToolName = std.BoundedArray(u8, 64); /// with GCC's. /// For example the Zig target `arm-freestanding-eabi` would need the `arm-none-eabi` tools fn possibleProgramNames(raw_triple: ?[]const u8, name: []const u8, target_specific: *TargetSpecificToolName) std.BoundedArray([]const u8, 2) { - var possible_names = std.BoundedArray([]const u8, 2).init(0) catch unreachable; + var possible_names: std.BoundedArray([]const u8, 2) = .{}; if (raw_triple) |triple| { const w = target_specific.writer(); if (w.print("{s}-{s}", .{ triple, name })) { - possible_names.appendAssumeCapacity(target_specific.slice()); + possible_names.appendAssumeCapacity(target_specific.constSlice()); } else |_| {} } possible_names.appendAssumeCapacity(name); @@ -228,10 +228,10 @@ fn getProgramPath(tc: *const Toolchain, name: []const u8, buf: []u8) []const u8 var path_buf: [std.fs.MAX_PATH_BYTES]u8 = undefined; var fib = std.heap.FixedBufferAllocator.init(&path_buf); - var tool_specific_name = TargetSpecificToolName.init(0) catch unreachable; + var tool_specific_name: TargetSpecificToolName = .{}; const possible_names = possibleProgramNames(tc.driver.raw_target_triple, name, &tool_specific_name); - for (possible_names.slice()) |tool_name| { + for (possible_names.constSlice()) |tool_name| { for (tc.program_paths.items) |program_path| { defer fib.reset(); diff --git a/src/driver/GCCDetector.zig b/src/driver/GCCDetector.zig index f8270b8c..286539ab 100644 --- a/src/driver/GCCDetector.zig +++ b/src/driver/GCCDetector.zig @@ -368,10 +368,10 @@ pub fn discover(self: *GCCDetector, tc: *Toolchain) !void { const bivariant_target = if (target.ptrBitWidth() == 32) target_util.get64BitArchVariant(target) else target_util.get32BitArchVariant(target); _ = bivariant_target; - var candidate_lib_dirs = PathPrefixes.init(0) catch unreachable; - var candidate_biarch_lib_dirs = PathPrefixes.init(0) catch unreachable; - var candidate_triple_aliases = PathPrefixes.init(0) catch unreachable; - var candidate_biarch_triple_aliases = PathPrefixes.init(0) catch unreachable; + var candidate_lib_dirs: PathPrefixes = .{}; + var candidate_biarch_lib_dirs: PathPrefixes = .{}; + var candidate_triple_aliases: PathPrefixes = .{}; + var candidate_biarch_triple_aliases: PathPrefixes = .{}; try self.collectLibDirsAndTriples(tc, &candidate_lib_dirs, &candidate_biarch_lib_dirs, &candidate_triple_aliases, &candidate_biarch_triple_aliases); var target_buf: std.BoundedArray(u8, 32) = .{}; @@ -383,7 +383,7 @@ pub fn discover(self: *GCCDetector, tc: *Toolchain) !void { // if (TargetTriple.str() != BiarchTriple.str()) // BiarchTripleAliases.push_back(BiarchTriple.str()); - var prefixes = PathPrefixes.init(0) catch unreachable; + var prefixes: PathPrefixes = .{}; const gcc_toolchain_dir = gccToolchainDir(tc); if (gcc_toolchain_dir.len != 0) { const adjusted = if (gcc_toolchain_dir[gcc_toolchain_dir.len - 1] == '/') diff --git a/src/driver/Multilib.zig b/src/driver/Multilib.zig index 4548b5e0..35c92387 100644 --- a/src/driver/Multilib.zig +++ b/src/driver/Multilib.zig @@ -59,7 +59,6 @@ priority: u32 = 0, pub fn init(gcc_suffix: []const u8, os_suffix: []const u8, flags: []const []const u8) Multilib { var self: Multilib = .{ - .flags = Flags.init(0) catch unreachable, .gcc_suffix = gcc_suffix, .os_suffix = os_suffix, }; From bd14d9454d63801df2e8676a054efbcfb5225fc0 Mon Sep 17 00:00:00 2001 From: Evan Haas Date: Sun, 13 Aug 2023 15:56:36 -0700 Subject: [PATCH 33/50] Multilib: return an error instead of panicking if too many multilibs found --- src/driver/GCCDetector.zig | 5 +---- src/driver/Multilib.zig | 4 ++-- src/main.zig | 5 +++++ 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/driver/GCCDetector.zig b/src/driver/GCCDetector.zig index 286539ab..5515468c 100644 --- a/src/driver/GCCDetector.zig +++ b/src/driver/GCCDetector.zig @@ -488,10 +488,7 @@ fn findBiarchMultilibs(self: *GCCDetector, tc: *const Toolchain, result: *Multil flags.appendAssumeCapacity(if (target_ptr_width == 32) "+m32" else "-m32"); flags.appendAssumeCapacity(if (target_ptr_width == 64 and is_x32) "+mx32" else "-mx32"); - const success = result.select(flags); - if (!success) return false; - - return true; + return result.select(flags); } fn scanGCCForMultilibs(self: *GCCDetector, tc: *const Toolchain, target: std.Target, path: [2][]const u8, needs_biarch_suffix: bool) !bool { diff --git a/src/driver/Multilib.zig b/src/driver/Multilib.zig index 35c92387..56cf755b 100644 --- a/src/driver/Multilib.zig +++ b/src/driver/Multilib.zig @@ -20,7 +20,7 @@ pub const Detected = struct { self.multilibs.resize(found_count) catch unreachable; } - pub fn select(self: *Detected, flags: Flags) bool { + pub fn select(self: *Detected, flags: Flags) !bool { var filtered: std.BoundedArray(Multilib, 4) = .{}; for (self.multilibs.constSlice()) |multilib| { for (multilib.flags.constSlice()) |multilib_flag| { @@ -37,7 +37,7 @@ pub const Detected = struct { self.selected = filtered.get(0); return true; } - @panic("Got too many multilibs"); + return error.TooManyMultilibs; } }; diff --git a/src/main.zig b/src/main.zig index 03930284..ea659257 100644 --- a/src/main.zig +++ b/src/main.zig @@ -73,6 +73,11 @@ pub fn main() !u8 { if (fast_exit) std.process.exit(1); return 1; }, + error.TooManyMultilibs => { + std.debug.print("found more than one multilib with the same priority\n", .{}); + if (fast_exit) std.process.exit(1); + return 1; + }, else => |err| return err, }; if (fast_exit) std.process.exit(@intFromBool(comp.diag.errors != 0)); From fcb31406a2ae014364119dbb2d949d534628a33a Mon Sep 17 00:00:00 2001 From: Evan Haas Date: Sun, 13 Aug 2023 18:59:57 -0700 Subject: [PATCH 34/50] GCCDetector: Fill out remaining collectLibDirsAndTriples prongs --- src/driver/GCCDetector.zig | 105 ++++++++++++++++++++++++++----------- 1 file changed, 73 insertions(+), 32 deletions(-) diff --git a/src/driver/GCCDetector.zig b/src/driver/GCCDetector.zig index 5515468c..f14b923c 100644 --- a/src/driver/GCCDetector.zig +++ b/src/driver/GCCDetector.zig @@ -140,69 +140,49 @@ fn collectLibDirsAndTriples( const MIPSN32ELTriples: [2][]const u8 = .{ "mips64el-linux-gnuabin32", "mipsisa64r6el-linux-gnuabin32" }; const MSP430LibDirs: [1][]const u8 = .{"/lib"}; - _ = MSP430LibDirs; const MSP430Triples: [1][]const u8 = .{"msp430-elf"}; - _ = MSP430Triples; const PPCLibDirs: [2][]const u8 = .{ "/lib32", "/lib" }; - _ = PPCLibDirs; const PPCTriples: [5][]const u8 = .{ "powerpc-linux-gnu", "powerpc-unknown-linux-gnu", "powerpc-linux-gnuspe", // On 32-bit PowerPC systems running SUSE Linux, gcc is configured as a // 64-bit compiler which defaults to "-m32", hence "powerpc64-suse-linux". "powerpc64-suse-linux", "powerpc-montavista-linuxspe", }; - _ = PPCTriples; const PPCLELibDirs: [2][]const u8 = .{ "/lib32", "/lib" }; - _ = PPCLELibDirs; const PPCLETriples: [3][]const u8 = .{ "powerpcle-linux-gnu", "powerpcle-unknown-linux-gnu", "powerpcle-linux-musl" }; - _ = PPCLETriples; const PPC64LibDirs: [2][]const u8 = .{ "/lib64", "/lib" }; - _ = PPC64LibDirs; const PPC64Triples: [4][]const u8 = .{ "powerpc64-linux-gnu", "powerpc64-unknown-linux-gnu", "powerpc64-suse-linux", "ppc64-redhat-linux", }; - _ = PPC64Triples; const PPC64LELibDirs: [2][]const u8 = .{ "/lib64", "/lib" }; - _ = PPC64LELibDirs; const PPC64LETriples: [5][]const u8 = .{ "powerpc64le-linux-gnu", "powerpc64le-unknown-linux-gnu", "powerpc64le-none-linux-gnu", "powerpc64le-suse-linux", "ppc64le-redhat-linux", }; - _ = PPC64LETriples; const RISCV32LibDirs: [2][]const u8 = .{ "/lib32", "/lib" }; - _ = RISCV32LibDirs; const RISCV32Triples: [3][]const u8 = .{ "riscv32-unknown-linux-gnu", "riscv32-linux-gnu", "riscv32-unknown-elf" }; - _ = RISCV32Triples; const RISCV64LibDirs: [2][]const u8 = .{ "/lib64", "/lib" }; - _ = RISCV64LibDirs; const RISCV64Triples: [3][]const u8 = .{ "riscv64-unknown-linux-gnu", "riscv64-linux-gnu", "riscv64-unknown-elf", }; - _ = RISCV64Triples; const SPARCv8LibDirs: [2][]const u8 = .{ "/lib32", "/lib" }; - _ = SPARCv8LibDirs; const SPARCv8Triples: [2][]const u8 = .{ "sparc-linux-gnu", "sparcv8-linux-gnu" }; - _ = SPARCv8Triples; const SPARCv9LibDirs: [2][]const u8 = .{ "/lib64", "/lib" }; - _ = SPARCv9LibDirs; const SPARCv9Triples: [2][]const u8 = .{ "sparc64-linux-gnu", "sparcv9-linux-gnu" }; - _ = SPARCv9Triples; const SystemZLibDirs: [2][]const u8 = .{ "/lib64", "/lib" }; - _ = SystemZLibDirs; const SystemZTriples: [5][]const u8 = .{ "s390x-linux-gnu", "s390x-unknown-linux-gnu", "s390x-ibm-linux-gnu", "s390x-suse-linux", "s390x-redhat-linux", }; - _ = SystemZTriples; const target = tc.getTarget(); if (target.os.tag == .solaris) { // TODO @@ -344,18 +324,79 @@ fn collectLibDirsAndTriples( biarch_libdirs.appendSliceAssumeCapacity(&MIPSN32ELLibDirs); biarch_triple_aliases.appendSliceAssumeCapacity(&MIPSN32ELTriples); }, - .mips64 => @panic("TODO"), - .mips64el => @panic("TODO"), - .msp430 => @panic("TODO"), - .powerpc => @panic("TODO"), - .powerpcle => @panic("TODO"), - .powerpc64 => @panic("TODO"), - .powerpc64le => @panic("TODO"), - .riscv32 => @panic("TODO"), - .riscv64 => @panic("TODO"), - .sparc, .sparcel => @panic("TODO"), - .sparc64 => @panic("TODO"), - .s390x => @panic("TODO"), + .mips64 => { + lib_dirs.appendSliceAssumeCapacity(&MIPS64LibDirs); + triple_aliases.appendSliceAssumeCapacity(&MIPS64Triples); + biarch_libdirs.appendSliceAssumeCapacity(&MIPSLibDirs); + biarch_triple_aliases.appendSliceAssumeCapacity(&MIPSTriples); + biarch_libdirs.appendSliceAssumeCapacity(&MIPSN32LibDirs); + biarch_triple_aliases.appendSliceAssumeCapacity(&MIPSN32Triples); + }, + .mips64el => { + lib_dirs.appendSliceAssumeCapacity(&MIPS64ELLibDirs); + triple_aliases.appendSliceAssumeCapacity(&MIPS64ELTriples); + biarch_libdirs.appendSliceAssumeCapacity(&MIPSELLibDirs); + biarch_triple_aliases.appendSliceAssumeCapacity(&MIPSELTriples); + biarch_libdirs.appendSliceAssumeCapacity(&MIPSN32ELLibDirs); + biarch_triple_aliases.appendSliceAssumeCapacity(&MIPSN32ELTriples); + biarch_triple_aliases.appendSliceAssumeCapacity(&MIPSTriples); + }, + .msp430 => { + lib_dirs.appendSliceAssumeCapacity(&MSP430LibDirs); + triple_aliases.appendSliceAssumeCapacity(&MSP430Triples); + }, + .powerpc => { + lib_dirs.appendSliceAssumeCapacity(&PPCLibDirs); + triple_aliases.appendSliceAssumeCapacity(&PPCTriples); + biarch_libdirs.appendSliceAssumeCapacity(&PPC64LibDirs); + biarch_triple_aliases.appendSliceAssumeCapacity(&PPC64Triples); + }, + .powerpcle => { + lib_dirs.appendSliceAssumeCapacity(&PPCLELibDirs); + triple_aliases.appendSliceAssumeCapacity(&PPCLETriples); + biarch_libdirs.appendSliceAssumeCapacity(&PPC64LELibDirs); + biarch_triple_aliases.appendSliceAssumeCapacity(&PPC64LETriples); + }, + .powerpc64 => { + lib_dirs.appendSliceAssumeCapacity(&PPC64LibDirs); + triple_aliases.appendSliceAssumeCapacity(&PPC64Triples); + biarch_libdirs.appendSliceAssumeCapacity(&PPCLibDirs); + biarch_triple_aliases.appendSliceAssumeCapacity(&PPCTriples); + }, + .powerpc64le => { + lib_dirs.appendSliceAssumeCapacity(&PPC64LELibDirs); + triple_aliases.appendSliceAssumeCapacity(&PPC64LETriples); + biarch_libdirs.appendSliceAssumeCapacity(&PPCLELibDirs); + biarch_triple_aliases.appendSliceAssumeCapacity(&PPCLETriples); + }, + .riscv32 => { + lib_dirs.appendSliceAssumeCapacity(&RISCV32LibDirs); + triple_aliases.appendSliceAssumeCapacity(&RISCV32Triples); + biarch_libdirs.appendSliceAssumeCapacity(&RISCV64LibDirs); + biarch_triple_aliases.appendSliceAssumeCapacity(&RISCV64Triples); + }, + .riscv64 => { + lib_dirs.appendSliceAssumeCapacity(&RISCV64LibDirs); + triple_aliases.appendSliceAssumeCapacity(&RISCV64Triples); + biarch_libdirs.appendSliceAssumeCapacity(&RISCV32LibDirs); + biarch_triple_aliases.appendSliceAssumeCapacity(&RISCV32Triples); + }, + .sparc, .sparcel => { + lib_dirs.appendSliceAssumeCapacity(&SPARCv8LibDirs); + triple_aliases.appendSliceAssumeCapacity(&SPARCv8Triples); + biarch_libdirs.appendSliceAssumeCapacity(&SPARCv9LibDirs); + biarch_triple_aliases.appendSliceAssumeCapacity(&SPARCv9Triples); + }, + .sparc64 => { + lib_dirs.appendSliceAssumeCapacity(&SPARCv9LibDirs); + triple_aliases.appendSliceAssumeCapacity(&SPARCv9Triples); + biarch_libdirs.appendSliceAssumeCapacity(&SPARCv8LibDirs); + biarch_triple_aliases.appendSliceAssumeCapacity(&SPARCv8Triples); + }, + .s390x => { + lib_dirs.appendSliceAssumeCapacity(&SystemZLibDirs); + triple_aliases.appendSliceAssumeCapacity(&SystemZTriples); + }, else => {}, } } From a464cdcc966e9c23fb115f0d2a8436be7fdb5951 Mon Sep 17 00:00:00 2001 From: Evan Haas Date: Sun, 13 Aug 2023 22:25:44 -0700 Subject: [PATCH 35/50] GCCDetector: make use of biarch_variant_target --- src/driver/GCCDetector.zig | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/src/driver/GCCDetector.zig b/src/driver/GCCDetector.zig index f14b923c..38c08614 100644 --- a/src/driver/GCCDetector.zig +++ b/src/driver/GCCDetector.zig @@ -60,15 +60,12 @@ fn addDefaultGCCPrefixes(prefixes: *PathPrefixes, tc: *const Toolchain) !void { const PathPrefixes = std.BoundedArray([]const u8, 16); fn collectLibDirsAndTriples( - self: *GCCDetector, tc: *Toolchain, lib_dirs: *PathPrefixes, triple_aliases: *PathPrefixes, biarch_libdirs: *PathPrefixes, biarch_triple_aliases: *PathPrefixes, ) !void { - _ = self; - const AArch64LibDirs: [2][]const u8 = .{ "/lib64", "/lib" }; const AArch64Triples: [4][]const u8 = .{ "aarch64-none-linux-gnu", "aarch64-linux-gnu", "aarch64-redhat-linux", "aarch64-suse-linux" }; const AArch64beLibDirs: [1][]const u8 = .{"/lib"}; @@ -406,23 +403,28 @@ pub fn discover(self: *GCCDetector, tc: *Toolchain) !void { var fib = std.heap.FixedBufferAllocator.init(&path_buf); const target = tc.getTarget(); - const bivariant_target = if (target.ptrBitWidth() == 32) target_util.get64BitArchVariant(target) else target_util.get32BitArchVariant(target); - _ = bivariant_target; + const biarch_variant_target = if (target.ptrBitWidth() == 32) target_util.get64BitArchVariant(target) else target_util.get32BitArchVariant(target); var candidate_lib_dirs: PathPrefixes = .{}; var candidate_biarch_lib_dirs: PathPrefixes = .{}; var candidate_triple_aliases: PathPrefixes = .{}; var candidate_biarch_triple_aliases: PathPrefixes = .{}; - try self.collectLibDirsAndTriples(tc, &candidate_lib_dirs, &candidate_biarch_lib_dirs, &candidate_triple_aliases, &candidate_biarch_triple_aliases); + try collectLibDirsAndTriples(tc, &candidate_lib_dirs, &candidate_biarch_lib_dirs, &candidate_triple_aliases, &candidate_biarch_triple_aliases); var target_buf: std.BoundedArray(u8, 32) = .{}; try target_util.toLLVMTriple(target_buf.writer(), target); const triple_str = target_buf.constSlice(); candidate_triple_aliases.appendAssumeCapacity(triple_str); - // // Also include the multiarch variant if it's different. - // if (TargetTriple.str() != BiarchTriple.str()) - // BiarchTripleAliases.push_back(BiarchTriple.str()); + // Also include the multiarch variant if it's different. + var biarch_buf: std.BoundedArray(u8, 32) = .{}; + if (biarch_variant_target) |biarch_target| { + try target_util.toLLVMTriple(biarch_buf.writer(), biarch_target); + const biarch_triple_str = biarch_buf.constSlice(); + if (!std.mem.eql(u8, biarch_triple_str, triple_str)) { + candidate_triple_aliases.appendAssumeCapacity(biarch_triple_str); + } + } var prefixes: PathPrefixes = .{}; const gcc_toolchain_dir = gccToolchainDir(tc); From 8e609a62d2415d1217522b3c70ea39497c9c5afa Mon Sep 17 00:00:00 2001 From: Evan Haas Date: Wed, 16 Aug 2023 22:01:01 -0700 Subject: [PATCH 36/50] Toolchain: Add more multiarch triples --- src/toolchains/Linux.zig | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/src/toolchains/Linux.zig b/src/toolchains/Linux.zig index 8d99b382..99bde397 100644 --- a/src/toolchains/Linux.zig +++ b/src/toolchains/Linux.zig @@ -103,8 +103,13 @@ fn findPaths(self: *Linux, tc: *Toolchain) !void { const target = tc.getTarget(); const sysroot = tc.getSysroot(); + var target_buf: std.BoundedArray(u8, 32) = .{}; + const os_lib_dir = getOSLibDir(target); - const multiarch_triple = getMultiarchTriple(target); + const multiarch_triple = getMultiarchTriple(target) orelse blk: { + try target_util.toLLVMTriple(target_buf.writer(), target); + break :blk target_buf.constSlice(); + }; try self.addMultiLibPaths(tc, sysroot, os_lib_dir); @@ -323,17 +328,29 @@ pub fn buildLinkerArgs(self: *const Linux, tc: *const Toolchain, argv: *std.Arra // TODO add -T args } -fn getMultiarchTriple(target: std.Target) []const u8 { +fn getMultiarchTriple(target: std.Target) ?[]const u8 { const is_android = target.isAndroid(); - + const is_mips_r6 = std.Target.mips.featureSetHas(target.cpu.features, .mips32r6); return switch (target.cpu.arch) { + .arm, .thumb => if (is_android) "arm-linux-androideabi" else if (target.abi == .gnueabihf) "arm-linux-gnueabihf" else "arm-linux-gnueabi", + .armeb, .thumbeb => if (target.abi == .gnueabihf) "armeb-linux-gnueabihf" else "armeb-linux-gnueabi", .aarch64 => if (is_android) "aarch64-linux-android" else "aarch64-linux-gnu", .aarch64_be => "aarch64_be-linux-gnu", .x86 => if (is_android) "i686-linux-android" else "i386-linux-gnu", .x86_64 => if (is_android) "x86_64-linux-android" else if (target.abi == .gnux32) "x86_64-linux-gnux32" else "x86_64-linux-gnu", + .m68k => "m68k-linux-gnu", + .mips => if (is_mips_r6) "mipsisa32r6-linux-gnu" else "mips-linux-gnu", + .mipsel => if (is_android) "mipsel-linux-android" else if (is_mips_r6) "mipsisa32r6el-linux-gnu" else "mipsel-linux-gnu", + .powerpcle => "powerpcle-linux-gnu", + .powerpc64 => "powerpc64-linux-gnu", + .powerpc64le => "powerpc64le-linux-gnu", + .riscv64 => "riscv64-linux-gnu", + .sparc => "sparc-linux-gnu", + .sparc64 => "sparc64-linux-gnu", + .s390x => "s390x-linux-gnu", // TODO: expand this - else => "", + else => null, }; } From 8f3d337ddf982060b73372cd1ced3464dfdeb5b1 Mon Sep 17 00:00:00 2001 From: Evan Haas Date: Wed, 16 Aug 2023 22:24:28 -0700 Subject: [PATCH 37/50] Toolchain: get PATH env var from compilation instead of getenv --- src/Compilation.zig | 3 +++ src/Toolchain.zig | 2 +- src/driver/Filesystem.zig | 16 +++++++++------- 3 files changed, 13 insertions(+), 8 deletions(-) diff --git a/src/Compilation.zig b/src/Compilation.zig index 4e45a550..fd971b08 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -31,6 +31,9 @@ pub const Environment = struct { /// TODO: not implemented yet tmpdir: ?[]const u8 = null, + /// PATH environment variable used to search for programs + path: ?[]const u8 = null, + /// Directories to try when searching for subprograms. /// TODO: not implemented yet compiler_path: ?[]const u8 = null, diff --git a/src/Toolchain.zig b/src/Toolchain.zig index a09b4b1e..c98367c0 100644 --- a/src/Toolchain.zig +++ b/src/Toolchain.zig @@ -242,7 +242,7 @@ fn getProgramPath(tc: *const Toolchain, name: []const u8, buf: []u8) []const u8 return buf[0..candidate.len]; } } - return tc.filesystem.findProgramByName(tc.driver.comp.gpa, name, buf) orelse continue; + return tc.filesystem.findProgramByName(tc.driver.comp.gpa, name, tc.driver.comp.environment.path, buf) orelse continue; } @memcpy(buf[0..name.len], name); return buf[0..name.len]; diff --git a/src/driver/Filesystem.zig b/src/driver/Filesystem.zig index fd49ab8f..be286746 100644 --- a/src/driver/Filesystem.zig +++ b/src/driver/Filesystem.zig @@ -3,7 +3,8 @@ const mem = std.mem; const builtin = @import("builtin"); const is_windows = builtin.os.tag == .windows; -fn findProgramByNameFake(allocator: std.mem.Allocator, name: []const u8, buf: []u8) ?[]const u8 { +fn findProgramByNameFake(allocator: std.mem.Allocator, name: []const u8, path: ?[]const u8, buf: []u8) ?[]const u8 { + _ = path; _ = buf; _ = name; _ = allocator; @@ -40,7 +41,8 @@ fn canExecuteWindows(path: []const u8) bool { } /// TODO -fn findProgramByNameWindows(allocator: std.mem.Allocator, name: []const u8, buf: []u8) ?[]const u8 { +fn findProgramByNameWindows(allocator: std.mem.Allocator, name: []const u8, path: ?[]const u8, buf: []u8) ?[]const u8 { + _ = path; _ = buf; _ = name; _ = allocator; @@ -48,12 +50,12 @@ fn findProgramByNameWindows(allocator: std.mem.Allocator, name: []const u8, buf: } /// TODO: does WASI need special handling? -fn findProgramByNamePosix(name: []const u8, buf: []u8) ?[]const u8 { +fn findProgramByNamePosix(name: []const u8, path: ?[]const u8, buf: []u8) ?[]const u8 { if (mem.indexOfScalar(u8, name, '/') != null) { @memcpy(buf[0..name.len], name); return buf[0..name.len]; } - const path_env = std.os.getenvZ("PATH") orelse return null; + const path_env = path orelse return null; var fib = std.heap.FixedBufferAllocator.init(buf); var it = mem.tokenizeScalar(u8, path_env, ':'); @@ -97,11 +99,11 @@ pub const Filesystem = union(enum) { /// Search for an executable named `name` using platform-specific logic /// If it's found, write the full path to `buf` and return a slice of it /// Otherwise retun null - pub fn findProgramByName(fs: Filesystem, allocator: std.mem.Allocator, name: []const u8, buf: []u8) ?[]const u8 { + pub fn findProgramByName(fs: Filesystem, allocator: std.mem.Allocator, name: []const u8, path: ?[]const u8, buf: []u8) ?[]const u8 { std.debug.assert(name.len > 0); return switch (fs) { - .real => if (is_windows) findProgramByNameWindows(allocator, name, buf) else findProgramByNamePosix(name, buf), - .fake => findProgramByNameFake(allocator, name, buf), + .real => if (is_windows) findProgramByNameWindows(allocator, name, path, buf) else findProgramByNamePosix(name, path, buf), + .fake => findProgramByNameFake(allocator, name, path, buf), }; } }; From feb3b4b989d1cde873302d4039adbc58d0945d66 Mon Sep 17 00:00:00 2001 From: Evan Haas Date: Wed, 16 Aug 2023 22:51:10 -0700 Subject: [PATCH 38/50] Filesystem: make PATH separator configurable --- build.zig | 4 ++++ src/driver/Filesystem.zig | 3 ++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/build.zig b/build.zig index 21a9b97c..69c4f519 100644 --- a/build.zig +++ b/build.zig @@ -40,6 +40,8 @@ pub fn build(b: *Build) !void { // between Debug, ReleaseSafe, ReleaseFast, and ReleaseSmall. const mode = b.standardOptimizeOption(.{}); + const default_path_sep: u8 = if (target.isWindows()) ';' else ':'; + const enable_linker_build_id = b.option(bool, "enable-linker-build-id", "pass --build-id to linker") orelse false; const default_linker = b.option([]const u8, "default-linker", "Default linker aro will use if none is supplied via -fuse-ld") orelse "ld"; const default_sysroot = b.option([]const u8, "default-sysroot", "Default to all compiler invocations for --sysroot=.") orelse ""; @@ -49,6 +51,7 @@ pub fn build(b: *Build) !void { if (std.mem.eql(u8, default_rtlib, "libgcc")) "libgcc" else ""; const test_all_allocation_failures = b.option(bool, "test-all-allocation-failures", "Test all allocation failures") orelse false; const link_libc = b.option(bool, "link-libc", "Force self-hosted compiler to link libc") orelse (mode != .Debug); + const path_sep = b.option(u8, "path-sep", "Path separator in PATH environment variable") orelse default_path_sep; const tracy = b.option([]const u8, "tracy", "Enable Tracy integration. Supply path to Tracy source"); const tracy_callstack = b.option(bool, "tracy-callstack", "Include callstack information with Tracy data. Does nothing if -Dtracy is not provided") orelse false; const tracy_allocation = b.option(bool, "tracy-allocation", "Include allocation information with Tracy data. Does nothing if -Dtracy is not provided") orelse false; @@ -78,6 +81,7 @@ pub fn build(b: *Build) !void { system_defaults.addOption(bool, "enable_linker_build_id", enable_linker_build_id); system_defaults.addOption([]const u8, "linker", default_linker); + system_defaults.addOption(u8, "path_sep", path_sep); system_defaults.addOption([]const u8, "sysroot", default_sysroot); system_defaults.addOption([]const u8, "gcc_install_prefix", gcc_install_prefix); system_defaults.addOption([]const u8, "rtlib", default_rtlib); diff --git a/src/driver/Filesystem.zig b/src/driver/Filesystem.zig index be286746..f57db2ad 100644 --- a/src/driver/Filesystem.zig +++ b/src/driver/Filesystem.zig @@ -1,6 +1,7 @@ const std = @import("std"); const mem = std.mem; const builtin = @import("builtin"); +const system_defaults = @import("system_defaults"); const is_windows = builtin.os.tag == .windows; fn findProgramByNameFake(allocator: std.mem.Allocator, name: []const u8, path: ?[]const u8, buf: []u8) ?[]const u8 { @@ -58,7 +59,7 @@ fn findProgramByNamePosix(name: []const u8, path: ?[]const u8, buf: []u8) ?[]con const path_env = path orelse return null; var fib = std.heap.FixedBufferAllocator.init(buf); - var it = mem.tokenizeScalar(u8, path_env, ':'); + var it = mem.tokenizeScalar(u8, path_env, system_defaults.path_sep); while (it.next()) |path_dir| { defer fib.reset(); const full_path = std.fs.path.join(fib.allocator(), &.{ path_dir, name }) catch continue; From 865a145c33fb48e9108449fc76f17fe664dabcd3 Mon Sep 17 00:00:00 2001 From: Evan Haas Date: Wed, 16 Aug 2023 23:42:47 -0700 Subject: [PATCH 39/50] Linux toolchain: Add basic test for linker args --- build.zig | 1 + src/driver/Filesystem.zig | 54 +++++++++++++------- src/main.zig | 1 + src/toolchains/Linux.zig | 101 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 140 insertions(+), 17 deletions(-) diff --git a/build.zig b/build.zig index 69c4f519..c75f866d 100644 --- a/build.zig +++ b/build.zig @@ -123,6 +123,7 @@ pub fn build(b: *Build) !void { var unit_tests = b.addTest(.{ .root_source_file = .{ .path = "src/main.zig" } }); unit_tests.addModule("zig", zig_module); + unit_tests.addOptions("system_defaults", system_defaults); const run_test = b.addRunArtifact(unit_tests); tests_step.dependOn(&run_test.step); diff --git a/src/driver/Filesystem.zig b/src/driver/Filesystem.zig index f57db2ad..debe8f9f 100644 --- a/src/driver/Filesystem.zig +++ b/src/driver/Filesystem.zig @@ -4,27 +4,42 @@ const builtin = @import("builtin"); const system_defaults = @import("system_defaults"); const is_windows = builtin.os.tag == .windows; -fn findProgramByNameFake(allocator: std.mem.Allocator, name: []const u8, path: ?[]const u8, buf: []u8) ?[]const u8 { - _ = path; - _ = buf; - _ = name; - _ = allocator; - @panic("TODO"); +fn findProgramByNameFake(entries: []const Filesystem.Entry, name: []const u8, path: ?[]const u8, buf: []u8) ?[]const u8 { + @setCold(true); + if (mem.indexOfScalar(u8, name, '/') != null) { + @memcpy(buf[0..name.len], name); + return buf[0..name.len]; + } + const path_env = path orelse return null; + var fib = std.heap.FixedBufferAllocator.init(buf); + + var it = mem.tokenizeScalar(u8, path_env, system_defaults.path_sep); + while (it.next()) |path_dir| { + defer fib.reset(); + const full_path = std.fs.path.join(fib.allocator(), &.{ path_dir, name }) catch continue; + if (canExecuteFake(entries, full_path)) return full_path; + } + + return null; } -fn canExecuteFake(paths: []const []const u8, path: []const u8) bool { - _ = path; - _ = paths; +fn canExecuteFake(entries: []const Filesystem.Entry, path: []const u8) bool { @setCold(true); - @panic("TODO"); + for (entries) |entry| { + if (mem.eql(u8, entry.path, path)) { + return entry.executable; + } + } + return false; } -fn existsFake(paths: []const []const u8, path: []const u8) bool { +fn existsFake(entries: []const Filesystem.Entry, path: []const u8) bool { + @setCold(true); var buf: [std.fs.MAX_PATH_BYTES]u8 = undefined; var fib = std.heap.FixedBufferAllocator.init(&buf); const resolved = std.fs.path.resolvePosix(fib.allocator(), &.{path}) catch return false; - for (paths) |fakepath| { - if (mem.eql(u8, fakepath, resolved)) return true; + for (entries) |entry| { + if (mem.eql(u8, entry.path, resolved)) return true; } return false; } @@ -71,7 +86,12 @@ fn findProgramByNamePosix(name: []const u8, path: ?[]const u8, buf: []u8) ?[]con pub const Filesystem = union(enum) { real: void, - fake: []const []const u8, + fake: []const Entry, + + const Entry = struct { + path: []const u8, + executable: bool = false, + }; pub fn exists(fs: Filesystem, path: []const u8) bool { switch (fs) { @@ -93,7 +113,7 @@ pub const Filesystem = union(enum) { pub fn canExecute(fs: Filesystem, path: []const u8) bool { return switch (fs) { .real => if (is_windows) canExecuteWindows(path) else canExecutePosix(path), - .fake => |paths| canExecuteFake(paths, path), + .fake => |entries| canExecuteFake(entries, path), }; } @@ -104,14 +124,14 @@ pub const Filesystem = union(enum) { std.debug.assert(name.len > 0); return switch (fs) { .real => if (is_windows) findProgramByNameWindows(allocator, name, path, buf) else findProgramByNamePosix(name, path, buf), - .fake => findProgramByNameFake(allocator, name, path, buf), + .fake => |entries| findProgramByNameFake(entries, name, path, buf), }; } }; test "Fake filesystem" { const fs: Filesystem = .{ .fake = &.{ - "/usr/bin", + .{ .path = "/usr/bin" }, } }; try std.testing.expect(fs.exists("/usr/bin")); try std.testing.expect(fs.exists("/usr/bin/foo/..")); diff --git a/src/main.zig b/src/main.zig index ea659257..9a1695ac 100644 --- a/src/main.zig +++ b/src/main.zig @@ -100,6 +100,7 @@ test { _ = @import("Source.zig"); _ = @import("Tokenizer.zig"); _ = @import("driver/GCCVersion.zig"); + _ = @import("toolchains/Linux.zig"); _ = @import("Tree.zig"); _ = @import("Type.zig"); _ = @import("target.zig"); diff --git a/src/toolchains/Linux.zig b/src/toolchains/Linux.zig index 99bde397..a41bd5dd 100644 --- a/src/toolchains/Linux.zig +++ b/src/toolchains/Linux.zig @@ -375,3 +375,104 @@ fn getOSLibDir(target: std.Target) []const u8 { } return "lib64"; } + +test Linux { + if (@import("builtin").os.tag == .windows) return error.SkipZigTest; + + var arena_instance = std.heap.ArenaAllocator.init(std.testing.allocator); + defer arena_instance.deinit(); + const arena = arena_instance.allocator(); + + var comp = Compilation.init(std.testing.allocator); + defer comp.deinit(); + comp.environment = .{ + .path = "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", + }; + + const raw_triple = "x86_64-linux-gnu"; + const cross = std.zig.CrossTarget.parse(.{ .arch_os_abi = raw_triple }) catch unreachable; + comp.target = cross.toTarget(); // TODO deprecated + comp.langopts.setEmulatedCompiler(.gcc); + + var driver: Driver = .{ .comp = &comp }; + defer driver.deinit(); + driver.raw_target_triple = raw_triple; + + const link_obj = try driver.comp.gpa.dupe(u8, "/tmp/foo.o"); + try driver.link_objects.append(driver.comp.gpa, link_obj); + driver.temp_file_count += 1; + + var toolchain: Toolchain = .{ .driver = &driver, .arena = arena, .filesystem = .{ .fake = &.{ + .{ .path = "/tmp" }, + .{ .path = "/usr" }, + .{ .path = "/usr/lib64" }, + .{ .path = "/usr/bin" }, + .{ .path = "/usr/bin/ld", .executable = true }, + .{ .path = "/lib" }, + .{ .path = "/lib/x86_64-linux-gnu" }, + .{ .path = "/lib/x86_64-linux-gnu/crt1.o" }, + .{ .path = "/lib/x86_64-linux-gnu/crti.o" }, + .{ .path = "/lib/x86_64-linux-gnu/crtn.o" }, + .{ .path = "/lib64" }, + .{ .path = "/usr/lib" }, + .{ .path = "/usr/lib/gcc" }, + .{ .path = "/usr/lib/gcc/x86_64-linux-gnu" }, + .{ .path = "/usr/lib/gcc/x86_64-linux-gnu/9" }, + .{ .path = "/usr/lib/gcc/x86_64-linux-gnu/9/crtbegin.o" }, + .{ .path = "/usr/lib/gcc/x86_64-linux-gnu/9/crtend.o" }, + .{ .path = "/usr/lib/x86_64-linux-gnu" }, + } } }; + defer toolchain.deinit(); + + try toolchain.discover(); + + var argv = std.ArrayList([]const u8).init(driver.comp.gpa); + defer argv.deinit(); + + var linker_path_buf: [std.fs.MAX_PATH_BYTES]u8 = undefined; + const linker_path = try toolchain.getLinkerPath(&linker_path_buf); + try argv.append(linker_path); + + try toolchain.buildLinkerArgs(&argv); + + const expected = [_][]const u8{ + "/usr/bin/ld", + "-z", + "relro", + "--hash-style=gnu", + "--eh-frame-hdr", + "-m", + "elf_x86_64", + "-dynamic-linker", + "/lib64/ld-linux-x86-64.so.2", + "-o", + "a.out", + "/lib/x86_64-linux-gnu/crt1.o", + "/lib/x86_64-linux-gnu/crti.o", + "/usr/lib/gcc/x86_64-linux-gnu/9/crtbegin.o", + "-L/usr/lib/gcc/x86_64-linux-gnu/9", + "-L/usr/lib/gcc/x86_64-linux-gnu/9/../../../../lib64", + "-L/lib/x86_64-linux-gnu", + "-L/lib/../lib64", + "-L/usr/lib/x86_64-linux-gnu", + "-L/usr/lib/../lib64", + "-L/lib", + "-L/usr/lib", + link_obj, + "-lgcc", + "--as-needed", + "-lgcc_s", + "--no-as-needed", + "-lc", + "-lgcc", + "--as-needed", + "-lgcc_s", + "--no-as-needed", + "/usr/lib/gcc/x86_64-linux-gnu/9/crtend.o", + "/lib/x86_64-linux-gnu/crtn.o", + }; + try std.testing.expectEqual(expected.len, argv.items.len); + for (expected, argv.items) |expected_item, actual_item| { + try std.testing.expectEqualStrings(expected_item, actual_item); + } +} From 0ddafc477a2261fa9ec4b2e5b6393e3881a0580f Mon Sep 17 00:00:00 2001 From: Evan Haas Date: Thu, 17 Aug 2023 15:44:00 -0700 Subject: [PATCH 40/50] Toolchain: use Filesystem for Distro and GCC detection --- src/driver/Distro.zig | 35 +++++-------- src/driver/Filesystem.zig | 103 +++++++++++++++++++++++++++++++++++++ src/driver/GCCDetector.zig | 2 +- src/toolchains/Linux.zig | 7 +++ 4 files changed, 123 insertions(+), 24 deletions(-) diff --git a/src/driver/Distro.zig b/src/driver/Distro.zig index 8b134e3b..749bbf09 100644 --- a/src/driver/Distro.zig +++ b/src/driver/Distro.zig @@ -13,17 +13,6 @@ pub const HashStyle = enum { gnu, }; -/// Read the file at `path` into `buf`. -/// Returns null if any errors are encountered -/// Otherwise returns a slice of `buf`. If the file is larger than `buf` partial contents are returned -fn readFile(path: []const u8, buf: []u8) ?[]const u8 { - const file = std.fs.openFileAbsolute(path, .{}) catch return null; - defer file.close(); - - const bytes_read = file.readAll(buf) catch return null; - return buf[0..bytes_read]; -} - pub const Tag = enum { alpine, arch, @@ -180,9 +169,9 @@ fn scanForOsRelease(buf: []const u8) ?Tag { return null; } -fn detectOsRelease() ?Tag { +fn detectOsRelease(fs: Filesystem) ?Tag { var buf: [MAX_BYTES]u8 = undefined; - const data = readFile("/etc/os-release", &buf) orelse readFile("/usr/lib/os-release", &buf) orelse return null; + const data = fs.readFile("/etc/os-release", &buf) orelse fs.readFile("/usr/lib/os-release", &buf) orelse return null; return scanForOsRelease(data); } @@ -227,9 +216,9 @@ fn scanForLSBRelease(buf: []const u8) ?Tag { return null; } -fn detectLSBRelease() ?Tag { +fn detectLSBRelease(fs: Filesystem) ?Tag { var buf: [MAX_BYTES]u8 = undefined; - const data = readFile("/etc/lsb-release", &buf) orelse return null; + const data = fs.readFile("/etc/lsb-release", &buf) orelse return null; return scanForLSBRelease(data); } @@ -245,9 +234,9 @@ fn scanForRedHat(buf: []const u8) Tag { return .unknown; } -fn detectRedhat() ?Tag { +fn detectRedhat(fs: Filesystem) ?Tag { var buf: [MAX_BYTES]u8 = undefined; - const data = readFile("/etc/redhat-release", &buf) orelse return null; + const data = fs.readFile("/etc/redhat-release", &buf) orelse return null; return scanForRedHat(data); } @@ -281,19 +270,19 @@ fn scanForDebian(buf: []const u8) Tag { return .unknown; } -fn detectDebian() ?Tag { +fn detectDebian(fs: Filesystem) ?Tag { var buf: [MAX_BYTES]u8 = undefined; - const data = readFile("/etc/debian_version", &buf) orelse return null; + const data = fs.readFile("/etc/debian_version", &buf) orelse return null; return scanForDebian(data); } pub fn detect(target: std.Target, fs: Filesystem) Tag { if (target.os.tag != .linux) return .unknown; - if (detectOsRelease()) |tag| return tag; - if (detectLSBRelease()) |tag| return tag; - if (detectRedhat()) |tag| return tag; - if (detectDebian()) |tag| return tag; + if (detectOsRelease(fs)) |tag| return tag; + if (detectLSBRelease(fs)) |tag| return tag; + if (detectRedhat(fs)) |tag| return tag; + if (detectDebian(fs)) |tag| return tag; if (fs.exists("/etc/gentoo-release")) return .gentoo; diff --git a/src/driver/Filesystem.zig b/src/driver/Filesystem.zig index debe8f9f..7ffab2b1 100644 --- a/src/driver/Filesystem.zig +++ b/src/driver/Filesystem.zig @@ -4,6 +4,18 @@ const builtin = @import("builtin"); const system_defaults = @import("system_defaults"); const is_windows = builtin.os.tag == .windows; +fn readFileFake(entries: []const Filesystem.Entry, path: []const u8, buf: []u8) ?[]const u8 { + @setCold(true); + for (entries) |entry| { + if (mem.eql(u8, entry.path, path)) { + const len = @min(entry.contents.len, buf.len); + @memcpy(buf[0..len], entry.contents[0..len]); + return buf[0..len]; + } + } + return null; +} + fn findProgramByNameFake(entries: []const Filesystem.Entry, name: []const u8, path: ?[]const u8, buf: []u8) ?[]const u8 { @setCold(true); if (mem.indexOfScalar(u8, name, '/') != null) { @@ -90,9 +102,77 @@ pub const Filesystem = union(enum) { const Entry = struct { path: []const u8, + contents: []const u8 = "", executable: bool = false, }; + const FakeDir = struct { + entries: []const Entry, + path: []const u8, + + fn iterate(self: FakeDir) FakeDir.Iterator { + return .{ + .entries = self.entries, + .base = self.path, + }; + } + + const Iterator = struct { + entries: []const Entry, + base: []const u8, + i: usize = 0, + + const Self = @This(); + + fn next(self: *@This()) !?std.fs.IterableDir.Entry { + while (self.i < self.entries.len) { + const entry = self.entries[self.i]; + self.i += 1; + if (entry.path.len == self.base.len) continue; + if (std.mem.startsWith(u8, entry.path, self.base)) { + const remaining = entry.path[self.base.len + 1 ..]; + if (std.mem.indexOfScalar(u8, remaining, std.fs.path.sep) != null) continue; + const extension = std.fs.path.extension(remaining); + const kind: std.fs.IterableDir.Entry.Kind = if (extension.len == 0) .directory else .file; + return .{ .name = remaining, .kind = kind }; + } + } + return null; + } + }; + }; + + const IterableDir = union(enum) { + dir: std.fs.IterableDir, + fake: FakeDir, + + pub fn iterate(self: IterableDir) Iterator { + return switch (self) { + .dir => |dir| .{ .iterator = dir.iterate() }, + .fake => |fake| .{ .fake = fake.iterate() }, + }; + } + + pub fn close(self: *IterableDir) void { + switch (self.*) { + .dir => |*d| d.close(), + .fake => {}, + } + } + }; + + const Iterator = union(enum) { + iterator: std.fs.IterableDir.Iterator, + fake: FakeDir.Iterator, + + pub fn next(self: *Iterator) std.fs.IterableDir.Iterator.Error!?std.fs.IterableDir.Entry { + return switch (self.*) { + .iterator => |*it| it.next(), + .fake => |*it| it.next(), + }; + } + }; + pub fn exists(fs: Filesystem, path: []const u8) bool { switch (fs) { .real => { @@ -127,6 +207,29 @@ pub const Filesystem = union(enum) { .fake => |entries| findProgramByNameFake(entries, name, path, buf), }; } + + /// Read the file at `path` into `buf`. + /// Returns null if any errors are encountered + /// Otherwise returns a slice of `buf`. If the file is larger than `buf` partial contents are returned + pub fn readFile(fs: Filesystem, path: []const u8, buf: []u8) ?[]const u8 { + return switch (fs) { + .real => { + const file = std.fs.cwd().openFile(path, .{}) catch return null; + defer file.close(); + + const bytes_read = file.readAll(buf) catch return null; + return buf[0..bytes_read]; + }, + .fake => |entries| readFileFake(entries, path, buf), + }; + } + + pub fn openIterableDir(fs: Filesystem, dir_name: []const u8) std.fs.Dir.OpenError!IterableDir { + return switch (fs) { + .real => .{ .dir = try std.fs.cwd().openIterableDir(dir_name, .{ .access_sub_paths = false }) }, + .fake => |entries| .{ .fake = .{ .entries = entries, .path = dir_name } }, + }; + } }; test "Fake filesystem" { diff --git a/src/driver/GCCDetector.zig b/src/driver/GCCDetector.zig index 38c08614..f8c8f2fc 100644 --- a/src/driver/GCCDetector.zig +++ b/src/driver/GCCDetector.zig @@ -579,7 +579,7 @@ fn scanLibDirForGCCTriple( const lib_suffix = lib_suffix_buf.constSlice(); const dir_name = std.fs.path.join(fib.allocator(), &.{ lib_dir, lib_suffix }) catch continue; - var parent_dir = std.fs.cwd().openIterableDir(dir_name, .{ .access_sub_paths = false }) catch continue; + var parent_dir = tc.filesystem.openIterableDir(dir_name) catch continue; defer parent_dir.close(); var it = parent_dir.iterate(); diff --git a/src/toolchains/Linux.zig b/src/toolchains/Linux.zig index a41bd5dd..ae4211c5 100644 --- a/src/toolchains/Linux.zig +++ b/src/toolchains/Linux.zig @@ -421,6 +421,13 @@ test Linux { .{ .path = "/usr/lib/gcc/x86_64-linux-gnu/9/crtbegin.o" }, .{ .path = "/usr/lib/gcc/x86_64-linux-gnu/9/crtend.o" }, .{ .path = "/usr/lib/x86_64-linux-gnu" }, + .{ .path = "/etc/lsb-release", .contents = + \\DISTRIB_ID=Ubuntu + \\DISTRIB_RELEASE=20.04 + \\DISTRIB_CODENAME=focal + \\DISTRIB_DESCRIPTION="Ubuntu 20.04.6 LTS" + \\ + }, } } }; defer toolchain.deinit(); From 6fac5b5c77ee9854de92505cef2bab7eddc16362 Mon Sep 17 00:00:00 2001 From: Evan Haas Date: Thu, 17 Aug 2023 21:58:59 -0700 Subject: [PATCH 41/50] GCCDetector: join lib_suffix correctly --- src/driver/GCCDetector.zig | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/driver/GCCDetector.zig b/src/driver/GCCDetector.zig index f8c8f2fc..22369541 100644 --- a/src/driver/GCCDetector.zig +++ b/src/driver/GCCDetector.zig @@ -572,11 +572,9 @@ fn scanLibDirForGCCTriple( defer fib.reset(); const base: []const u8 = if (i == 0) "gcc" else "gcc-cross"; - var lib_suffix_buf: std.BoundedArray(u8, 64) = .{}; - lib_suffix_buf.appendSliceAssumeCapacity(base); - lib_suffix_buf.appendAssumeCapacity(std.fs.path.sep); - lib_suffix_buf.appendSliceAssumeCapacity(candidate_triple); - const lib_suffix = lib_suffix_buf.constSlice(); + var lib_suffix_buf: [64]u8 = undefined; + var suffix_buf_fib = std.heap.FixedBufferAllocator.init(&lib_suffix_buf); + const lib_suffix = std.fs.path.join(suffix_buf_fib.allocator(), &.{ base, candidate_triple }) catch continue; const dir_name = std.fs.path.join(fib.allocator(), &.{ lib_dir, lib_suffix }) catch continue; var parent_dir = tc.filesystem.openIterableDir(dir_name) catch continue; From 4d124b3086c00aae5fc7b81cac8306d69bea5c9a Mon Sep 17 00:00:00 2001 From: Evan Haas Date: Thu, 17 Aug 2023 22:53:08 -0700 Subject: [PATCH 42/50] Driver: move self-exe determination to main And store it in the driver since it will be used in multiple places --- src/Compilation.zig | 4 ++-- src/Toolchain.zig | 4 ++-- src/driver/Driver.zig | 11 +++-------- src/main.zig | 9 ++++++++- test/record_runner.zig | 13 ++++++++----- test/runner.zig | 15 +++++++++------ 6 files changed, 32 insertions(+), 24 deletions(-) diff --git a/src/Compilation.zig b/src/Compilation.zig index fd971b08..5ddb7ce4 100644 --- a/src/Compilation.zig +++ b/src/Compilation.zig @@ -904,9 +904,9 @@ pub fn getCharSignedness(comp: *const Compilation) std.builtin.Signedness { return comp.langopts.char_signedness_override orelse comp.target.charSignedness(); } -pub fn defineSystemIncludes(comp: *Compilation) !void { +pub fn defineSystemIncludes(comp: *Compilation, aro_dir: []const u8) !void { var buf: [std.fs.MAX_PATH_BYTES]u8 = undefined; - var search_path: []const u8 = std.fs.selfExePath(&buf) catch return error.SelfExeNotFound; + var search_path = aro_dir; while (std.fs.path.dirname(search_path)) |dirname| : (search_path = dirname) { var base_dir = std.fs.cwd().openDir(dirname, .{}) catch continue; defer base_dir.close(); diff --git a/src/Toolchain.zig b/src/Toolchain.zig index c98367c0..66fceb7f 100644 --- a/src/Toolchain.zig +++ b/src/Toolchain.zig @@ -263,8 +263,8 @@ pub fn getFilePath(tc: *const Toolchain, name: []const u8) ![]const u8 { // todo check resource dir // todo check compiler RT path - - const candidate = try std.fs.path.join(allocator, &.{ tc.driver.aro_dir, "..", name }); + const aro_dir = std.fs.path.dirname(tc.driver.aro_name) orelse ""; + const candidate = try std.fs.path.join(allocator, &.{ aro_dir, "..", name }); if (tc.filesystem.exists(candidate)) { return tc.arena.dupe(u8, candidate); } diff --git a/src/driver/Driver.zig b/src/driver/Driver.zig index 061f4bbe..101d58cc 100644 --- a/src/driver/Driver.zig +++ b/src/driver/Driver.zig @@ -37,10 +37,8 @@ verbose_pp: bool = false, verbose_ir: bool = false, verbose_linker_args: bool = false, -/// name of the aro executable -aro_name: []const u8 = "arocc", -/// Directory from which aro was invoked -aro_dir: []const u8 = "", +/// Full path to the aro executable +aro_name: []const u8 = "", /// Value of --triple= passed via CLI raw_target_triple: ?[]const u8 = null, @@ -415,8 +413,6 @@ pub fn parseArgs( .off => false, .unset => util.fileSupportsColor(std.io.getStdErr()) and !std.process.hasEnvVarConstant("NO_COLOR"), }; - d.aro_name = std.fs.path.basename(args[0]); - d.aro_dir = std.fs.path.dirname(args[0]) orelse ""; return false; } @@ -465,9 +461,8 @@ pub fn main(d: *Driver, tc: *Toolchain, args: []const []const u8) !void { try d.comp.diag.add(.{ .tag = .cli_unused_link_object, .extra = .{ .str = obj } }, &.{}); }; - d.comp.defineSystemIncludes() catch |er| switch (er) { + d.comp.defineSystemIncludes(d.aro_name) catch |er| switch (er) { error.OutOfMemory => return error.OutOfMemory, - error.SelfExeNotFound => return d.fatal("unable to find Aro executable path", .{}), error.AroIncludeNotFound => return d.fatal("unable to find Aro builtin headers", .{}), }; diff --git a/src/main.zig b/src/main.zig index 9a1695ac..57f81d5a 100644 --- a/src/main.zig +++ b/src/main.zig @@ -30,6 +30,13 @@ pub fn main() !u8 { return 1; }; + const aro_name = std.fs.selfExePathAlloc(gpa) catch { + std.debug.print("unable to find Aro executable path\n", .{}); + if (fast_exit) std.process.exit(1); + return 1; + }; + defer gpa.free(aro_name); + var comp = Compilation.init(gpa); defer comp.deinit(); @@ -51,7 +58,7 @@ pub fn main() !u8 { }; comp.langopts.setEmulatedCompiler(target_util.systemCompiler(comp.target)); - var driver: Driver = .{ .comp = &comp }; + var driver: Driver = .{ .comp = &comp, .aro_name = aro_name }; defer driver.deinit(); var toolchain: Toolchain = .{ .driver = &driver, .arena = arena }; diff --git a/test/record_runner.zig b/test/record_runner.zig index 10fdce73..03a61762 100644 --- a/test/record_runner.zig +++ b/test/record_runner.zig @@ -93,6 +93,9 @@ pub fn main() !void { const gpa = general_purpose_allocator.allocator(); defer if (general_purpose_allocator.deinit() == .leak) std.process.exit(1); + const self_exe_dir = try std.fs.selfExePathAlloc(gpa); + defer gpa.free(self_exe_dir); + var args = try std.process.argsAlloc(gpa); defer std.process.argsFree(gpa, args); @@ -163,7 +166,7 @@ pub fn main() !void { for (0..thread_count) |i| { wait_group.start(); try thread_pool.spawn(runTestCases, .{ - gpa, &wait_group, test_cases.items[i..], thread_count, &stats, + gpa, self_exe_dir, &wait_group, test_cases.items[i..], thread_count, &stats, }); } @@ -181,7 +184,7 @@ pub fn main() !void { } } -fn runTestCases(allocator: std.mem.Allocator, wg: *std.Thread.WaitGroup, test_cases: []const TestCase, stride: usize, stats: *Stats) void { +fn runTestCases(allocator: std.mem.Allocator, self_exe_dir: []const u8, wg: *std.Thread.WaitGroup, test_cases: []const TestCase, stride: usize, stats: *Stats) void { defer wg.finish(); var mem = allocator.alloc(u8, MAX_MEM_PER_TEST) catch |err| { std.log.err("{s}", .{@errorName(err)}); @@ -198,7 +201,7 @@ fn runTestCases(allocator: std.mem.Allocator, wg: *std.Thread.WaitGroup, test_ca if (i % stride != 0) continue; defer fib.end_index = 0; - singleRun(fib.allocator(), case, stats) catch |err| { + singleRun(fib.allocator(), self_exe_dir, case, stats) catch |err| { std.log.err("{s}", .{@errorName(err)}); if (@errorReturnTrace()) |trace| { std.debug.dumpStackTrace(trace.*); @@ -209,14 +212,14 @@ fn runTestCases(allocator: std.mem.Allocator, wg: *std.Thread.WaitGroup, test_ca } } -fn singleRun(alloc: std.mem.Allocator, test_case: TestCase, stats: *Stats) !void { +fn singleRun(alloc: std.mem.Allocator, self_exe_dir: []const u8, test_case: TestCase, stats: *Stats) !void { const path = test_case.path; var comp = aro.Compilation.init(alloc); defer comp.deinit(); try comp.addDefaultPragmaHandlers(); - try comp.defineSystemIncludes(); + try comp.defineSystemIncludes(self_exe_dir); const target = setTarget(&comp, test_case.target) catch |err| switch (err) { error.UnknownCpuModel => unreachable, diff --git a/test/runner.zig b/test/runner.zig index 1446f15f..cdd040e0 100644 --- a/test/runner.zig +++ b/test/runner.zig @@ -44,12 +44,12 @@ fn addCommandLineArgs(comp: *aro.Compilation, file: aro.Source, macro_buf: anyty return only_preprocess; } -fn testOne(allocator: std.mem.Allocator, path: []const u8) !void { +fn testOne(allocator: std.mem.Allocator, path: []const u8, self_exe_dir: []const u8) !void { var comp = aro.Compilation.init(allocator); defer comp.deinit(); try comp.addDefaultPragmaHandlers(); - try comp.defineSystemIncludes(); + try comp.defineSystemIncludes(self_exe_dir); const file = try comp.addSourceFromPath(path); var macro_buf = std.ArrayList(u8).init(comp.gpa); @@ -82,7 +82,7 @@ fn testOne(allocator: std.mem.Allocator, path: []const u8) !void { tree.dump(false, std.io.null_writer) catch {}; } -fn testAllAllocationFailures(cases: [][]const u8) !void { +fn testAllAllocationFailures(cases: [][]const u8, self_exe_dir: []const u8) !void { var progress = std.Progress{}; const root_node = progress.start("Memory Allocation Test", cases.len); @@ -93,7 +93,7 @@ fn testAllAllocationFailures(cases: [][]const u8) !void { defer case_node.end(); progress.refresh(); - std.testing.checkAllAllocationFailures(std.testing.allocator, testOne, .{case}) catch |er| switch (er) { + std.testing.checkAllAllocationFailures(std.testing.allocator, testOne, .{ case, self_exe_dir }) catch |er| switch (er) { error.SwallowedOutOfMemoryError => {}, else => |e| return e, }; @@ -105,6 +105,9 @@ pub fn main() !void { const gpa = general_purpose_allocator.allocator(); defer if (general_purpose_allocator.deinit() == .leak) std.process.exit(1); + const self_exe_dir = try std.fs.selfExePathAlloc(gpa); + defer gpa.free(self_exe_dir); + var args = try std.process.argsAlloc(gpa); defer std.process.argsFree(gpa, args); @@ -140,7 +143,7 @@ pub fn main() !void { } } if (build_options.test_all_allocation_failures) { - return testAllAllocationFailures(cases.items); + return testAllAllocationFailures(cases.items, self_exe_dir); } var progress = std.Progress{}; @@ -161,7 +164,7 @@ pub fn main() !void { try initial_comp.include_dirs.append(cases_next_include_dir); try initial_comp.addDefaultPragmaHandlers(); - try initial_comp.defineSystemIncludes(); + try initial_comp.defineSystemIncludes(self_exe_dir); // apparently we can't use setAstCwd without libc on windows yet const win = @import("builtin").os.tag == .windows; From 39de175110f620a87f44369adb0cc87c9ed63c28 Mon Sep 17 00:00:00 2001 From: Evan Haas Date: Thu, 17 Aug 2023 22:59:59 -0700 Subject: [PATCH 43/50] Toolchain: reset allocator properly in path search --- src/Toolchain.zig | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/Toolchain.zig b/src/Toolchain.zig index 66fceb7f..2d58b2d9 100644 --- a/src/Toolchain.zig +++ b/src/Toolchain.zig @@ -269,13 +269,11 @@ pub fn getFilePath(tc: *const Toolchain, name: []const u8) ![]const u8 { return tc.arena.dupe(u8, candidate); } - fib.reset(); - if (tc.searchPaths(allocator, sysroot, tc.library_paths.items, name)) |path| { + if (tc.searchPaths(&fib, sysroot, tc.library_paths.items, name)) |path| { return tc.arena.dupe(u8, path); } - fib.reset(); - if (tc.searchPaths(allocator, sysroot, tc.file_paths.items, name)) |path| { + if (tc.searchPaths(&fib, sysroot, tc.file_paths.items, name)) |path| { return try tc.arena.dupe(u8, path); } @@ -284,14 +282,15 @@ pub fn getFilePath(tc: *const Toolchain, name: []const u8) ![]const u8 { /// Search a list of `path_prefixes` for the existence `name` /// Assumes that `fba` is a fixed-buffer allocator, so does not free joined path candidates -fn searchPaths(tc: *const Toolchain, fba: mem.Allocator, sysroot: []const u8, path_prefixes: []const []const u8, name: []const u8) ?[]const u8 { +fn searchPaths(tc: *const Toolchain, fib: *std.heap.FixedBufferAllocator, sysroot: []const u8, path_prefixes: []const []const u8, name: []const u8) ?[]const u8 { for (path_prefixes) |path| { + fib.reset(); if (path.len == 0) continue; const candidate = if (path[0] == '=') - std.fs.path.join(fba, &.{ sysroot, path[1..], name }) catch continue + std.fs.path.join(fib.allocator(), &.{ sysroot, path[1..], name }) catch continue else - std.fs.path.join(fba, &.{ path, name }) catch continue; + std.fs.path.join(fib.allocator(), &.{ path, name }) catch continue; if (tc.filesystem.exists(candidate)) { return candidate; From 0c34e511e7de8b8c23d4e580299aa076a47053f4 Mon Sep 17 00:00:00 2001 From: Evan Haas Date: Fri, 25 Aug 2023 07:20:13 -0600 Subject: [PATCH 44/50] Driver: remove unnecessary driver_test.zig --- src/driver_test.zig | 21 --------------------- src/main.zig | 1 - 2 files changed, 22 deletions(-) delete mode 100644 src/driver_test.zig diff --git a/src/driver_test.zig b/src/driver_test.zig deleted file mode 100644 index 1046ddbf..00000000 --- a/src/driver_test.zig +++ /dev/null @@ -1,21 +0,0 @@ -const std = @import("std"); -const Driver = @import("driver/Driver.zig"); -const Compilation = @import("Compilation.zig"); -const Toolchain = @import("Toolchain.zig"); - -test Driver { - var arena_instance = std.heap.ArenaAllocator.init(std.testing.allocator); - defer arena_instance.deinit(); - const arena = arena_instance.allocator(); - - var comp = Compilation.init(std.testing.allocator); - defer comp.deinit(); - - var driver: Driver = .{ .comp = &comp }; - defer driver.deinit(); - - var toolchain: Toolchain = .{ .driver = &driver, .arena = arena }; - defer toolchain.deinit(); - - try std.testing.expect(1 == 1); -} diff --git a/src/main.zig b/src/main.zig index 57f81d5a..b9f9e002 100644 --- a/src/main.zig +++ b/src/main.zig @@ -98,7 +98,6 @@ test { _ = @import("Diagnostics.zig"); _ = @import("driver/Distro.zig"); _ = @import("driver/Filesystem.zig"); - _ = @import("driver_test.zig"); _ = @import("InitList.zig"); _ = @import("LangOpts.zig"); _ = @import("Parser.zig"); From 6b072e3ab4a7466dd069030c3aecbd3390d98fdf Mon Sep 17 00:00:00 2001 From: Evan Haas Date: Fri, 25 Aug 2023 11:02:06 -0600 Subject: [PATCH 45/50] Driver: reorganize namespace so Driver.zig is at the top level --- src/{driver => }/Driver.zig | 22 +++++++++++----------- src/{driver => Driver}/Distro.zig | 0 src/{driver => Driver}/Filesystem.zig | 0 src/{driver => Driver}/GCCDetector.zig | 0 src/{driver => Driver}/GCCVersion.zig | 0 src/{driver => Driver}/Multilib.zig | 0 src/Toolchain.zig | 6 +++--- src/lib.zig | 2 +- src/main.zig | 8 ++++---- src/toolchains/Linux.zig | 6 +++--- 10 files changed, 22 insertions(+), 22 deletions(-) rename src/{driver => }/Driver.zig (98%) rename src/{driver => Driver}/Distro.zig (100%) rename src/{driver => Driver}/Filesystem.zig (100%) rename src/{driver => Driver}/GCCDetector.zig (100%) rename src/{driver => Driver}/GCCVersion.zig (100%) rename src/{driver => Driver}/Multilib.zig (100%) diff --git a/src/driver/Driver.zig b/src/Driver.zig similarity index 98% rename from src/driver/Driver.zig rename to src/Driver.zig index 101d58cc..b1abaac6 100644 --- a/src/driver/Driver.zig +++ b/src/Driver.zig @@ -2,15 +2,15 @@ const std = @import("std"); const mem = std.mem; const Allocator = mem.Allocator; const process = std.process; -const Codegen = @import("../Codegen_legacy.zig"); -const Compilation = @import("../Compilation.zig"); -const LangOpts = @import("../LangOpts.zig"); -const Preprocessor = @import("../Preprocessor.zig"); -const Parser = @import("../Parser.zig"); -const Source = @import("../Source.zig"); -const Toolchain = @import("../Toolchain.zig"); -const util = @import("../util.zig"); -const target_util = @import("../target.zig"); +const Codegen = @import("Codegen_legacy.zig"); +const Compilation = @import("Compilation.zig"); +const LangOpts = @import("LangOpts.zig"); +const Preprocessor = @import("Preprocessor.zig"); +const Parser = @import("Parser.zig"); +const Source = @import("Source.zig"); +const Toolchain = @import("Toolchain.zig"); +const util = @import("util.zig"); +const target_util = @import("target.zig"); const Driver = @This(); @@ -176,7 +176,7 @@ pub fn parseArgs( }; return true; } else if (mem.eql(u8, arg, "-v") or mem.eql(u8, arg, "--version")) { - std_out.writeAll(@import("../lib.zig").version_str ++ "\n") catch |er| { + std_out.writeAll(@import("lib.zig").version_str ++ "\n") catch |er| { return d.fatal("unable to print version: {s}", .{util.errorDescription(er)}); }; return true; @@ -574,7 +574,7 @@ fn processSource( } if (d.verbose_ir) { - try @import("../CodeGen.zig").generateTree(d.comp, tree); + try @import("CodeGen.zig").generateTree(d.comp, tree); } const obj = try Codegen.generateTree(d.comp, tree); diff --git a/src/driver/Distro.zig b/src/Driver/Distro.zig similarity index 100% rename from src/driver/Distro.zig rename to src/Driver/Distro.zig diff --git a/src/driver/Filesystem.zig b/src/Driver/Filesystem.zig similarity index 100% rename from src/driver/Filesystem.zig rename to src/Driver/Filesystem.zig diff --git a/src/driver/GCCDetector.zig b/src/Driver/GCCDetector.zig similarity index 100% rename from src/driver/GCCDetector.zig rename to src/Driver/GCCDetector.zig diff --git a/src/driver/GCCVersion.zig b/src/Driver/GCCVersion.zig similarity index 100% rename from src/driver/GCCVersion.zig rename to src/Driver/GCCVersion.zig diff --git a/src/driver/Multilib.zig b/src/Driver/Multilib.zig similarity index 100% rename from src/driver/Multilib.zig rename to src/Driver/Multilib.zig diff --git a/src/Toolchain.zig b/src/Toolchain.zig index 2d58b2d9..dcc4a518 100644 --- a/src/Toolchain.zig +++ b/src/Toolchain.zig @@ -1,13 +1,13 @@ const std = @import("std"); -const Driver = @import("driver/Driver.zig"); +const Driver = @import("Driver.zig"); const Compilation = @import("Compilation.zig"); const util = @import("util.zig"); const mem = std.mem; const system_defaults = @import("system_defaults"); const target_util = @import("target.zig"); const Linux = @import("toolchains/Linux.zig"); -const Multilib = @import("driver/Multilib.zig"); -const Filesystem = @import("driver/Filesystem.zig").Filesystem; +const Multilib = @import("Driver/Multilib.zig"); +const Filesystem = @import("Driver/Filesystem.zig").Filesystem; const Toolchain = @This(); diff --git a/src/lib.zig b/src/lib.zig index 61f3f949..e19becb7 100644 --- a/src/lib.zig +++ b/src/lib.zig @@ -3,7 +3,7 @@ pub const Codegen = @import("Codegen_legacy.zig"); pub const CodeGen = @import("CodeGen.zig"); pub const Compilation = @import("Compilation.zig"); pub const Diagnostics = @import("Diagnostics.zig"); -pub const Driver = @import("driver/Driver.zig"); +pub const Driver = @import("Driver.zig"); pub const Interner = @import("Interner.zig"); pub const Ir = @import("Ir.zig"); pub const Object = @import("Object.zig"); diff --git a/src/main.zig b/src/main.zig index b9f9e002..824bda55 100644 --- a/src/main.zig +++ b/src/main.zig @@ -3,7 +3,7 @@ const mem = std.mem; const Allocator = mem.Allocator; const process = std.process; const Compilation = @import("Compilation.zig"); -const Driver = @import("driver/Driver.zig"); +const Driver = @import("Driver.zig"); const target_util = @import("target.zig"); const Toolchain = @import("Toolchain.zig"); @@ -96,8 +96,8 @@ test { _ = @import("Codegen_legacy.zig"); _ = @import("Compilation.zig"); _ = @import("Diagnostics.zig"); - _ = @import("driver/Distro.zig"); - _ = @import("driver/Filesystem.zig"); + _ = @import("Driver/Distro.zig"); + _ = @import("Driver/Filesystem.zig"); _ = @import("InitList.zig"); _ = @import("LangOpts.zig"); _ = @import("Parser.zig"); @@ -105,7 +105,7 @@ test { _ = @import("Preprocessor.zig"); _ = @import("Source.zig"); _ = @import("Tokenizer.zig"); - _ = @import("driver/GCCVersion.zig"); + _ = @import("Driver/GCCVersion.zig"); _ = @import("toolchains/Linux.zig"); _ = @import("Tree.zig"); _ = @import("Type.zig"); diff --git a/src/toolchains/Linux.zig b/src/toolchains/Linux.zig index ae4211c5..698a419a 100644 --- a/src/toolchains/Linux.zig +++ b/src/toolchains/Linux.zig @@ -1,10 +1,10 @@ const std = @import("std"); const mem = std.mem; const Compilation = @import("../Compilation.zig"); -const GCCDetector = @import("../driver/GCCDetector.zig"); +const GCCDetector = @import("../Driver/GCCDetector.zig"); const Toolchain = @import("../Toolchain.zig"); -const Driver = @import("../driver/Driver.zig"); -const Distro = @import("../driver/Distro.zig"); +const Driver = @import("../Driver.zig"); +const Distro = @import("../Driver/Distro.zig"); const target_util = @import("../target.zig"); const system_defaults = @import("system_defaults"); From 72fd17dd6e1cb54d70d349e474a7d21f117ecec3 Mon Sep 17 00:00:00 2001 From: Evan Haas Date: Fri, 25 Aug 2023 11:09:45 -0600 Subject: [PATCH 46/50] target: use Zig arch name for spu_2 since LLVM does not support it --- src/target.zig | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/target.zig b/src/target.zig index a5ee8bae..c9758a0a 100644 --- a/src/target.zig +++ b/src/target.zig @@ -632,7 +632,8 @@ pub fn toLLVMTriple(writer: anytype, target: std.Target) !void { .renderscript32 => "renderscript32", .renderscript64 => "renderscript64", .ve => "ve", - .spu_2 => return error.@"LLVM backend does not support SPU Mark II", + // Note: spu_2 is not supported in LLVM; this is the Zig arch name + .spu_2 => "spu_2", }; try writer.writeAll(llvm_arch); try writer.writeByte('-'); From 01328f20169e740f8247f9b05c1a80bdf1001108 Mon Sep 17 00:00:00 2001 From: Evan Haas Date: Fri, 25 Aug 2023 14:11:39 -0600 Subject: [PATCH 47/50] target: adjust toLLVMTriple to it doesn't return an error union This is not meant to be a general purpose function, so we just require that the caller pass a large-enough buffer for any possible target. --- src/Driver/GCCDetector.zig | 10 ++++------ src/target.zig | 23 +++++++++++++++-------- src/toolchains/Linux.zig | 7 ++----- 3 files changed, 21 insertions(+), 19 deletions(-) diff --git a/src/Driver/GCCDetector.zig b/src/Driver/GCCDetector.zig index 22369541..6f544f6e 100644 --- a/src/Driver/GCCDetector.zig +++ b/src/Driver/GCCDetector.zig @@ -411,16 +411,14 @@ pub fn discover(self: *GCCDetector, tc: *Toolchain) !void { var candidate_biarch_triple_aliases: PathPrefixes = .{}; try collectLibDirsAndTriples(tc, &candidate_lib_dirs, &candidate_biarch_lib_dirs, &candidate_triple_aliases, &candidate_biarch_triple_aliases); - var target_buf: std.BoundedArray(u8, 32) = .{}; - try target_util.toLLVMTriple(target_buf.writer(), target); - const triple_str = target_buf.constSlice(); + var target_buf: [64]u8 = undefined; + const triple_str = target_util.toLLVMTriple(target, &target_buf); candidate_triple_aliases.appendAssumeCapacity(triple_str); // Also include the multiarch variant if it's different. - var biarch_buf: std.BoundedArray(u8, 32) = .{}; + var biarch_buf: [64]u8 = undefined; if (biarch_variant_target) |biarch_target| { - try target_util.toLLVMTriple(biarch_buf.writer(), biarch_target); - const biarch_triple_str = biarch_buf.constSlice(); + const biarch_triple_str = target_util.toLLVMTriple(biarch_target, &biarch_buf); if (!std.mem.eql(u8, biarch_triple_str, triple_str)) { candidate_triple_aliases.appendAssumeCapacity(biarch_triple_str); } diff --git a/src/target.zig b/src/target.zig index c9758a0a..d3612a77 100644 --- a/src/target.zig +++ b/src/target.zig @@ -570,7 +570,13 @@ pub fn get64BitArchVariant(target: std.Target) ?std.Target { } /// Adapted from Zig's src/codegen/llvm.zig -pub fn toLLVMTriple(writer: anytype, target: std.Target) !void { +pub fn toLLVMTriple(target: std.Target, buf: []u8) []const u8 { + // 64 bytes is assumed to be large enough to hold any target triple; increase if necessary + std.debug.assert(buf.len >= 64); + + var stream = std.io.fixedBufferStream(buf); + const writer = stream.writer(); + const llvm_arch = switch (target.cpu.arch) { .arm => "arm", .armeb => "armeb", @@ -635,8 +641,8 @@ pub fn toLLVMTriple(writer: anytype, target: std.Target) !void { // Note: spu_2 is not supported in LLVM; this is the Zig arch name .spu_2 => "spu_2", }; - try writer.writeAll(llvm_arch); - try writer.writeByte('-'); + writer.writeAll(llvm_arch) catch unreachable; + writer.writeByte('-') catch unreachable; const llvm_os = switch (target.os.tag) { .freestanding => "unknown", @@ -685,17 +691,17 @@ pub fn toLLVMTriple(writer: anytype, target: std.Target) !void { .other, => "unknown", }; - try writer.writeAll(llvm_os); + writer.writeAll(llvm_os) catch unreachable; if (target.os.tag.isDarwin()) { const min_version = target.os.version_range.semver.min; - try writer.print("{d}.{d}.{d}", .{ + writer.print("{d}.{d}.{d}", .{ min_version.major, min_version.minor, min_version.patch, - }); + }) catch unreachable; } - try writer.writeByte('-'); + writer.writeByte('-') catch unreachable; const llvm_abi = switch (target.abi) { .none => "unknown", @@ -739,7 +745,8 @@ pub fn toLLVMTriple(writer: anytype, target: std.Target) !void { .mesh => "mesh", .amplification => "amplification", }; - try writer.writeAll(llvm_abi); + writer.writeAll(llvm_abi) catch unreachable; + return stream.getWritten(); } test "alignment functions - smoke test" { diff --git a/src/toolchains/Linux.zig b/src/toolchains/Linux.zig index 698a419a..7ec73204 100644 --- a/src/toolchains/Linux.zig +++ b/src/toolchains/Linux.zig @@ -103,13 +103,10 @@ fn findPaths(self: *Linux, tc: *Toolchain) !void { const target = tc.getTarget(); const sysroot = tc.getSysroot(); - var target_buf: std.BoundedArray(u8, 32) = .{}; + var output: [64]u8 = undefined; const os_lib_dir = getOSLibDir(target); - const multiarch_triple = getMultiarchTriple(target) orelse blk: { - try target_util.toLLVMTriple(target_buf.writer(), target); - break :blk target_buf.constSlice(); - }; + const multiarch_triple = getMultiarchTriple(target) orelse target_util.toLLVMTriple(target, &output); try self.addMultiLibPaths(tc, sysroot, os_lib_dir); From dfb5f0d0b7bdc29b360f6db453a6d401b221e68e Mon Sep 17 00:00:00 2001 From: Evan Haas Date: Fri, 25 Aug 2023 14:20:43 -0600 Subject: [PATCH 48/50] GCCDetector: skip directory entries that cause errors --- src/Driver/GCCDetector.zig | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/Driver/GCCDetector.zig b/src/Driver/GCCDetector.zig index 6f544f6e..21c11550 100644 --- a/src/Driver/GCCDetector.zig +++ b/src/Driver/GCCDetector.zig @@ -476,14 +476,13 @@ pub fn discover(self: *GCCDetector, tc: *Toolchain) !void { } } -fn findBiarchMultilibs(self: *GCCDetector, tc: *const Toolchain, result: *Multilib.Detected, target: std.Target, path: [2][]const u8, needs_biarch_suffix: bool) !bool { +fn findBiarchMultilibs(tc: *const Toolchain, result: *Multilib.Detected, target: std.Target, path: [2][]const u8, needs_biarch_suffix: bool) !bool { const suff64 = if (target.os.tag == .solaris) switch (target.cpu.arch) { .x86, .x86_64 => "/amd64", .sparc => "/sparcv9", else => "/64", } else "/64"; - _ = self; const alt_64 = Multilib.init(suff64, suff64, &.{ "-m32", "+m64", "-mx32" }); const alt_32 = Multilib.init("/32", "/32", &.{ "+m32", "-m64", "-mx32" }); const alt_x32 = Multilib.init("/x32", "/x32", &.{ "-m32", "-m64", "+mx32" }); @@ -544,7 +543,7 @@ fn scanGCCForMultilibs(self: *GCCDetector, tc: *const Toolchain, target: std.Tar // TODO } else if (target.cpu.arch == .avr) { // No multilibs - } else if (!try self.findBiarchMultilibs(tc, &detected, target, path, needs_biarch_suffix)) { + } else if (!try findBiarchMultilibs(tc, &detected, target, path, needs_biarch_suffix)) { return false; } self.selected = detected.selected; @@ -579,7 +578,7 @@ fn scanLibDirForGCCTriple( defer parent_dir.close(); var it = parent_dir.iterate(); - while (try it.next()) |entry| { + while (it.next() catch continue) |entry| { if (entry.kind != .directory) continue; const version_text = entry.name; From d13b5e69db674092def5d3425c62ab9cefaca4f6 Mon Sep 17 00:00:00 2001 From: Evan Haas Date: Fri, 25 Aug 2023 14:21:26 -0600 Subject: [PATCH 49/50] main: return u8 instead of an error union from main --- src/main.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main.zig b/src/main.zig index 824bda55..d9fb55c5 100644 --- a/src/main.zig +++ b/src/main.zig @@ -9,7 +9,7 @@ const Toolchain = @import("Toolchain.zig"); var general_purpose_allocator = std.heap.GeneralPurposeAllocator(.{}){}; -pub fn main() !u8 { +pub fn main() u8 { const gpa = if (@import("builtin").link_libc) std.heap.raw_c_allocator else From ff76738c73ea20f1018ef481105397541597c641 Mon Sep 17 00:00:00 2001 From: Evan Haas Date: Fri, 25 Aug 2023 14:34:36 -0600 Subject: [PATCH 50/50] Multilib: make MultilibArray its own type --- src/Driver/Multilib.zig | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/Driver/Multilib.zig b/src/Driver/Multilib.zig index 56cf755b..673ae6f2 100644 --- a/src/Driver/Multilib.zig +++ b/src/Driver/Multilib.zig @@ -4,8 +4,13 @@ const Filesystem = @import("Filesystem.zig").Filesystem; pub const Flags = std.BoundedArray([]const u8, 6); +/// Large enough for GCCDetector for Linux; may need to be increased to support other toolchains. +const max_multilibs = 4; + +const MultilibArray = std.BoundedArray(Multilib, max_multilibs); + pub const Detected = struct { - multilibs: std.BoundedArray(Multilib, 4) = .{}, + multilibs: MultilibArray = .{}, selected: Multilib = .{}, biarch_sibling: ?Multilib = null, @@ -21,7 +26,7 @@ pub const Detected = struct { } pub fn select(self: *Detected, flags: Flags) !bool { - var filtered: std.BoundedArray(Multilib, 4) = .{}; + var filtered: MultilibArray = .{}; for (self.multilibs.constSlice()) |multilib| { for (multilib.flags.constSlice()) |multilib_flag| { const matched = for (flags.constSlice()) |arg_flag| {