From 36e2d992dd8c45ca89a51d508c6c413cff5ad2cd Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 31 Jan 2023 00:19:51 -0700 Subject: combine std.build and std.build.Builder into std.Build I've been wanting to do this for along time. --- lib/std/Build/InstallRawStep.zig | 110 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 110 insertions(+) create mode 100644 lib/std/Build/InstallRawStep.zig (limited to 'lib/std/Build/InstallRawStep.zig') diff --git a/lib/std/Build/InstallRawStep.zig b/lib/std/Build/InstallRawStep.zig new file mode 100644 index 0000000000..08d646ff88 --- /dev/null +++ b/lib/std/Build/InstallRawStep.zig @@ -0,0 +1,110 @@ +//! TODO: Rename this to ObjCopyStep now that it invokes the `zig objcopy` +//! subcommand rather than containing an implementation directly. + +const std = @import("std"); +const InstallRawStep = @This(); + +const Allocator = std.mem.Allocator; +const ArenaAllocator = std.heap.ArenaAllocator; +const ArrayListUnmanaged = std.ArrayListUnmanaged; +const File = std.fs.File; +const InstallDir = std.Build.InstallDir; +const LibExeObjStep = std.Build.LibExeObjStep; +const Step = std.Build.Step; +const elf = std.elf; +const fs = std.fs; +const io = std.io; +const sort = std.sort; + +pub const base_id = .install_raw; + +pub const RawFormat = enum { + bin, + hex, +}; + +step: Step, +builder: *std.Build, +artifact: *LibExeObjStep, +dest_dir: InstallDir, +dest_filename: []const u8, +options: CreateOptions, +output_file: std.Build.GeneratedFile, + +pub const CreateOptions = struct { + format: ?RawFormat = null, + dest_dir: ?InstallDir = null, + only_section: ?[]const u8 = null, + pad_to: ?u64 = null, +}; + +pub fn create( + builder: *std.Build, + artifact: *LibExeObjStep, + dest_filename: []const u8, + options: CreateOptions, +) *InstallRawStep { + const self = builder.allocator.create(InstallRawStep) catch unreachable; + self.* = InstallRawStep{ + .step = Step.init(.install_raw, builder.fmt("install raw binary {s}", .{artifact.step.name}), builder.allocator, make), + .builder = builder, + .artifact = artifact, + .dest_dir = if (options.dest_dir) |d| d else switch (artifact.kind) { + .obj => unreachable, + .@"test" => unreachable, + .exe, .test_exe => .bin, + .lib => unreachable, + }, + .dest_filename = dest_filename, + .options = options, + .output_file = std.Build.GeneratedFile{ .step = &self.step }, + }; + self.step.dependOn(&artifact.step); + + builder.pushInstalledFile(self.dest_dir, dest_filename); + return self; +} + +pub fn getOutputSource(self: *const InstallRawStep) std.Build.FileSource { + return std.Build.FileSource{ .generated = &self.output_file }; +} + +fn make(step: *Step) !void { + const self = @fieldParentPtr(InstallRawStep, "step", step); + const b = self.builder; + + if (self.artifact.target.getObjectFormat() != .elf) { + std.debug.print("InstallRawStep only works with ELF format.\n", .{}); + return error.InvalidObjectFormat; + } + + const full_src_path = self.artifact.getOutputSource().getPath(b); + const full_dest_path = b.getInstallPath(self.dest_dir, self.dest_filename); + self.output_file.path = full_dest_path; + + fs.cwd().makePath(b.getInstallPath(self.dest_dir, "")) catch unreachable; + + var argv_list = std.ArrayList([]const u8).init(b.allocator); + try argv_list.appendSlice(&.{ b.zig_exe, "objcopy" }); + + if (self.options.only_section) |only_section| { + try argv_list.appendSlice(&.{ "-j", only_section }); + } + if (self.options.pad_to) |pad_to| { + try argv_list.appendSlice(&.{ + "--pad-to", + b.fmt("{d}", .{pad_to}), + }); + } + if (self.options.format) |format| switch (format) { + .bin => try argv_list.appendSlice(&.{ "-O", "binary" }), + .hex => try argv_list.appendSlice(&.{ "-O", "hex" }), + }; + + try argv_list.appendSlice(&.{ full_src_path, full_dest_path }); + _ = try self.builder.execFromStep(argv_list.items, &self.step); +} + +test { + std.testing.refAllDecls(InstallRawStep); +} -- cgit v1.2.3 From 16cdd1297ebfac534615eaeb8439a4e1de71837c Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 31 Jan 2023 13:44:06 -0700 Subject: rename std.Build.LibExeObjStep to std.Build.CompileStep This matches the nomenclature internally: a Compilation is the main type that represents a single invokation of the compiler. --- build.zig | 12 +- lib/std/Build.zig | 49 +- lib/std/Build/CheckObjectStep.zig | 2 +- lib/std/Build/CompileStep.zig | 2045 +++++++++++++++++++++++++++ lib/std/Build/EmulatableRunStep.zig | 6 +- lib/std/Build/InstallArtifactStep.zig | 8 +- lib/std/Build/InstallRawStep.zig | 6 +- lib/std/Build/LibExeObjStep.zig | 2045 --------------------------- lib/std/Build/OptionsStep.zig | 6 +- lib/std/Build/RunStep.zig | 10 +- lib/std/Build/Step.zig | 4 +- lib/std/Build/TranslateCStep.zig | 6 +- lib/std/std.zig | 2 + test/link/macho/bugs/13457/build.zig | 1 - test/link/macho/dead_strip/build.zig | 7 +- test/link/macho/dead_strip_dylibs/build.zig | 3 +- test/link/macho/headerpad/build.zig | 3 +- test/link/macho/needed_framework/build.zig | 1 - test/link/macho/needed_library/build.zig | 1 - test/link/macho/search_strategy/build.zig | 7 +- test/link/macho/strict_validation/build.zig | 1 - test/link/macho/unwind_info/build.zig | 7 +- test/link/macho/uuid/build.zig | 7 +- test/link/macho/weak_framework/build.zig | 1 - test/link/macho/weak_library/build.zig | 1 - test/tests.zig | 10 +- 26 files changed, 2130 insertions(+), 2121 deletions(-) create mode 100644 lib/std/Build/CompileStep.zig delete mode 100644 lib/std/Build/LibExeObjStep.zig (limited to 'lib/std/Build/InstallRawStep.zig') diff --git a/build.zig b/build.zig index 713eac6517..ecf51fe316 100644 --- a/build.zig +++ b/build.zig @@ -516,7 +516,7 @@ fn addCompilerStep( b: *std.Build, optimize: std.builtin.OptimizeMode, target: std.zig.CrossTarget, -) *std.Build.LibExeObjStep { +) *std.Build.CompileStep { const exe = b.addExecutable(.{ .name = "zig", .root_source_file = .{ .path = "src/main.zig" }, @@ -544,7 +544,7 @@ const exe_cflags = [_][]const u8{ fn addCmakeCfgOptionsToExe( b: *std.Build, cfg: CMakeConfig, - exe: *std.Build.LibExeObjStep, + exe: *std.Build.CompileStep, use_zig_libcxx: bool, ) !void { if (exe.target.isDarwin()) { @@ -623,7 +623,7 @@ fn addCmakeCfgOptionsToExe( } } -fn addStaticLlvmOptionsToExe(exe: *std.Build.LibExeObjStep) !void { +fn addStaticLlvmOptionsToExe(exe: *std.Build.CompileStep) !void { // Adds the Zig C++ sources which both stage1 and stage2 need. // // We need this because otherwise zig_clang_cc1_main.cpp ends up pulling @@ -662,7 +662,7 @@ fn addStaticLlvmOptionsToExe(exe: *std.Build.LibExeObjStep) !void { fn addCxxKnownPath( b: *std.Build, ctx: CMakeConfig, - exe: *std.Build.LibExeObjStep, + exe: *std.Build.CompileStep, objname: []const u8, errtxt: ?[]const u8, need_cpp_includes: bool, @@ -695,7 +695,7 @@ fn addCxxKnownPath( } } -fn addCMakeLibraryList(exe: *std.Build.LibExeObjStep, list: []const u8) void { +fn addCMakeLibraryList(exe: *std.Build.CompileStep, list: []const u8) void { var it = mem.tokenize(u8, list, ";"); while (it.next()) |lib| { if (mem.startsWith(u8, lib, "-l")) { @@ -709,7 +709,7 @@ fn addCMakeLibraryList(exe: *std.Build.LibExeObjStep, list: []const u8) void { } const CMakeConfig = struct { - llvm_linkage: std.Build.LibExeObjStep.Linkage, + llvm_linkage: std.Build.CompileStep.Linkage, cmake_binary_dir: []const u8, cmake_prefix_path: []const u8, cmake_static_library_prefix: []const u8, diff --git a/lib/std/Build.zig b/lib/std/Build.zig index c0152ef6f1..4647baa398 100644 --- a/lib/std/Build.zig +++ b/lib/std/Build.zig @@ -19,6 +19,9 @@ const NativeTargetInfo = std.zig.system.NativeTargetInfo; const Sha256 = std.crypto.hash.sha2.Sha256; const Build = @This(); +///// deprecated: use `CompileStep` instead. +//pub const LibExeObjStep = CompileStep; + pub const Step = @import("Build/Step.zig"); pub const CheckFileStep = @import("Build/CheckFileStep.zig"); pub const CheckObjectStep = @import("Build/CheckObjectStep.zig"); @@ -29,7 +32,7 @@ pub const InstallArtifactStep = @import("Build/InstallArtifactStep.zig"); pub const InstallDirStep = @import("Build/InstallDirStep.zig"); pub const InstallFileStep = @import("Build/InstallFileStep.zig"); pub const InstallRawStep = @import("Build/InstallRawStep.zig"); -pub const LibExeObjStep = @import("Build/LibExeObjStep.zig"); +pub const CompileStep = @import("Build/CompileStep.zig"); pub const LogStep = @import("Build/LogStep.zig"); pub const OptionsStep = @import("Build/OptionsStep.zig"); pub const RemoveDirStep = @import("Build/RemoveDirStep.zig"); @@ -423,11 +426,11 @@ pub const ExecutableOptions = struct { version: ?std.builtin.Version = null, target: CrossTarget = .{}, optimize: std.builtin.Mode = .Debug, - linkage: ?LibExeObjStep.Linkage = null, + linkage: ?CompileStep.Linkage = null, }; -pub fn addExecutable(b: *Build, options: ExecutableOptions) *LibExeObjStep { - return LibExeObjStep.create(b, .{ +pub fn addExecutable(b: *Build, options: ExecutableOptions) *CompileStep { + return CompileStep.create(b, .{ .name = options.name, .root_source_file = options.root_source_file, .version = options.version, @@ -445,8 +448,8 @@ pub const ObjectOptions = struct { optimize: std.builtin.Mode, }; -pub fn addObject(b: *Build, options: ObjectOptions) *LibExeObjStep { - return LibExeObjStep.create(b, .{ +pub fn addObject(b: *Build, options: ObjectOptions) *CompileStep { + return CompileStep.create(b, .{ .name = options.name, .root_source_file = options.root_source_file, .target = options.target, @@ -463,8 +466,8 @@ pub const SharedLibraryOptions = struct { optimize: std.builtin.Mode, }; -pub fn addSharedLibrary(b: *Build, options: SharedLibraryOptions) *LibExeObjStep { - return LibExeObjStep.create(b, .{ +pub fn addSharedLibrary(b: *Build, options: SharedLibraryOptions) *CompileStep { + return CompileStep.create(b, .{ .name = options.name, .root_source_file = options.root_source_file, .kind = .lib, @@ -483,8 +486,8 @@ pub const StaticLibraryOptions = struct { version: ?std.builtin.Version = null, }; -pub fn addStaticLibrary(b: *Build, options: StaticLibraryOptions) *LibExeObjStep { - return LibExeObjStep.create(b, .{ +pub fn addStaticLibrary(b: *Build, options: StaticLibraryOptions) *CompileStep { + return CompileStep.create(b, .{ .name = options.name, .root_source_file = options.root_source_file, .kind = .lib, @@ -497,15 +500,15 @@ pub fn addStaticLibrary(b: *Build, options: StaticLibraryOptions) *LibExeObjStep pub const TestOptions = struct { name: []const u8 = "test", - kind: LibExeObjStep.Kind = .@"test", + kind: CompileStep.Kind = .@"test", root_source_file: FileSource, target: CrossTarget = .{}, optimize: std.builtin.Mode = .Debug, version: ?std.builtin.Version = null, }; -pub fn addTest(b: *Build, options: TestOptions) *LibExeObjStep { - return LibExeObjStep.create(b, .{ +pub fn addTest(b: *Build, options: TestOptions) *CompileStep { + return CompileStep.create(b, .{ .name = options.name, .kind = options.kind, .root_source_file = options.root_source_file, @@ -521,8 +524,8 @@ pub const AssemblyOptions = struct { optimize: std.builtin.Mode, }; -pub fn addAssembly(b: *Build, options: AssemblyOptions) *LibExeObjStep { - const obj_step = LibExeObjStep.create(b, .{ +pub fn addAssembly(b: *Build, options: AssemblyOptions) *CompileStep { + const obj_step = CompileStep.create(b, .{ .name = options.name, .root_source_file = null, .target = options.target, @@ -536,7 +539,7 @@ pub fn addAssembly(b: *Build, options: AssemblyOptions) *LibExeObjStep { /// executable. More command line arguments can be added with `addArg`, /// `addArgs`, and `addArtifactArg`. /// Be careful using this function, as it introduces a system dependency. -/// To run an executable built with zig build, see `LibExeObjStep.run`. +/// To run an executable built with zig build, see `CompileStep.run`. pub fn addSystemCommand(self: *Build, argv: []const []const u8) *RunStep { assert(argv.len >= 1); const run_step = RunStep.create(self, self.fmt("run {s}", .{argv[0]})); @@ -1167,11 +1170,11 @@ pub fn makePath(self: *Build, path: []const u8) !void { }; } -pub fn installArtifact(self: *Build, artifact: *LibExeObjStep) void { +pub fn installArtifact(self: *Build, artifact: *CompileStep) void { self.getInstallStep().dependOn(&self.addInstallArtifact(artifact).step); } -pub fn addInstallArtifact(self: *Build, artifact: *LibExeObjStep) *InstallArtifactStep { +pub fn addInstallArtifact(self: *Build, artifact: *CompileStep) *InstallArtifactStep { return InstallArtifactStep.create(self, artifact); } @@ -1195,7 +1198,7 @@ pub fn installLibFile(self: *Build, src_path: []const u8, dest_rel_path: []const } /// Output format (BIN vs Intel HEX) determined by filename -pub fn installRaw(self: *Build, artifact: *LibExeObjStep, dest_filename: []const u8, options: InstallRawStep.CreateOptions) *InstallRawStep { +pub fn installRaw(self: *Build, artifact: *CompileStep, dest_filename: []const u8, options: InstallRawStep.CreateOptions) *InstallRawStep { const raw = self.addInstallRaw(artifact, dest_filename, options); self.getInstallStep().dependOn(&raw.step); return raw; @@ -1220,7 +1223,7 @@ pub fn addInstallHeaderFile(b: *Build, src_path: []const u8, dest_rel_path: []co return b.addInstallFileWithDir(.{ .path = src_path }, .header, dest_rel_path); } -pub fn addInstallRaw(self: *Build, artifact: *LibExeObjStep, dest_filename: []const u8, options: InstallRawStep.CreateOptions) *InstallRawStep { +pub fn addInstallRaw(self: *Build, artifact: *CompileStep, dest_filename: []const u8, options: InstallRawStep.CreateOptions) *InstallRawStep { return InstallRawStep.create(self, artifact, dest_filename, options); } @@ -1456,8 +1459,8 @@ pub fn getInstallPath(self: *Build, dir: InstallDir, dest_rel_path: []const u8) pub const Dependency = struct { builder: *Build, - pub fn artifact(d: *Dependency, name: []const u8) *LibExeObjStep { - var found: ?*LibExeObjStep = null; + pub fn artifact(d: *Dependency, name: []const u8) *CompileStep { + var found: ?*CompileStep = null; for (d.builder.install_tls.step.dependencies.items) |dep_step| { const inst = dep_step.cast(InstallArtifactStep) orelse continue; if (mem.eql(u8, inst.artifact.name, name)) { @@ -1767,7 +1770,7 @@ test { _ = InstallDirStep; _ = InstallFileStep; _ = InstallRawStep; - _ = LibExeObjStep; + _ = CompileStep; _ = LogStep; _ = OptionsStep; _ = RemoveDirStep; diff --git a/lib/std/Build/CheckObjectStep.zig b/lib/std/Build/CheckObjectStep.zig index 7907be1787..bfa5338927 100644 --- a/lib/std/Build/CheckObjectStep.zig +++ b/lib/std/Build/CheckObjectStep.zig @@ -42,7 +42,7 @@ pub fn runAndCompare(self: *CheckObjectStep) *EmulatableRunStep { const dependencies_len = self.step.dependencies.items.len; assert(dependencies_len > 0); const exe_step = self.step.dependencies.items[dependencies_len - 1]; - const exe = exe_step.cast(std.Build.LibExeObjStep).?; + const exe = exe_step.cast(std.Build.CompileStep).?; const emulatable_step = EmulatableRunStep.create(self.builder, "EmulatableRun", exe); emulatable_step.step.dependOn(&self.step); return emulatable_step; diff --git a/lib/std/Build/CompileStep.zig b/lib/std/Build/CompileStep.zig new file mode 100644 index 0000000000..5257c85a5c --- /dev/null +++ b/lib/std/Build/CompileStep.zig @@ -0,0 +1,2045 @@ +const builtin = @import("builtin"); +const std = @import("../std.zig"); +const mem = std.mem; +const log = std.log; +const fs = std.fs; +const assert = std.debug.assert; +const panic = std.debug.panic; +const ArrayList = std.ArrayList; +const StringHashMap = std.StringHashMap; +const Sha256 = std.crypto.hash.sha2.Sha256; +const Allocator = mem.Allocator; +const Step = std.Build.Step; +const CrossTarget = std.zig.CrossTarget; +const NativeTargetInfo = std.zig.system.NativeTargetInfo; +const FileSource = std.Build.FileSource; +const PkgConfigPkg = std.Build.PkgConfigPkg; +const PkgConfigError = std.Build.PkgConfigError; +const ExecError = std.Build.ExecError; +const Pkg = std.Build.Pkg; +const VcpkgRoot = std.Build.VcpkgRoot; +const InstallDir = std.Build.InstallDir; +const InstallArtifactStep = std.Build.InstallArtifactStep; +const GeneratedFile = std.Build.GeneratedFile; +const InstallRawStep = std.Build.InstallRawStep; +const EmulatableRunStep = std.Build.EmulatableRunStep; +const CheckObjectStep = std.Build.CheckObjectStep; +const RunStep = std.Build.RunStep; +const OptionsStep = std.Build.OptionsStep; +const ConfigHeaderStep = std.Build.ConfigHeaderStep; +const CompileStep = @This(); + +pub const base_id: Step.Id = .compile; + +step: Step, +builder: *std.Build, +name: []const u8, +target: CrossTarget, +target_info: NativeTargetInfo, +optimize: std.builtin.Mode, +linker_script: ?FileSource = null, +version_script: ?[]const u8 = null, +out_filename: []const u8, +linkage: ?Linkage = null, +version: ?std.builtin.Version, +kind: Kind, +major_only_filename: ?[]const u8, +name_only_filename: ?[]const u8, +strip: ?bool, +unwind_tables: ?bool, +// keep in sync with src/link.zig:CompressDebugSections +compress_debug_sections: enum { none, zlib } = .none, +lib_paths: ArrayList([]const u8), +rpaths: ArrayList([]const u8), +framework_dirs: ArrayList([]const u8), +frameworks: StringHashMap(FrameworkLinkInfo), +verbose_link: bool, +verbose_cc: bool, +emit_analysis: EmitOption = .default, +emit_asm: EmitOption = .default, +emit_bin: EmitOption = .default, +emit_docs: EmitOption = .default, +emit_implib: EmitOption = .default, +emit_llvm_bc: EmitOption = .default, +emit_llvm_ir: EmitOption = .default, +// Lots of things depend on emit_h having a consistent path, +// so it is not an EmitOption for now. +emit_h: bool = false, +bundle_compiler_rt: ?bool = null, +single_threaded: ?bool = null, +stack_protector: ?bool = null, +disable_stack_probing: bool, +disable_sanitize_c: bool, +sanitize_thread: bool, +rdynamic: bool, +import_memory: bool = false, +/// For WebAssembly targets, this will allow for undefined symbols to +/// be imported from the host environment. +import_symbols: bool = false, +import_table: bool = false, +export_table: bool = false, +initial_memory: ?u64 = null, +max_memory: ?u64 = null, +shared_memory: bool = false, +global_base: ?u64 = null, +c_std: std.Build.CStd, +override_lib_dir: ?[]const u8, +main_pkg_path: ?[]const u8, +exec_cmd_args: ?[]const ?[]const u8, +name_prefix: []const u8, +filter: ?[]const u8, +test_evented_io: bool = false, +test_runner: ?[]const u8, +code_model: std.builtin.CodeModel = .default, +wasi_exec_model: ?std.builtin.WasiExecModel = null, +/// Symbols to be exported when compiling to wasm +export_symbol_names: []const []const u8 = &.{}, + +root_src: ?FileSource, +out_h_filename: []const u8, +out_lib_filename: []const u8, +out_pdb_filename: []const u8, +packages: ArrayList(Pkg), + +object_src: []const u8, + +link_objects: ArrayList(LinkObject), +include_dirs: ArrayList(IncludeDir), +c_macros: ArrayList([]const u8), +installed_headers: ArrayList(*Step), +output_dir: ?[]const u8, +is_linking_libc: bool = false, +is_linking_libcpp: bool = false, +vcpkg_bin_path: ?[]const u8 = null, + +/// This may be set in order to override the default install directory +override_dest_dir: ?InstallDir, +installed_path: ?[]const u8, +install_step: ?*InstallArtifactStep, + +/// Base address for an executable image. +image_base: ?u64 = null, + +libc_file: ?FileSource = null, + +valgrind_support: ?bool = null, +each_lib_rpath: ?bool = null, +/// On ELF targets, this will emit a link section called ".note.gnu.build-id" +/// which can be used to coordinate a stripped binary with its debug symbols. +/// As an example, the bloaty project refuses to work unless its inputs have +/// build ids, in order to prevent accidental mismatches. +/// The default is to not include this section because it slows down linking. +build_id: ?bool = null, + +/// Create a .eh_frame_hdr section and a PT_GNU_EH_FRAME segment in the ELF +/// file. +link_eh_frame_hdr: bool = false, +link_emit_relocs: bool = false, + +/// Place every function in its own section so that unused ones may be +/// safely garbage-collected during the linking phase. +link_function_sections: bool = false, + +/// Remove functions and data that are unreachable by the entry point or +/// exported symbols. +link_gc_sections: ?bool = null, + +linker_allow_shlib_undefined: ?bool = null, + +/// Permit read-only relocations in read-only segments. Disallowed by default. +link_z_notext: bool = false, + +/// Force all relocations to be read-only after processing. +link_z_relro: bool = true, + +/// Allow relocations to be lazily processed after load. +link_z_lazy: bool = false, + +/// Common page size +link_z_common_page_size: ?u64 = null, + +/// Maximum page size +link_z_max_page_size: ?u64 = null, + +/// (Darwin) Install name for the dylib +install_name: ?[]const u8 = null, + +/// (Darwin) Path to entitlements file +entitlements: ?[]const u8 = null, + +/// (Darwin) Size of the pagezero segment. +pagezero_size: ?u64 = null, + +/// (Darwin) Search strategy for searching system libraries. Either `paths_first` or `dylibs_first`. +/// The former lowers to `-search_paths_first` linker option, while the latter to `-search_dylibs_first` +/// option. +/// By default, if no option is specified, the linker assumes `paths_first` as the default +/// search strategy. +search_strategy: ?enum { paths_first, dylibs_first } = null, + +/// (Darwin) Set size of the padding between the end of load commands +/// and start of `__TEXT,__text` section. +headerpad_size: ?u32 = null, + +/// (Darwin) Automatically Set size of the padding between the end of load commands +/// and start of `__TEXT,__text` section to a value fitting all paths expanded to MAXPATHLEN. +headerpad_max_install_names: bool = false, + +/// (Darwin) Remove dylibs that are unreachable by the entry point or exported symbols. +dead_strip_dylibs: bool = false, + +/// Position Independent Code +force_pic: ?bool = null, + +/// Position Independent Executable +pie: ?bool = null, + +red_zone: ?bool = null, + +omit_frame_pointer: ?bool = null, +dll_export_fns: ?bool = null, + +subsystem: ?std.Target.SubSystem = null, + +entry_symbol_name: ?[]const u8 = null, + +/// Overrides the default stack size +stack_size: ?u64 = null, + +want_lto: ?bool = null, +use_llvm: ?bool = null, +use_lld: ?bool = null, + +output_path_source: GeneratedFile, +output_lib_path_source: GeneratedFile, +output_h_path_source: GeneratedFile, +output_pdb_path_source: GeneratedFile, + +pub const CSourceFiles = struct { + files: []const []const u8, + flags: []const []const u8, +}; + +pub const CSourceFile = struct { + source: FileSource, + args: []const []const u8, + + pub fn dupe(self: CSourceFile, b: *std.Build) CSourceFile { + return .{ + .source = self.source.dupe(b), + .args = b.dupeStrings(self.args), + }; + } +}; + +pub const LinkObject = union(enum) { + static_path: FileSource, + other_step: *CompileStep, + system_lib: SystemLib, + assembly_file: FileSource, + c_source_file: *CSourceFile, + c_source_files: *CSourceFiles, +}; + +pub const SystemLib = struct { + name: []const u8, + needed: bool, + weak: bool, + use_pkg_config: enum { + /// Don't use pkg-config, just pass -lfoo where foo is name. + no, + /// Try to get information on how to link the library from pkg-config. + /// If that fails, fall back to passing -lfoo where foo is name. + yes, + /// Try to get information on how to link the library from pkg-config. + /// If that fails, error out. + force, + }, +}; + +const FrameworkLinkInfo = struct { + needed: bool = false, + weak: bool = false, +}; + +pub const IncludeDir = union(enum) { + raw_path: []const u8, + raw_path_system: []const u8, + other_step: *CompileStep, + config_header_step: *ConfigHeaderStep, +}; + +pub const Options = struct { + name: []const u8, + root_source_file: ?FileSource = null, + target: CrossTarget, + optimize: std.builtin.Mode, + kind: Kind, + linkage: ?Linkage = null, + version: ?std.builtin.Version = null, +}; + +pub const Kind = enum { + exe, + lib, + obj, + @"test", + test_exe, +}; + +pub const Linkage = enum { dynamic, static }; + +pub const EmitOption = union(enum) { + default: void, + no_emit: void, + emit: void, + emit_to: []const u8, + + fn getArg(self: @This(), b: *std.Build, arg_name: []const u8) ?[]const u8 { + return switch (self) { + .no_emit => b.fmt("-fno-{s}", .{arg_name}), + .default => null, + .emit => b.fmt("-f{s}", .{arg_name}), + .emit_to => |path| b.fmt("-f{s}={s}", .{ arg_name, path }), + }; + } +}; + +pub fn create(builder: *std.Build, options: Options) *CompileStep { + const name = builder.dupe(options.name); + const root_src: ?FileSource = if (options.root_source_file) |rsrc| rsrc.dupe(builder) else null; + if (mem.indexOf(u8, name, "/") != null or mem.indexOf(u8, name, "\\") != null) { + panic("invalid name: '{s}'. It looks like a file path, but it is supposed to be the library or application name.", .{name}); + } + + const self = builder.allocator.create(CompileStep) catch unreachable; + self.* = CompileStep{ + .strip = null, + .unwind_tables = null, + .builder = builder, + .verbose_link = false, + .verbose_cc = false, + .optimize = options.optimize, + .target = options.target, + .linkage = options.linkage, + .kind = options.kind, + .root_src = root_src, + .name = name, + .frameworks = StringHashMap(FrameworkLinkInfo).init(builder.allocator), + .step = Step.init(base_id, name, builder.allocator, make), + .version = options.version, + .out_filename = undefined, + .out_h_filename = builder.fmt("{s}.h", .{name}), + .out_lib_filename = undefined, + .out_pdb_filename = builder.fmt("{s}.pdb", .{name}), + .major_only_filename = null, + .name_only_filename = null, + .packages = ArrayList(Pkg).init(builder.allocator), + .include_dirs = ArrayList(IncludeDir).init(builder.allocator), + .link_objects = ArrayList(LinkObject).init(builder.allocator), + .c_macros = ArrayList([]const u8).init(builder.allocator), + .lib_paths = ArrayList([]const u8).init(builder.allocator), + .rpaths = ArrayList([]const u8).init(builder.allocator), + .framework_dirs = ArrayList([]const u8).init(builder.allocator), + .installed_headers = ArrayList(*Step).init(builder.allocator), + .object_src = undefined, + .c_std = std.Build.CStd.C99, + .override_lib_dir = null, + .main_pkg_path = null, + .exec_cmd_args = null, + .name_prefix = "", + .filter = null, + .test_runner = null, + .disable_stack_probing = false, + .disable_sanitize_c = false, + .sanitize_thread = false, + .rdynamic = false, + .output_dir = null, + .override_dest_dir = null, + .installed_path = null, + .install_step = null, + + .output_path_source = GeneratedFile{ .step = &self.step }, + .output_lib_path_source = GeneratedFile{ .step = &self.step }, + .output_h_path_source = GeneratedFile{ .step = &self.step }, + .output_pdb_path_source = GeneratedFile{ .step = &self.step }, + + .target_info = NativeTargetInfo.detect(self.target) catch unreachable, + }; + self.computeOutFileNames(); + if (root_src) |rs| rs.addStepDependencies(&self.step); + return self; +} + +fn computeOutFileNames(self: *CompileStep) void { + const target = self.target_info.target; + + self.out_filename = std.zig.binNameAlloc(self.builder.allocator, .{ + .root_name = self.name, + .target = target, + .output_mode = switch (self.kind) { + .lib => .Lib, + .obj => .Obj, + .exe, .@"test", .test_exe => .Exe, + }, + .link_mode = if (self.linkage) |some| @as(std.builtin.LinkMode, switch (some) { + .dynamic => .Dynamic, + .static => .Static, + }) else null, + .version = self.version, + }) catch unreachable; + + if (self.kind == .lib) { + if (self.linkage != null and self.linkage.? == .static) { + self.out_lib_filename = self.out_filename; + } else if (self.version) |version| { + if (target.isDarwin()) { + self.major_only_filename = self.builder.fmt("lib{s}.{d}.dylib", .{ + self.name, + version.major, + }); + self.name_only_filename = self.builder.fmt("lib{s}.dylib", .{self.name}); + self.out_lib_filename = self.out_filename; + } else if (target.os.tag == .windows) { + self.out_lib_filename = self.builder.fmt("{s}.lib", .{self.name}); + } else { + self.major_only_filename = self.builder.fmt("lib{s}.so.{d}", .{ self.name, version.major }); + self.name_only_filename = self.builder.fmt("lib{s}.so", .{self.name}); + self.out_lib_filename = self.out_filename; + } + } else { + if (target.isDarwin()) { + self.out_lib_filename = self.out_filename; + } else if (target.os.tag == .windows) { + self.out_lib_filename = self.builder.fmt("{s}.lib", .{self.name}); + } else { + self.out_lib_filename = self.out_filename; + } + } + if (self.output_dir != null) { + self.output_lib_path_source.path = self.builder.pathJoin( + &.{ self.output_dir.?, self.out_lib_filename }, + ); + } + } +} + +pub fn setOutputDir(self: *CompileStep, dir: []const u8) void { + self.output_dir = self.builder.dupePath(dir); +} + +pub fn install(self: *CompileStep) void { + self.builder.installArtifact(self); +} + +pub fn installRaw(self: *CompileStep, dest_filename: []const u8, options: InstallRawStep.CreateOptions) *InstallRawStep { + return self.builder.installRaw(self, dest_filename, options); +} + +pub fn installHeader(a: *CompileStep, src_path: []const u8, dest_rel_path: []const u8) void { + const install_file = a.builder.addInstallHeaderFile(src_path, dest_rel_path); + a.builder.getInstallStep().dependOn(&install_file.step); + a.installed_headers.append(&install_file.step) catch unreachable; +} + +pub fn installHeadersDirectory( + a: *CompileStep, + src_dir_path: []const u8, + dest_rel_path: []const u8, +) void { + return installHeadersDirectoryOptions(a, .{ + .source_dir = src_dir_path, + .install_dir = .header, + .install_subdir = dest_rel_path, + }); +} + +pub fn installHeadersDirectoryOptions( + a: *CompileStep, + options: std.Build.InstallDirStep.Options, +) void { + const install_dir = a.builder.addInstallDirectory(options); + a.builder.getInstallStep().dependOn(&install_dir.step); + a.installed_headers.append(&install_dir.step) catch unreachable; +} + +pub fn installLibraryHeaders(a: *CompileStep, l: *CompileStep) void { + assert(l.kind == .lib); + const install_step = a.builder.getInstallStep(); + // Copy each element from installed_headers, modifying the builder + // to be the new parent's builder. + for (l.installed_headers.items) |step| { + const step_copy = switch (step.id) { + inline .install_file, .install_dir => |id| blk: { + const T = id.Type(); + const ptr = a.builder.allocator.create(T) catch unreachable; + ptr.* = step.cast(T).?.*; + ptr.override_source_builder = ptr.builder; + ptr.builder = a.builder; + break :blk &ptr.step; + }, + else => unreachable, + }; + a.installed_headers.append(step_copy) catch unreachable; + install_step.dependOn(step_copy); + } + a.installed_headers.appendSlice(l.installed_headers.items) catch unreachable; +} + +/// Creates a `RunStep` with an executable built with `addExecutable`. +/// Add command line arguments with `addArg`. +pub fn run(exe: *CompileStep) *RunStep { + assert(exe.kind == .exe or exe.kind == .test_exe); + + // It doesn't have to be native. We catch that if you actually try to run it. + // Consider that this is declarative; the run step may not be run unless a user + // option is supplied. + const run_step = RunStep.create(exe.builder, exe.builder.fmt("run {s}", .{exe.step.name})); + run_step.addArtifactArg(exe); + + if (exe.kind == .test_exe) { + run_step.addArg(exe.builder.zig_exe); + } + + if (exe.vcpkg_bin_path) |path| { + run_step.addPathDir(path); + } + + return run_step; +} + +/// Creates an `EmulatableRunStep` with an executable built with `addExecutable`. +/// Allows running foreign binaries through emulation platforms such as Qemu or Rosetta. +/// When a binary cannot be ran through emulation or the option is disabled, a warning +/// will be printed and the binary will *NOT* be ran. +pub fn runEmulatable(exe: *CompileStep) *EmulatableRunStep { + assert(exe.kind == .exe or exe.kind == .test_exe); + + const run_step = EmulatableRunStep.create(exe.builder, exe.builder.fmt("run {s}", .{exe.step.name}), exe); + if (exe.vcpkg_bin_path) |path| { + RunStep.addPathDirInternal(&run_step.step, exe.builder, path); + } + return run_step; +} + +pub fn checkObject(self: *CompileStep, obj_format: std.Target.ObjectFormat) *CheckObjectStep { + return CheckObjectStep.create(self.builder, self.getOutputSource(), obj_format); +} + +pub fn setLinkerScriptPath(self: *CompileStep, source: FileSource) void { + self.linker_script = source.dupe(self.builder); + source.addStepDependencies(&self.step); +} + +pub fn linkFramework(self: *CompileStep, framework_name: []const u8) void { + self.frameworks.put(self.builder.dupe(framework_name), .{}) catch unreachable; +} + +pub fn linkFrameworkNeeded(self: *CompileStep, framework_name: []const u8) void { + self.frameworks.put(self.builder.dupe(framework_name), .{ + .needed = true, + }) catch unreachable; +} + +pub fn linkFrameworkWeak(self: *CompileStep, framework_name: []const u8) void { + self.frameworks.put(self.builder.dupe(framework_name), .{ + .weak = true, + }) catch unreachable; +} + +/// Returns whether the library, executable, or object depends on a particular system library. +pub fn dependsOnSystemLibrary(self: CompileStep, name: []const u8) bool { + if (isLibCLibrary(name)) { + return self.is_linking_libc; + } + if (isLibCppLibrary(name)) { + return self.is_linking_libcpp; + } + for (self.link_objects.items) |link_object| { + switch (link_object) { + .system_lib => |lib| if (mem.eql(u8, lib.name, name)) return true, + else => continue, + } + } + return false; +} + +pub fn linkLibrary(self: *CompileStep, lib: *CompileStep) void { + assert(lib.kind == .lib); + self.linkLibraryOrObject(lib); +} + +pub fn isDynamicLibrary(self: *CompileStep) bool { + return self.kind == .lib and self.linkage == Linkage.dynamic; +} + +pub fn isStaticLibrary(self: *CompileStep) bool { + return self.kind == .lib and self.linkage != Linkage.dynamic; +} + +pub fn producesPdbFile(self: *CompileStep) bool { + if (!self.target.isWindows() and !self.target.isUefi()) return false; + if (self.target.getObjectFormat() == .c) return false; + if (self.strip == true) return false; + return self.isDynamicLibrary() or self.kind == .exe or self.kind == .test_exe; +} + +pub fn linkLibC(self: *CompileStep) void { + self.is_linking_libc = true; +} + +pub fn linkLibCpp(self: *CompileStep) void { + self.is_linking_libcpp = true; +} + +/// If the value is omitted, it is set to 1. +/// `name` and `value` need not live longer than the function call. +pub fn defineCMacro(self: *CompileStep, name: []const u8, value: ?[]const u8) void { + const macro = std.Build.constructCMacro(self.builder.allocator, name, value); + self.c_macros.append(macro) catch unreachable; +} + +/// name_and_value looks like [name]=[value]. If the value is omitted, it is set to 1. +pub fn defineCMacroRaw(self: *CompileStep, name_and_value: []const u8) void { + self.c_macros.append(self.builder.dupe(name_and_value)) catch unreachable; +} + +/// This one has no integration with anything, it just puts -lname on the command line. +/// Prefer to use `linkSystemLibrary` instead. +pub fn linkSystemLibraryName(self: *CompileStep, name: []const u8) void { + self.link_objects.append(.{ + .system_lib = .{ + .name = self.builder.dupe(name), + .needed = false, + .weak = false, + .use_pkg_config = .no, + }, + }) catch unreachable; +} + +/// This one has no integration with anything, it just puts -needed-lname on the command line. +/// Prefer to use `linkSystemLibraryNeeded` instead. +pub fn linkSystemLibraryNeededName(self: *CompileStep, name: []const u8) void { + self.link_objects.append(.{ + .system_lib = .{ + .name = self.builder.dupe(name), + .needed = true, + .weak = false, + .use_pkg_config = .no, + }, + }) catch unreachable; +} + +/// Darwin-only. This one has no integration with anything, it just puts -weak-lname on the +/// command line. Prefer to use `linkSystemLibraryWeak` instead. +pub fn linkSystemLibraryWeakName(self: *CompileStep, name: []const u8) void { + self.link_objects.append(.{ + .system_lib = .{ + .name = self.builder.dupe(name), + .needed = false, + .weak = true, + .use_pkg_config = .no, + }, + }) catch unreachable; +} + +/// This links against a system library, exclusively using pkg-config to find the library. +/// Prefer to use `linkSystemLibrary` instead. +pub fn linkSystemLibraryPkgConfigOnly(self: *CompileStep, lib_name: []const u8) void { + self.link_objects.append(.{ + .system_lib = .{ + .name = self.builder.dupe(lib_name), + .needed = false, + .weak = false, + .use_pkg_config = .force, + }, + }) catch unreachable; +} + +/// This links against a system library, exclusively using pkg-config to find the library. +/// Prefer to use `linkSystemLibraryNeeded` instead. +pub fn linkSystemLibraryNeededPkgConfigOnly(self: *CompileStep, lib_name: []const u8) void { + self.link_objects.append(.{ + .system_lib = .{ + .name = self.builder.dupe(lib_name), + .needed = true, + .weak = false, + .use_pkg_config = .force, + }, + }) catch unreachable; +} + +/// Run pkg-config for the given library name and parse the output, returning the arguments +/// that should be passed to zig to link the given library. +pub fn runPkgConfig(self: *CompileStep, lib_name: []const u8) ![]const []const u8 { + const pkg_name = match: { + // First we have to map the library name to pkg config name. Unfortunately, + // there are several examples where this is not straightforward: + // -lSDL2 -> pkg-config sdl2 + // -lgdk-3 -> pkg-config gdk-3.0 + // -latk-1.0 -> pkg-config atk + const pkgs = try getPkgConfigList(self.builder); + + // Exact match means instant winner. + for (pkgs) |pkg| { + if (mem.eql(u8, pkg.name, lib_name)) { + break :match pkg.name; + } + } + + // Next we'll try ignoring case. + for (pkgs) |pkg| { + if (std.ascii.eqlIgnoreCase(pkg.name, lib_name)) { + break :match pkg.name; + } + } + + // Now try appending ".0". + for (pkgs) |pkg| { + if (std.ascii.indexOfIgnoreCase(pkg.name, lib_name)) |pos| { + if (pos != 0) continue; + if (mem.eql(u8, pkg.name[lib_name.len..], ".0")) { + break :match pkg.name; + } + } + } + + // Trimming "-1.0". + if (mem.endsWith(u8, lib_name, "-1.0")) { + const trimmed_lib_name = lib_name[0 .. lib_name.len - "-1.0".len]; + for (pkgs) |pkg| { + if (std.ascii.eqlIgnoreCase(pkg.name, trimmed_lib_name)) { + break :match pkg.name; + } + } + } + + return error.PackageNotFound; + }; + + var code: u8 = undefined; + const stdout = if (self.builder.execAllowFail(&[_][]const u8{ + "pkg-config", + pkg_name, + "--cflags", + "--libs", + }, &code, .Ignore)) |stdout| stdout else |err| switch (err) { + error.ProcessTerminated => return error.PkgConfigCrashed, + error.ExecNotSupported => return error.PkgConfigFailed, + error.ExitCodeFailure => return error.PkgConfigFailed, + error.FileNotFound => return error.PkgConfigNotInstalled, + error.ChildExecFailed => return error.PkgConfigFailed, + else => return err, + }; + + var zig_args = ArrayList([]const u8).init(self.builder.allocator); + defer zig_args.deinit(); + + var it = mem.tokenize(u8, stdout, " \r\n\t"); + while (it.next()) |tok| { + if (mem.eql(u8, tok, "-I")) { + const dir = it.next() orelse return error.PkgConfigInvalidOutput; + try zig_args.appendSlice(&[_][]const u8{ "-I", dir }); + } else if (mem.startsWith(u8, tok, "-I")) { + try zig_args.append(tok); + } else if (mem.eql(u8, tok, "-L")) { + const dir = it.next() orelse return error.PkgConfigInvalidOutput; + try zig_args.appendSlice(&[_][]const u8{ "-L", dir }); + } else if (mem.startsWith(u8, tok, "-L")) { + try zig_args.append(tok); + } else if (mem.eql(u8, tok, "-l")) { + const lib = it.next() orelse return error.PkgConfigInvalidOutput; + try zig_args.appendSlice(&[_][]const u8{ "-l", lib }); + } else if (mem.startsWith(u8, tok, "-l")) { + try zig_args.append(tok); + } else if (mem.eql(u8, tok, "-D")) { + const macro = it.next() orelse return error.PkgConfigInvalidOutput; + try zig_args.appendSlice(&[_][]const u8{ "-D", macro }); + } else if (mem.startsWith(u8, tok, "-D")) { + try zig_args.append(tok); + } else if (self.builder.verbose) { + log.warn("Ignoring pkg-config flag '{s}'", .{tok}); + } + } + + return zig_args.toOwnedSlice(); +} + +pub fn linkSystemLibrary(self: *CompileStep, name: []const u8) void { + self.linkSystemLibraryInner(name, .{}); +} + +pub fn linkSystemLibraryNeeded(self: *CompileStep, name: []const u8) void { + self.linkSystemLibraryInner(name, .{ .needed = true }); +} + +pub fn linkSystemLibraryWeak(self: *CompileStep, name: []const u8) void { + self.linkSystemLibraryInner(name, .{ .weak = true }); +} + +fn linkSystemLibraryInner(self: *CompileStep, name: []const u8, opts: struct { + needed: bool = false, + weak: bool = false, +}) void { + if (isLibCLibrary(name)) { + self.linkLibC(); + return; + } + if (isLibCppLibrary(name)) { + self.linkLibCpp(); + return; + } + + self.link_objects.append(.{ + .system_lib = .{ + .name = self.builder.dupe(name), + .needed = opts.needed, + .weak = opts.weak, + .use_pkg_config = .yes, + }, + }) catch unreachable; +} + +pub fn setNamePrefix(self: *CompileStep, text: []const u8) void { + assert(self.kind == .@"test" or self.kind == .test_exe); + self.name_prefix = self.builder.dupe(text); +} + +pub fn setFilter(self: *CompileStep, text: ?[]const u8) void { + assert(self.kind == .@"test" or self.kind == .test_exe); + self.filter = if (text) |t| self.builder.dupe(t) else null; +} + +pub fn setTestRunner(self: *CompileStep, path: ?[]const u8) void { + assert(self.kind == .@"test" or self.kind == .test_exe); + self.test_runner = if (path) |p| self.builder.dupePath(p) else null; +} + +/// Handy when you have many C/C++ source files and want them all to have the same flags. +pub fn addCSourceFiles(self: *CompileStep, files: []const []const u8, flags: []const []const u8) void { + const c_source_files = self.builder.allocator.create(CSourceFiles) catch unreachable; + + const files_copy = self.builder.dupeStrings(files); + const flags_copy = self.builder.dupeStrings(flags); + + c_source_files.* = .{ + .files = files_copy, + .flags = flags_copy, + }; + self.link_objects.append(.{ .c_source_files = c_source_files }) catch unreachable; +} + +pub fn addCSourceFile(self: *CompileStep, file: []const u8, flags: []const []const u8) void { + self.addCSourceFileSource(.{ + .args = flags, + .source = .{ .path = file }, + }); +} + +pub fn addCSourceFileSource(self: *CompileStep, source: CSourceFile) void { + const c_source_file = self.builder.allocator.create(CSourceFile) catch unreachable; + c_source_file.* = source.dupe(self.builder); + self.link_objects.append(.{ .c_source_file = c_source_file }) catch unreachable; + source.source.addStepDependencies(&self.step); +} + +pub fn setVerboseLink(self: *CompileStep, value: bool) void { + self.verbose_link = value; +} + +pub fn setVerboseCC(self: *CompileStep, value: bool) void { + self.verbose_cc = value; +} + +pub fn overrideZigLibDir(self: *CompileStep, dir_path: []const u8) void { + self.override_lib_dir = self.builder.dupePath(dir_path); +} + +pub fn setMainPkgPath(self: *CompileStep, dir_path: []const u8) void { + self.main_pkg_path = self.builder.dupePath(dir_path); +} + +pub fn setLibCFile(self: *CompileStep, libc_file: ?FileSource) void { + self.libc_file = if (libc_file) |f| f.dupe(self.builder) else null; +} + +/// Returns the generated executable, library or object file. +/// To run an executable built with zig build, use `run`, or create an install step and invoke it. +pub fn getOutputSource(self: *CompileStep) FileSource { + return FileSource{ .generated = &self.output_path_source }; +} + +/// Returns the generated import library. This function can only be called for libraries. +pub fn getOutputLibSource(self: *CompileStep) FileSource { + assert(self.kind == .lib); + return FileSource{ .generated = &self.output_lib_path_source }; +} + +/// Returns the generated header file. +/// This function can only be called for libraries or object files which have `emit_h` set. +pub fn getOutputHSource(self: *CompileStep) FileSource { + assert(self.kind != .exe and self.kind != .test_exe and self.kind != .@"test"); + assert(self.emit_h); + return FileSource{ .generated = &self.output_h_path_source }; +} + +/// Returns the generated PDB file. This function can only be called for Windows and UEFI. +pub fn getOutputPdbSource(self: *CompileStep) FileSource { + // TODO: Is this right? Isn't PDB for *any* PE/COFF file? + assert(self.target.isWindows() or self.target.isUefi()); + return FileSource{ .generated = &self.output_pdb_path_source }; +} + +pub fn addAssemblyFile(self: *CompileStep, path: []const u8) void { + self.link_objects.append(.{ + .assembly_file = .{ .path = self.builder.dupe(path) }, + }) catch unreachable; +} + +pub fn addAssemblyFileSource(self: *CompileStep, source: FileSource) void { + const source_duped = source.dupe(self.builder); + self.link_objects.append(.{ .assembly_file = source_duped }) catch unreachable; + source_duped.addStepDependencies(&self.step); +} + +pub fn addObjectFile(self: *CompileStep, source_file: []const u8) void { + self.addObjectFileSource(.{ .path = source_file }); +} + +pub fn addObjectFileSource(self: *CompileStep, source: FileSource) void { + self.link_objects.append(.{ .static_path = source.dupe(self.builder) }) catch unreachable; + source.addStepDependencies(&self.step); +} + +pub fn addObject(self: *CompileStep, obj: *CompileStep) void { + assert(obj.kind == .obj); + self.linkLibraryOrObject(obj); +} + +pub const addSystemIncludeDir = @compileError("deprecated; use addSystemIncludePath"); +pub const addIncludeDir = @compileError("deprecated; use addIncludePath"); +pub const addLibPath = @compileError("deprecated, use addLibraryPath"); +pub const addFrameworkDir = @compileError("deprecated, use addFrameworkPath"); + +pub fn addSystemIncludePath(self: *CompileStep, path: []const u8) void { + self.include_dirs.append(IncludeDir{ .raw_path_system = self.builder.dupe(path) }) catch unreachable; +} + +pub fn addIncludePath(self: *CompileStep, path: []const u8) void { + self.include_dirs.append(IncludeDir{ .raw_path = self.builder.dupe(path) }) catch unreachable; +} + +pub fn addConfigHeader(self: *CompileStep, config_header: *ConfigHeaderStep) void { + self.step.dependOn(&config_header.step); + self.include_dirs.append(.{ .config_header_step = config_header }) catch @panic("OOM"); +} + +pub fn addLibraryPath(self: *CompileStep, path: []const u8) void { + self.lib_paths.append(self.builder.dupe(path)) catch unreachable; +} + +pub fn addRPath(self: *CompileStep, path: []const u8) void { + self.rpaths.append(self.builder.dupe(path)) catch unreachable; +} + +pub fn addFrameworkPath(self: *CompileStep, dir_path: []const u8) void { + self.framework_dirs.append(self.builder.dupe(dir_path)) catch unreachable; +} + +pub fn addPackage(self: *CompileStep, package: Pkg) void { + self.packages.append(self.builder.dupePkg(package)) catch unreachable; + self.addRecursiveBuildDeps(package); +} + +pub fn addOptions(self: *CompileStep, package_name: []const u8, options: *OptionsStep) void { + self.addPackage(options.getPackage(package_name)); +} + +fn addRecursiveBuildDeps(self: *CompileStep, package: Pkg) void { + package.source.addStepDependencies(&self.step); + if (package.dependencies) |deps| { + for (deps) |dep| { + self.addRecursiveBuildDeps(dep); + } + } +} + +pub fn addPackagePath(self: *CompileStep, name: []const u8, pkg_index_path: []const u8) void { + self.addPackage(Pkg{ + .name = self.builder.dupe(name), + .source = .{ .path = self.builder.dupe(pkg_index_path) }, + }); +} + +/// If Vcpkg was found on the system, it will be added to include and lib +/// paths for the specified target. +pub fn addVcpkgPaths(self: *CompileStep, linkage: CompileStep.Linkage) !void { + // Ideally in the Unattempted case we would call the function recursively + // after findVcpkgRoot and have only one switch statement, but the compiler + // cannot resolve the error set. + switch (self.builder.vcpkg_root) { + .unattempted => { + self.builder.vcpkg_root = if (try findVcpkgRoot(self.builder.allocator)) |root| + VcpkgRoot{ .found = root } + else + .not_found; + }, + .not_found => return error.VcpkgNotFound, + .found => {}, + } + + switch (self.builder.vcpkg_root) { + .unattempted => unreachable, + .not_found => return error.VcpkgNotFound, + .found => |root| { + const allocator = self.builder.allocator; + const triplet = try self.target.vcpkgTriplet(allocator, if (linkage == .static) .Static else .Dynamic); + defer self.builder.allocator.free(triplet); + + const include_path = self.builder.pathJoin(&.{ root, "installed", triplet, "include" }); + errdefer allocator.free(include_path); + try self.include_dirs.append(IncludeDir{ .raw_path = include_path }); + + const lib_path = self.builder.pathJoin(&.{ root, "installed", triplet, "lib" }); + try self.lib_paths.append(lib_path); + + self.vcpkg_bin_path = self.builder.pathJoin(&.{ root, "installed", triplet, "bin" }); + }, + } +} + +pub fn setExecCmd(self: *CompileStep, args: []const ?[]const u8) void { + assert(self.kind == .@"test"); + const duped_args = self.builder.allocator.alloc(?[]u8, args.len) catch unreachable; + for (args) |arg, i| { + duped_args[i] = if (arg) |a| self.builder.dupe(a) else null; + } + self.exec_cmd_args = duped_args; +} + +fn linkLibraryOrObject(self: *CompileStep, other: *CompileStep) void { + self.step.dependOn(&other.step); + self.link_objects.append(.{ .other_step = other }) catch unreachable; + self.include_dirs.append(.{ .other_step = other }) catch unreachable; +} + +fn makePackageCmd(self: *CompileStep, pkg: Pkg, zig_args: *ArrayList([]const u8)) error{OutOfMemory}!void { + const builder = self.builder; + + try zig_args.append("--pkg-begin"); + try zig_args.append(pkg.name); + try zig_args.append(builder.pathFromRoot(pkg.source.getPath(self.builder))); + + if (pkg.dependencies) |dependencies| { + for (dependencies) |sub_pkg| { + try self.makePackageCmd(sub_pkg, zig_args); + } + } + + try zig_args.append("--pkg-end"); +} + +fn make(step: *Step) !void { + const self = @fieldParentPtr(CompileStep, "step", step); + const builder = self.builder; + + if (self.root_src == null and self.link_objects.items.len == 0) { + log.err("{s}: linker needs 1 or more objects to link", .{self.step.name}); + return error.NeedAnObject; + } + + var zig_args = ArrayList([]const u8).init(builder.allocator); + defer zig_args.deinit(); + + zig_args.append(builder.zig_exe) catch unreachable; + + const cmd = switch (self.kind) { + .lib => "build-lib", + .exe => "build-exe", + .obj => "build-obj", + .@"test" => "test", + .test_exe => "test", + }; + zig_args.append(cmd) catch unreachable; + + if (builder.color != .auto) { + try zig_args.append("--color"); + try zig_args.append(@tagName(builder.color)); + } + + if (builder.reference_trace) |some| { + try zig_args.append(try std.fmt.allocPrint(builder.allocator, "-freference-trace={d}", .{some})); + } + + try addFlag(&zig_args, "LLVM", self.use_llvm); + try addFlag(&zig_args, "LLD", self.use_lld); + + if (self.target.ofmt) |ofmt| { + try zig_args.append(try std.fmt.allocPrint(builder.allocator, "-ofmt={s}", .{@tagName(ofmt)})); + } + + if (self.entry_symbol_name) |entry| { + try zig_args.append("--entry"); + try zig_args.append(entry); + } + + if (self.stack_size) |stack_size| { + try zig_args.append("--stack"); + try zig_args.append(try std.fmt.allocPrint(builder.allocator, "{}", .{stack_size})); + } + + if (self.root_src) |root_src| try zig_args.append(root_src.getPath(builder)); + + // We will add link objects from transitive dependencies, but we want to keep + // all link objects in the same order provided. + // This array is used to keep self.link_objects immutable. + var transitive_deps: TransitiveDeps = .{ + .link_objects = ArrayList(LinkObject).init(builder.allocator), + .seen_system_libs = StringHashMap(void).init(builder.allocator), + .seen_steps = std.AutoHashMap(*const Step, void).init(builder.allocator), + .is_linking_libcpp = self.is_linking_libcpp, + .is_linking_libc = self.is_linking_libc, + .frameworks = &self.frameworks, + }; + + try transitive_deps.seen_steps.put(&self.step, {}); + try transitive_deps.add(self.link_objects.items); + + var prev_has_extra_flags = false; + + for (transitive_deps.link_objects.items) |link_object| { + switch (link_object) { + .static_path => |static_path| try zig_args.append(static_path.getPath(builder)), + + .other_step => |other| switch (other.kind) { + .exe => @panic("Cannot link with an executable build artifact"), + .test_exe => @panic("Cannot link with an executable build artifact"), + .@"test" => @panic("Cannot link with a test"), + .obj => { + try zig_args.append(other.getOutputSource().getPath(builder)); + }, + .lib => l: { + if (self.isStaticLibrary() and other.isStaticLibrary()) { + // Avoid putting a static library inside a static library. + break :l; + } + + const full_path_lib = other.getOutputLibSource().getPath(builder); + try zig_args.append(full_path_lib); + + if (other.linkage == Linkage.dynamic and !self.target.isWindows()) { + if (fs.path.dirname(full_path_lib)) |dirname| { + try zig_args.append("-rpath"); + try zig_args.append(dirname); + } + } + }, + }, + + .system_lib => |system_lib| { + const prefix: []const u8 = prefix: { + if (system_lib.needed) break :prefix "-needed-l"; + if (system_lib.weak) { + if (self.target.isDarwin()) break :prefix "-weak-l"; + log.warn("Weak library import used for a non-darwin target, this will be converted to normally library import `-lname`", .{}); + } + break :prefix "-l"; + }; + switch (system_lib.use_pkg_config) { + .no => try zig_args.append(builder.fmt("{s}{s}", .{ prefix, system_lib.name })), + .yes, .force => { + if (self.runPkgConfig(system_lib.name)) |args| { + try zig_args.appendSlice(args); + } else |err| switch (err) { + error.PkgConfigInvalidOutput, + error.PkgConfigCrashed, + error.PkgConfigFailed, + error.PkgConfigNotInstalled, + error.PackageNotFound, + => switch (system_lib.use_pkg_config) { + .yes => { + // pkg-config failed, so fall back to linking the library + // by name directly. + try zig_args.append(builder.fmt("{s}{s}", .{ + prefix, + system_lib.name, + })); + }, + .force => { + panic("pkg-config failed for library {s}", .{system_lib.name}); + }, + .no => unreachable, + }, + + else => |e| return e, + } + }, + } + }, + + .assembly_file => |asm_file| { + if (prev_has_extra_flags) { + try zig_args.append("-extra-cflags"); + try zig_args.append("--"); + prev_has_extra_flags = false; + } + try zig_args.append(asm_file.getPath(builder)); + }, + + .c_source_file => |c_source_file| { + if (c_source_file.args.len == 0) { + if (prev_has_extra_flags) { + try zig_args.append("-cflags"); + try zig_args.append("--"); + prev_has_extra_flags = false; + } + } else { + try zig_args.append("-cflags"); + for (c_source_file.args) |arg| { + try zig_args.append(arg); + } + try zig_args.append("--"); + } + try zig_args.append(c_source_file.source.getPath(builder)); + }, + + .c_source_files => |c_source_files| { + if (c_source_files.flags.len == 0) { + if (prev_has_extra_flags) { + try zig_args.append("-cflags"); + try zig_args.append("--"); + prev_has_extra_flags = false; + } + } else { + try zig_args.append("-cflags"); + for (c_source_files.flags) |flag| { + try zig_args.append(flag); + } + try zig_args.append("--"); + } + for (c_source_files.files) |file| { + try zig_args.append(builder.pathFromRoot(file)); + } + }, + } + } + + if (transitive_deps.is_linking_libcpp) { + try zig_args.append("-lc++"); + } + + if (transitive_deps.is_linking_libc) { + try zig_args.append("-lc"); + } + + if (self.image_base) |image_base| { + try zig_args.append("--image-base"); + try zig_args.append(builder.fmt("0x{x}", .{image_base})); + } + + if (self.filter) |filter| { + try zig_args.append("--test-filter"); + try zig_args.append(filter); + } + + if (self.test_evented_io) { + try zig_args.append("--test-evented-io"); + } + + if (self.name_prefix.len != 0) { + try zig_args.append("--test-name-prefix"); + try zig_args.append(self.name_prefix); + } + + if (self.test_runner) |test_runner| { + try zig_args.append("--test-runner"); + try zig_args.append(builder.pathFromRoot(test_runner)); + } + + for (builder.debug_log_scopes) |log_scope| { + try zig_args.append("--debug-log"); + try zig_args.append(log_scope); + } + + if (builder.debug_compile_errors) { + try zig_args.append("--debug-compile-errors"); + } + + if (builder.verbose_cimport) zig_args.append("--verbose-cimport") catch unreachable; + if (builder.verbose_air) zig_args.append("--verbose-air") catch unreachable; + if (builder.verbose_llvm_ir) zig_args.append("--verbose-llvm-ir") catch unreachable; + if (builder.verbose_link or self.verbose_link) zig_args.append("--verbose-link") catch unreachable; + if (builder.verbose_cc or self.verbose_cc) zig_args.append("--verbose-cc") catch unreachable; + if (builder.verbose_llvm_cpu_features) zig_args.append("--verbose-llvm-cpu-features") catch unreachable; + + if (self.emit_analysis.getArg(builder, "emit-analysis")) |arg| try zig_args.append(arg); + if (self.emit_asm.getArg(builder, "emit-asm")) |arg| try zig_args.append(arg); + if (self.emit_bin.getArg(builder, "emit-bin")) |arg| try zig_args.append(arg); + if (self.emit_docs.getArg(builder, "emit-docs")) |arg| try zig_args.append(arg); + if (self.emit_implib.getArg(builder, "emit-implib")) |arg| try zig_args.append(arg); + if (self.emit_llvm_bc.getArg(builder, "emit-llvm-bc")) |arg| try zig_args.append(arg); + if (self.emit_llvm_ir.getArg(builder, "emit-llvm-ir")) |arg| try zig_args.append(arg); + + if (self.emit_h) try zig_args.append("-femit-h"); + + try addFlag(&zig_args, "strip", self.strip); + try addFlag(&zig_args, "unwind-tables", self.unwind_tables); + + switch (self.compress_debug_sections) { + .none => {}, + .zlib => try zig_args.append("--compress-debug-sections=zlib"), + } + + if (self.link_eh_frame_hdr) { + try zig_args.append("--eh-frame-hdr"); + } + if (self.link_emit_relocs) { + try zig_args.append("--emit-relocs"); + } + if (self.link_function_sections) { + try zig_args.append("-ffunction-sections"); + } + if (self.link_gc_sections) |x| { + try zig_args.append(if (x) "--gc-sections" else "--no-gc-sections"); + } + if (self.linker_allow_shlib_undefined) |x| { + try zig_args.append(if (x) "-fallow-shlib-undefined" else "-fno-allow-shlib-undefined"); + } + if (self.link_z_notext) { + try zig_args.append("-z"); + try zig_args.append("notext"); + } + if (!self.link_z_relro) { + try zig_args.append("-z"); + try zig_args.append("norelro"); + } + if (self.link_z_lazy) { + try zig_args.append("-z"); + try zig_args.append("lazy"); + } + if (self.link_z_common_page_size) |size| { + try zig_args.append("-z"); + try zig_args.append(builder.fmt("common-page-size={d}", .{size})); + } + if (self.link_z_max_page_size) |size| { + try zig_args.append("-z"); + try zig_args.append(builder.fmt("max-page-size={d}", .{size})); + } + + if (self.libc_file) |libc_file| { + try zig_args.append("--libc"); + try zig_args.append(libc_file.getPath(builder)); + } else if (builder.libc_file) |libc_file| { + try zig_args.append("--libc"); + try zig_args.append(libc_file); + } + + switch (self.optimize) { + .Debug => {}, // Skip since it's the default. + else => zig_args.append(builder.fmt("-O{s}", .{@tagName(self.optimize)})) catch unreachable, + } + + try zig_args.append("--cache-dir"); + try zig_args.append(builder.pathFromRoot(builder.cache_root)); + + try zig_args.append("--global-cache-dir"); + try zig_args.append(builder.pathFromRoot(builder.global_cache_root)); + + zig_args.append("--name") catch unreachable; + zig_args.append(self.name) catch unreachable; + + if (self.linkage) |some| switch (some) { + .dynamic => try zig_args.append("-dynamic"), + .static => try zig_args.append("-static"), + }; + if (self.kind == .lib and self.linkage != null and self.linkage.? == .dynamic) { + if (self.version) |version| { + zig_args.append("--version") catch unreachable; + zig_args.append(builder.fmt("{}", .{version})) catch unreachable; + } + + if (self.target.isDarwin()) { + const install_name = self.install_name orelse builder.fmt("@rpath/{s}{s}{s}", .{ + self.target.libPrefix(), + self.name, + self.target.dynamicLibSuffix(), + }); + try zig_args.append("-install_name"); + try zig_args.append(install_name); + } + } + + if (self.entitlements) |entitlements| { + try zig_args.appendSlice(&[_][]const u8{ "--entitlements", entitlements }); + } + if (self.pagezero_size) |pagezero_size| { + const size = try std.fmt.allocPrint(builder.allocator, "{x}", .{pagezero_size}); + try zig_args.appendSlice(&[_][]const u8{ "-pagezero_size", size }); + } + if (self.search_strategy) |strat| switch (strat) { + .paths_first => try zig_args.append("-search_paths_first"), + .dylibs_first => try zig_args.append("-search_dylibs_first"), + }; + if (self.headerpad_size) |headerpad_size| { + const size = try std.fmt.allocPrint(builder.allocator, "{x}", .{headerpad_size}); + try zig_args.appendSlice(&[_][]const u8{ "-headerpad", size }); + } + if (self.headerpad_max_install_names) { + try zig_args.append("-headerpad_max_install_names"); + } + if (self.dead_strip_dylibs) { + try zig_args.append("-dead_strip_dylibs"); + } + + try addFlag(&zig_args, "compiler-rt", self.bundle_compiler_rt); + try addFlag(&zig_args, "single-threaded", self.single_threaded); + if (self.disable_stack_probing) { + try zig_args.append("-fno-stack-check"); + } + try addFlag(&zig_args, "stack-protector", self.stack_protector); + if (self.red_zone) |red_zone| { + if (red_zone) { + try zig_args.append("-mred-zone"); + } else { + try zig_args.append("-mno-red-zone"); + } + } + try addFlag(&zig_args, "omit-frame-pointer", self.omit_frame_pointer); + try addFlag(&zig_args, "dll-export-fns", self.dll_export_fns); + + if (self.disable_sanitize_c) { + try zig_args.append("-fno-sanitize-c"); + } + if (self.sanitize_thread) { + try zig_args.append("-fsanitize-thread"); + } + if (self.rdynamic) { + try zig_args.append("-rdynamic"); + } + if (self.import_memory) { + try zig_args.append("--import-memory"); + } + if (self.import_symbols) { + try zig_args.append("--import-symbols"); + } + if (self.import_table) { + try zig_args.append("--import-table"); + } + if (self.export_table) { + try zig_args.append("--export-table"); + } + if (self.initial_memory) |initial_memory| { + try zig_args.append(builder.fmt("--initial-memory={d}", .{initial_memory})); + } + if (self.max_memory) |max_memory| { + try zig_args.append(builder.fmt("--max-memory={d}", .{max_memory})); + } + if (self.shared_memory) { + try zig_args.append("--shared-memory"); + } + if (self.global_base) |global_base| { + try zig_args.append(builder.fmt("--global-base={d}", .{global_base})); + } + + if (self.code_model != .default) { + try zig_args.append("-mcmodel"); + try zig_args.append(@tagName(self.code_model)); + } + if (self.wasi_exec_model) |model| { + try zig_args.append(builder.fmt("-mexec-model={s}", .{@tagName(model)})); + } + for (self.export_symbol_names) |symbol_name| { + try zig_args.append(builder.fmt("--export={s}", .{symbol_name})); + } + + if (!self.target.isNative()) { + try zig_args.appendSlice(&.{ + "-target", try self.target.zigTriple(builder.allocator), + "-mcpu", try std.Build.serializeCpu(builder.allocator, self.target.getCpu()), + }); + + if (self.target.dynamic_linker.get()) |dynamic_linker| { + try zig_args.append("--dynamic-linker"); + try zig_args.append(dynamic_linker); + } + } + + if (self.linker_script) |linker_script| { + try zig_args.append("--script"); + try zig_args.append(linker_script.getPath(builder)); + } + + if (self.version_script) |version_script| { + try zig_args.append("--version-script"); + try zig_args.append(builder.pathFromRoot(version_script)); + } + + if (self.kind == .@"test") { + if (self.exec_cmd_args) |exec_cmd_args| { + for (exec_cmd_args) |cmd_arg| { + if (cmd_arg) |arg| { + try zig_args.append("--test-cmd"); + try zig_args.append(arg); + } else { + try zig_args.append("--test-cmd-bin"); + } + } + } else { + const need_cross_glibc = self.target.isGnuLibC() and transitive_deps.is_linking_libc; + + switch (builder.host.getExternalExecutor(self.target_info, .{ + .qemu_fixes_dl = need_cross_glibc and builder.glibc_runtimes_dir != null, + .link_libc = transitive_deps.is_linking_libc, + })) { + .native => {}, + .bad_dl, .bad_os_or_cpu => { + try zig_args.append("--test-no-exec"); + }, + .rosetta => if (builder.enable_rosetta) { + try zig_args.append("--test-cmd-bin"); + } else { + try zig_args.append("--test-no-exec"); + }, + .qemu => |bin_name| ok: { + if (builder.enable_qemu) qemu: { + const glibc_dir_arg = if (need_cross_glibc) + builder.glibc_runtimes_dir orelse break :qemu + else + null; + try zig_args.append("--test-cmd"); + try zig_args.append(bin_name); + if (glibc_dir_arg) |dir| { + // TODO look into making this a call to `linuxTriple`. This + // needs the directory to be called "i686" rather than + // "x86" which is why we do it manually here. + const fmt_str = "{s}" ++ fs.path.sep_str ++ "{s}-{s}-{s}"; + const cpu_arch = self.target.getCpuArch(); + const os_tag = self.target.getOsTag(); + const abi = self.target.getAbi(); + const cpu_arch_name: []const u8 = if (cpu_arch == .x86) + "i686" + else + @tagName(cpu_arch); + const full_dir = try std.fmt.allocPrint(builder.allocator, fmt_str, .{ + dir, cpu_arch_name, @tagName(os_tag), @tagName(abi), + }); + + try zig_args.append("--test-cmd"); + try zig_args.append("-L"); + try zig_args.append("--test-cmd"); + try zig_args.append(full_dir); + } + try zig_args.append("--test-cmd-bin"); + break :ok; + } + try zig_args.append("--test-no-exec"); + }, + .wine => |bin_name| if (builder.enable_wine) { + try zig_args.append("--test-cmd"); + try zig_args.append(bin_name); + try zig_args.append("--test-cmd-bin"); + } else { + try zig_args.append("--test-no-exec"); + }, + .wasmtime => |bin_name| if (builder.enable_wasmtime) { + try zig_args.append("--test-cmd"); + try zig_args.append(bin_name); + try zig_args.append("--test-cmd"); + try zig_args.append("--dir=."); + try zig_args.append("--test-cmd-bin"); + } else { + try zig_args.append("--test-no-exec"); + }, + .darling => |bin_name| if (builder.enable_darling) { + try zig_args.append("--test-cmd"); + try zig_args.append(bin_name); + try zig_args.append("--test-cmd-bin"); + } else { + try zig_args.append("--test-no-exec"); + }, + } + } + } else if (self.kind == .test_exe) { + try zig_args.append("--test-no-exec"); + } + + for (self.packages.items) |pkg| { + try self.makePackageCmd(pkg, &zig_args); + } + + for (self.include_dirs.items) |include_dir| { + switch (include_dir) { + .raw_path => |include_path| { + try zig_args.append("-I"); + try zig_args.append(builder.pathFromRoot(include_path)); + }, + .raw_path_system => |include_path| { + if (builder.sysroot != null) { + try zig_args.append("-iwithsysroot"); + } else { + try zig_args.append("-isystem"); + } + + const resolved_include_path = builder.pathFromRoot(include_path); + + const common_include_path = if (builtin.os.tag == .windows and builder.sysroot != null and fs.path.isAbsolute(resolved_include_path)) blk: { + // We need to check for disk designator and strip it out from dir path so + // that zig/clang can concat resolved_include_path with sysroot. + const disk_designator = fs.path.diskDesignatorWindows(resolved_include_path); + + if (mem.indexOf(u8, resolved_include_path, disk_designator)) |where| { + break :blk resolved_include_path[where + disk_designator.len ..]; + } + + break :blk resolved_include_path; + } else resolved_include_path; + + try zig_args.append(common_include_path); + }, + .other_step => |other| { + if (other.emit_h) { + const h_path = other.getOutputHSource().getPath(builder); + try zig_args.append("-isystem"); + try zig_args.append(fs.path.dirname(h_path).?); + } + if (other.installed_headers.items.len > 0) { + for (other.installed_headers.items) |install_step| { + try install_step.make(); + } + try zig_args.append("-I"); + try zig_args.append(builder.pathJoin(&.{ + other.builder.install_prefix, "include", + })); + } + }, + .config_header_step => |config_header| { + try zig_args.append("-I"); + try zig_args.append(config_header.output_dir); + }, + } + } + + for (self.lib_paths.items) |lib_path| { + try zig_args.append("-L"); + try zig_args.append(lib_path); + } + + for (self.rpaths.items) |rpath| { + try zig_args.append("-rpath"); + try zig_args.append(rpath); + } + + for (self.c_macros.items) |c_macro| { + try zig_args.append("-D"); + try zig_args.append(c_macro); + } + + if (self.target.isDarwin()) { + for (self.framework_dirs.items) |dir| { + if (builder.sysroot != null) { + try zig_args.append("-iframeworkwithsysroot"); + } else { + try zig_args.append("-iframework"); + } + try zig_args.append(dir); + try zig_args.append("-F"); + try zig_args.append(dir); + } + + var it = self.frameworks.iterator(); + while (it.next()) |entry| { + const name = entry.key_ptr.*; + const info = entry.value_ptr.*; + if (info.needed) { + zig_args.append("-needed_framework") catch unreachable; + } else if (info.weak) { + zig_args.append("-weak_framework") catch unreachable; + } else { + zig_args.append("-framework") catch unreachable; + } + zig_args.append(name) catch unreachable; + } + } else { + if (self.framework_dirs.items.len > 0) { + log.info("Framework directories have been added for a non-darwin target, this will have no affect on the build", .{}); + } + + if (self.frameworks.count() > 0) { + log.info("Frameworks have been added for a non-darwin target, this will have no affect on the build", .{}); + } + } + + if (builder.sysroot) |sysroot| { + try zig_args.appendSlice(&[_][]const u8{ "--sysroot", sysroot }); + } + + for (builder.search_prefixes.items) |search_prefix| { + try zig_args.append("-L"); + try zig_args.append(builder.pathJoin(&.{ + search_prefix, "lib", + })); + try zig_args.append("-I"); + try zig_args.append(builder.pathJoin(&.{ + search_prefix, "include", + })); + } + + try addFlag(&zig_args, "valgrind", self.valgrind_support); + try addFlag(&zig_args, "each-lib-rpath", self.each_lib_rpath); + try addFlag(&zig_args, "build-id", self.build_id); + + if (self.override_lib_dir) |dir| { + try zig_args.append("--zig-lib-dir"); + try zig_args.append(builder.pathFromRoot(dir)); + } else if (builder.override_lib_dir) |dir| { + try zig_args.append("--zig-lib-dir"); + try zig_args.append(builder.pathFromRoot(dir)); + } + + if (self.main_pkg_path) |dir| { + try zig_args.append("--main-pkg-path"); + try zig_args.append(builder.pathFromRoot(dir)); + } + + try addFlag(&zig_args, "PIC", self.force_pic); + try addFlag(&zig_args, "PIE", self.pie); + try addFlag(&zig_args, "lto", self.want_lto); + + if (self.subsystem) |subsystem| { + try zig_args.append("--subsystem"); + try zig_args.append(switch (subsystem) { + .Console => "console", + .Windows => "windows", + .Posix => "posix", + .Native => "native", + .EfiApplication => "efi_application", + .EfiBootServiceDriver => "efi_boot_service_driver", + .EfiRom => "efi_rom", + .EfiRuntimeDriver => "efi_runtime_driver", + }); + } + + try zig_args.append("--enable-cache"); + + // Windows has an argument length limit of 32,766 characters, macOS 262,144 and Linux + // 2,097,152. If our args exceed 30 KiB, we instead write them to a "response file" and + // pass that to zig, e.g. via 'zig build-lib @args.rsp' + // See @file syntax here: https://gcc.gnu.org/onlinedocs/gcc/Overall-Options.html + var args_length: usize = 0; + for (zig_args.items) |arg| { + args_length += arg.len + 1; // +1 to account for null terminator + } + if (args_length >= 30 * 1024) { + const args_dir = try fs.path.join( + builder.allocator, + &[_][]const u8{ builder.pathFromRoot("zig-cache"), "args" }, + ); + try std.fs.cwd().makePath(args_dir); + + var args_arena = std.heap.ArenaAllocator.init(builder.allocator); + defer args_arena.deinit(); + + const args_to_escape = zig_args.items[2..]; + var escaped_args = try ArrayList([]const u8).initCapacity(args_arena.allocator(), args_to_escape.len); + + arg_blk: for (args_to_escape) |arg| { + for (arg) |c, arg_idx| { + if (c == '\\' or c == '"') { + // Slow path for arguments that need to be escaped. We'll need to allocate and copy + var escaped = try ArrayList(u8).initCapacity(args_arena.allocator(), arg.len + 1); + const writer = escaped.writer(); + writer.writeAll(arg[0..arg_idx]) catch unreachable; + for (arg[arg_idx..]) |to_escape| { + if (to_escape == '\\' or to_escape == '"') try writer.writeByte('\\'); + try writer.writeByte(to_escape); + } + escaped_args.appendAssumeCapacity(escaped.items); + continue :arg_blk; + } + } + escaped_args.appendAssumeCapacity(arg); // no escaping needed so just use original argument + } + + // Write the args to zig-cache/args/ to avoid conflicts with + // other zig build commands running in parallel. + const partially_quoted = try std.mem.join(builder.allocator, "\" \"", escaped_args.items); + const args = try std.mem.concat(builder.allocator, u8, &[_][]const u8{ "\"", partially_quoted, "\"" }); + + var args_hash: [Sha256.digest_length]u8 = undefined; + Sha256.hash(args, &args_hash, .{}); + var args_hex_hash: [Sha256.digest_length * 2]u8 = undefined; + _ = try std.fmt.bufPrint( + &args_hex_hash, + "{s}", + .{std.fmt.fmtSliceHexLower(&args_hash)}, + ); + + const args_file = try fs.path.join(builder.allocator, &[_][]const u8{ args_dir, args_hex_hash[0..] }); + try std.fs.cwd().writeFile(args_file, args); + + zig_args.shrinkRetainingCapacity(2); + try zig_args.append(try std.mem.concat(builder.allocator, u8, &[_][]const u8{ "@", args_file })); + } + + const output_dir_nl = try builder.execFromStep(zig_args.items, &self.step); + const build_output_dir = mem.trimRight(u8, output_dir_nl, "\r\n"); + + if (self.output_dir) |output_dir| { + var src_dir = try std.fs.cwd().openIterableDir(build_output_dir, .{}); + defer src_dir.close(); + + // Create the output directory if it doesn't exist. + try std.fs.cwd().makePath(output_dir); + + var dest_dir = try std.fs.cwd().openDir(output_dir, .{}); + defer dest_dir.close(); + + var it = src_dir.iterate(); + while (try it.next()) |entry| { + // The compiler can put these files into the same directory, but we don't + // want to copy them over. + if (mem.eql(u8, entry.name, "llvm-ar.id") or + mem.eql(u8, entry.name, "libs.txt") or + mem.eql(u8, entry.name, "builtin.zig") or + mem.eql(u8, entry.name, "zld.id") or + mem.eql(u8, entry.name, "lld.id")) continue; + + _ = try src_dir.dir.updateFile(entry.name, dest_dir, entry.name, .{}); + } + } else { + self.output_dir = build_output_dir; + } + + // This will ensure all output filenames will now have the output_dir available! + self.computeOutFileNames(); + + // Update generated files + if (self.output_dir != null) { + self.output_path_source.path = builder.pathJoin( + &.{ self.output_dir.?, self.out_filename }, + ); + + if (self.emit_h) { + self.output_h_path_source.path = builder.pathJoin( + &.{ self.output_dir.?, self.out_h_filename }, + ); + } + + if (self.target.isWindows() or self.target.isUefi()) { + self.output_pdb_path_source.path = builder.pathJoin( + &.{ self.output_dir.?, self.out_pdb_filename }, + ); + } + } + + if (self.kind == .lib and self.linkage != null and self.linkage.? == .dynamic and self.version != null and self.target.wantSharedLibSymLinks()) { + try doAtomicSymLinks(builder.allocator, self.getOutputSource().getPath(builder), self.major_only_filename.?, self.name_only_filename.?); + } +} + +fn isLibCLibrary(name: []const u8) bool { + const libc_libraries = [_][]const u8{ "c", "m", "dl", "rt", "pthread" }; + for (libc_libraries) |libc_lib_name| { + if (mem.eql(u8, name, libc_lib_name)) + return true; + } + return false; +} + +fn isLibCppLibrary(name: []const u8) bool { + const libcpp_libraries = [_][]const u8{ "c++", "stdc++" }; + for (libcpp_libraries) |libcpp_lib_name| { + if (mem.eql(u8, name, libcpp_lib_name)) + return true; + } + return false; +} + +/// Returned slice must be freed by the caller. +fn findVcpkgRoot(allocator: Allocator) !?[]const u8 { + const appdata_path = try fs.getAppDataDir(allocator, "vcpkg"); + defer allocator.free(appdata_path); + + const path_file = try fs.path.join(allocator, &[_][]const u8{ appdata_path, "vcpkg.path.txt" }); + defer allocator.free(path_file); + + const file = fs.cwd().openFile(path_file, .{}) catch return null; + defer file.close(); + + const size = @intCast(usize, try file.getEndPos()); + const vcpkg_path = try allocator.alloc(u8, size); + const size_read = try file.read(vcpkg_path); + std.debug.assert(size == size_read); + + return vcpkg_path; +} + +pub fn doAtomicSymLinks(allocator: Allocator, output_path: []const u8, filename_major_only: []const u8, filename_name_only: []const u8) !void { + const out_dir = fs.path.dirname(output_path) orelse "."; + const out_basename = fs.path.basename(output_path); + // sym link for libfoo.so.1 to libfoo.so.1.2.3 + const major_only_path = fs.path.join( + allocator, + &[_][]const u8{ out_dir, filename_major_only }, + ) catch unreachable; + fs.atomicSymLink(allocator, out_basename, major_only_path) catch |err| { + log.err("Unable to symlink {s} -> {s}", .{ major_only_path, out_basename }); + return err; + }; + // sym link for libfoo.so to libfoo.so.1 + const name_only_path = fs.path.join( + allocator, + &[_][]const u8{ out_dir, filename_name_only }, + ) catch unreachable; + fs.atomicSymLink(allocator, filename_major_only, name_only_path) catch |err| { + log.err("Unable to symlink {s} -> {s}", .{ name_only_path, filename_major_only }); + return err; + }; +} + +fn execPkgConfigList(self: *std.Build, out_code: *u8) (PkgConfigError || ExecError)![]const PkgConfigPkg { + const stdout = try self.execAllowFail(&[_][]const u8{ "pkg-config", "--list-all" }, out_code, .Ignore); + var list = ArrayList(PkgConfigPkg).init(self.allocator); + errdefer list.deinit(); + var line_it = mem.tokenize(u8, stdout, "\r\n"); + while (line_it.next()) |line| { + if (mem.trim(u8, line, " \t").len == 0) continue; + var tok_it = mem.tokenize(u8, line, " \t"); + try list.append(PkgConfigPkg{ + .name = tok_it.next() orelse return error.PkgConfigInvalidOutput, + .desc = tok_it.rest(), + }); + } + return list.toOwnedSlice(); +} + +fn getPkgConfigList(self: *std.Build) ![]const PkgConfigPkg { + if (self.pkg_config_pkg_list) |res| { + return res; + } + var code: u8 = undefined; + if (execPkgConfigList(self, &code)) |list| { + self.pkg_config_pkg_list = list; + return list; + } else |err| { + const result = switch (err) { + error.ProcessTerminated => error.PkgConfigCrashed, + error.ExecNotSupported => error.PkgConfigFailed, + error.ExitCodeFailure => error.PkgConfigFailed, + error.FileNotFound => error.PkgConfigNotInstalled, + error.InvalidName => error.PkgConfigNotInstalled, + error.PkgConfigInvalidOutput => error.PkgConfigInvalidOutput, + error.ChildExecFailed => error.PkgConfigFailed, + else => return err, + }; + self.pkg_config_pkg_list = result; + return result; + } +} + +test "addPackage" { + if (builtin.os.tag == .wasi) return error.SkipZigTest; + + var arena = std.heap.ArenaAllocator.init(std.testing.allocator); + defer arena.deinit(); + + const host = try NativeTargetInfo.detect(.{}); + + var builder = try std.Build.create( + arena.allocator(), + "test", + "test", + "test", + "test", + host, + ); + defer builder.destroy(); + + const pkg_dep = Pkg{ + .name = "pkg_dep", + .source = .{ .path = "/not/a/pkg_dep.zig" }, + }; + const pkg_top = Pkg{ + .name = "pkg_dep", + .source = .{ .path = "/not/a/pkg_top.zig" }, + .dependencies = &[_]Pkg{pkg_dep}, + }; + + var exe = builder.addExecutable("not_an_executable", "/not/an/executable.zig"); + exe.addPackage(pkg_top); + + try std.testing.expectEqual(@as(usize, 1), exe.packages.items.len); + + const dupe = exe.packages.items[0]; + try std.testing.expectEqualStrings(pkg_top.name, dupe.name); +} + +fn addFlag(args: *ArrayList([]const u8), comptime name: []const u8, opt: ?bool) !void { + const cond = opt orelse return; + try args.ensureUnusedCapacity(1); + if (cond) { + args.appendAssumeCapacity("-f" ++ name); + } else { + args.appendAssumeCapacity("-fno-" ++ name); + } +} + +const TransitiveDeps = struct { + link_objects: ArrayList(LinkObject), + seen_system_libs: StringHashMap(void), + seen_steps: std.AutoHashMap(*const Step, void), + is_linking_libcpp: bool, + is_linking_libc: bool, + frameworks: *StringHashMap(FrameworkLinkInfo), + + fn add(td: *TransitiveDeps, link_objects: []const LinkObject) !void { + try td.link_objects.ensureUnusedCapacity(link_objects.len); + + for (link_objects) |link_object| { + try td.link_objects.append(link_object); + switch (link_object) { + .other_step => |other| try addInner(td, other, other.isDynamicLibrary()), + else => {}, + } + } + } + + fn addInner(td: *TransitiveDeps, other: *CompileStep, dyn: bool) !void { + // Inherit dependency on libc and libc++ + td.is_linking_libcpp = td.is_linking_libcpp or other.is_linking_libcpp; + td.is_linking_libc = td.is_linking_libc or other.is_linking_libc; + + // Inherit dependencies on darwin frameworks + if (!dyn) { + var it = other.frameworks.iterator(); + while (it.next()) |framework| { + try td.frameworks.put(framework.key_ptr.*, framework.value_ptr.*); + } + } + + // Inherit dependencies on system libraries and static libraries. + for (other.link_objects.items) |other_link_object| { + switch (other_link_object) { + .system_lib => |system_lib| { + if ((try td.seen_system_libs.fetchPut(system_lib.name, {})) != null) + continue; + + if (dyn) + continue; + + try td.link_objects.append(other_link_object); + }, + .other_step => |inner_other| { + if ((try td.seen_steps.fetchPut(&inner_other.step, {})) != null) + continue; + + if (!dyn) + try td.link_objects.append(other_link_object); + + try addInner(td, inner_other, dyn or inner_other.isDynamicLibrary()); + }, + else => continue, + } + } + } +}; diff --git a/lib/std/Build/EmulatableRunStep.zig b/lib/std/Build/EmulatableRunStep.zig index b7b12d791f..26804b2fc8 100644 --- a/lib/std/Build/EmulatableRunStep.zig +++ b/lib/std/Build/EmulatableRunStep.zig @@ -6,7 +6,7 @@ const std = @import("../std.zig"); const Step = std.Build.Step; -const LibExeObjStep = std.Build.LibExeObjStep; +const CompileStep = std.Build.CompileStep; const RunStep = std.Build.RunStep; const fs = std.fs; @@ -23,7 +23,7 @@ step: Step, builder: *std.Build, /// The artifact (executable) to be run by this step -exe: *LibExeObjStep, +exe: *CompileStep, /// Set this to `null` to ignore the exit code for the purpose of determining a successful execution expected_exit_code: ?u8 = 0, @@ -45,7 +45,7 @@ hide_foreign_binaries_warning: bool, /// binary through emulation when any of the emulation options such as `enable_rosetta` are set to true. /// When set to false, and the binary is foreign, running the executable is skipped. /// Asserts given artifact is an executable. -pub fn create(builder: *std.Build, name: []const u8, artifact: *LibExeObjStep) *EmulatableRunStep { +pub fn create(builder: *std.Build, name: []const u8, artifact: *CompileStep) *EmulatableRunStep { std.debug.assert(artifact.kind == .exe or artifact.kind == .test_exe); const self = builder.allocator.create(EmulatableRunStep) catch unreachable; diff --git a/lib/std/Build/InstallArtifactStep.zig b/lib/std/Build/InstallArtifactStep.zig index 929b30e935..898b9e85e7 100644 --- a/lib/std/Build/InstallArtifactStep.zig +++ b/lib/std/Build/InstallArtifactStep.zig @@ -1,6 +1,6 @@ const std = @import("../std.zig"); const Step = std.Build.Step; -const LibExeObjStep = std.Build.LibExeObjStep; +const CompileStep = std.Build.CompileStep; const InstallDir = std.Build.InstallDir; const InstallArtifactStep = @This(); @@ -8,12 +8,12 @@ pub const base_id = .install_artifact; step: Step, builder: *std.Build, -artifact: *LibExeObjStep, +artifact: *CompileStep, dest_dir: InstallDir, pdb_dir: ?InstallDir, h_dir: ?InstallDir, -pub fn create(builder: *std.Build, artifact: *LibExeObjStep) *InstallArtifactStep { +pub fn create(builder: *std.Build, artifact: *CompileStep) *InstallArtifactStep { if (artifact.install_step) |s| return s; const self = builder.allocator.create(InstallArtifactStep) catch unreachable; @@ -67,7 +67,7 @@ fn make(step: *Step) !void { const full_dest_path = builder.getInstallPath(self.dest_dir, self.artifact.out_filename); try builder.updateFile(self.artifact.getOutputSource().getPath(builder), full_dest_path); if (self.artifact.isDynamicLibrary() and self.artifact.version != null and self.artifact.target.wantSharedLibSymLinks()) { - try LibExeObjStep.doAtomicSymLinks(builder.allocator, full_dest_path, self.artifact.major_only_filename.?, self.artifact.name_only_filename.?); + try CompileStep.doAtomicSymLinks(builder.allocator, full_dest_path, self.artifact.major_only_filename.?, self.artifact.name_only_filename.?); } if (self.artifact.isDynamicLibrary() and self.artifact.target.isWindows() and self.artifact.emit_implib != .no_emit) { const full_implib_path = builder.getInstallPath(self.dest_dir, self.artifact.out_lib_filename); diff --git a/lib/std/Build/InstallRawStep.zig b/lib/std/Build/InstallRawStep.zig index 08d646ff88..e7ffe78878 100644 --- a/lib/std/Build/InstallRawStep.zig +++ b/lib/std/Build/InstallRawStep.zig @@ -9,7 +9,7 @@ const ArenaAllocator = std.heap.ArenaAllocator; const ArrayListUnmanaged = std.ArrayListUnmanaged; const File = std.fs.File; const InstallDir = std.Build.InstallDir; -const LibExeObjStep = std.Build.LibExeObjStep; +const CompileStep = std.Build.CompileStep; const Step = std.Build.Step; const elf = std.elf; const fs = std.fs; @@ -25,7 +25,7 @@ pub const RawFormat = enum { step: Step, builder: *std.Build, -artifact: *LibExeObjStep, +artifact: *CompileStep, dest_dir: InstallDir, dest_filename: []const u8, options: CreateOptions, @@ -40,7 +40,7 @@ pub const CreateOptions = struct { pub fn create( builder: *std.Build, - artifact: *LibExeObjStep, + artifact: *CompileStep, dest_filename: []const u8, options: CreateOptions, ) *InstallRawStep { diff --git a/lib/std/Build/LibExeObjStep.zig b/lib/std/Build/LibExeObjStep.zig deleted file mode 100644 index 67f42c1783..0000000000 --- a/lib/std/Build/LibExeObjStep.zig +++ /dev/null @@ -1,2045 +0,0 @@ -const builtin = @import("builtin"); -const std = @import("../std.zig"); -const mem = std.mem; -const log = std.log; -const fs = std.fs; -const assert = std.debug.assert; -const panic = std.debug.panic; -const ArrayList = std.ArrayList; -const StringHashMap = std.StringHashMap; -const Sha256 = std.crypto.hash.sha2.Sha256; -const Allocator = mem.Allocator; -const Step = std.Build.Step; -const CrossTarget = std.zig.CrossTarget; -const NativeTargetInfo = std.zig.system.NativeTargetInfo; -const FileSource = std.Build.FileSource; -const PkgConfigPkg = std.Build.PkgConfigPkg; -const PkgConfigError = std.Build.PkgConfigError; -const ExecError = std.Build.ExecError; -const Pkg = std.Build.Pkg; -const VcpkgRoot = std.Build.VcpkgRoot; -const InstallDir = std.Build.InstallDir; -const InstallArtifactStep = std.Build.InstallArtifactStep; -const GeneratedFile = std.Build.GeneratedFile; -const InstallRawStep = std.Build.InstallRawStep; -const EmulatableRunStep = std.Build.EmulatableRunStep; -const CheckObjectStep = std.Build.CheckObjectStep; -const RunStep = std.Build.RunStep; -const OptionsStep = std.Build.OptionsStep; -const ConfigHeaderStep = std.Build.ConfigHeaderStep; -const LibExeObjStep = @This(); - -pub const base_id = .lib_exe_obj; - -step: Step, -builder: *std.Build, -name: []const u8, -target: CrossTarget, -target_info: NativeTargetInfo, -optimize: std.builtin.Mode, -linker_script: ?FileSource = null, -version_script: ?[]const u8 = null, -out_filename: []const u8, -linkage: ?Linkage = null, -version: ?std.builtin.Version, -kind: Kind, -major_only_filename: ?[]const u8, -name_only_filename: ?[]const u8, -strip: ?bool, -unwind_tables: ?bool, -// keep in sync with src/link.zig:CompressDebugSections -compress_debug_sections: enum { none, zlib } = .none, -lib_paths: ArrayList([]const u8), -rpaths: ArrayList([]const u8), -framework_dirs: ArrayList([]const u8), -frameworks: StringHashMap(FrameworkLinkInfo), -verbose_link: bool, -verbose_cc: bool, -emit_analysis: EmitOption = .default, -emit_asm: EmitOption = .default, -emit_bin: EmitOption = .default, -emit_docs: EmitOption = .default, -emit_implib: EmitOption = .default, -emit_llvm_bc: EmitOption = .default, -emit_llvm_ir: EmitOption = .default, -// Lots of things depend on emit_h having a consistent path, -// so it is not an EmitOption for now. -emit_h: bool = false, -bundle_compiler_rt: ?bool = null, -single_threaded: ?bool = null, -stack_protector: ?bool = null, -disable_stack_probing: bool, -disable_sanitize_c: bool, -sanitize_thread: bool, -rdynamic: bool, -import_memory: bool = false, -/// For WebAssembly targets, this will allow for undefined symbols to -/// be imported from the host environment. -import_symbols: bool = false, -import_table: bool = false, -export_table: bool = false, -initial_memory: ?u64 = null, -max_memory: ?u64 = null, -shared_memory: bool = false, -global_base: ?u64 = null, -c_std: std.Build.CStd, -override_lib_dir: ?[]const u8, -main_pkg_path: ?[]const u8, -exec_cmd_args: ?[]const ?[]const u8, -name_prefix: []const u8, -filter: ?[]const u8, -test_evented_io: bool = false, -test_runner: ?[]const u8, -code_model: std.builtin.CodeModel = .default, -wasi_exec_model: ?std.builtin.WasiExecModel = null, -/// Symbols to be exported when compiling to wasm -export_symbol_names: []const []const u8 = &.{}, - -root_src: ?FileSource, -out_h_filename: []const u8, -out_lib_filename: []const u8, -out_pdb_filename: []const u8, -packages: ArrayList(Pkg), - -object_src: []const u8, - -link_objects: ArrayList(LinkObject), -include_dirs: ArrayList(IncludeDir), -c_macros: ArrayList([]const u8), -installed_headers: ArrayList(*Step), -output_dir: ?[]const u8, -is_linking_libc: bool = false, -is_linking_libcpp: bool = false, -vcpkg_bin_path: ?[]const u8 = null, - -/// This may be set in order to override the default install directory -override_dest_dir: ?InstallDir, -installed_path: ?[]const u8, -install_step: ?*InstallArtifactStep, - -/// Base address for an executable image. -image_base: ?u64 = null, - -libc_file: ?FileSource = null, - -valgrind_support: ?bool = null, -each_lib_rpath: ?bool = null, -/// On ELF targets, this will emit a link section called ".note.gnu.build-id" -/// which can be used to coordinate a stripped binary with its debug symbols. -/// As an example, the bloaty project refuses to work unless its inputs have -/// build ids, in order to prevent accidental mismatches. -/// The default is to not include this section because it slows down linking. -build_id: ?bool = null, - -/// Create a .eh_frame_hdr section and a PT_GNU_EH_FRAME segment in the ELF -/// file. -link_eh_frame_hdr: bool = false, -link_emit_relocs: bool = false, - -/// Place every function in its own section so that unused ones may be -/// safely garbage-collected during the linking phase. -link_function_sections: bool = false, - -/// Remove functions and data that are unreachable by the entry point or -/// exported symbols. -link_gc_sections: ?bool = null, - -linker_allow_shlib_undefined: ?bool = null, - -/// Permit read-only relocations in read-only segments. Disallowed by default. -link_z_notext: bool = false, - -/// Force all relocations to be read-only after processing. -link_z_relro: bool = true, - -/// Allow relocations to be lazily processed after load. -link_z_lazy: bool = false, - -/// Common page size -link_z_common_page_size: ?u64 = null, - -/// Maximum page size -link_z_max_page_size: ?u64 = null, - -/// (Darwin) Install name for the dylib -install_name: ?[]const u8 = null, - -/// (Darwin) Path to entitlements file -entitlements: ?[]const u8 = null, - -/// (Darwin) Size of the pagezero segment. -pagezero_size: ?u64 = null, - -/// (Darwin) Search strategy for searching system libraries. Either `paths_first` or `dylibs_first`. -/// The former lowers to `-search_paths_first` linker option, while the latter to `-search_dylibs_first` -/// option. -/// By default, if no option is specified, the linker assumes `paths_first` as the default -/// search strategy. -search_strategy: ?enum { paths_first, dylibs_first } = null, - -/// (Darwin) Set size of the padding between the end of load commands -/// and start of `__TEXT,__text` section. -headerpad_size: ?u32 = null, - -/// (Darwin) Automatically Set size of the padding between the end of load commands -/// and start of `__TEXT,__text` section to a value fitting all paths expanded to MAXPATHLEN. -headerpad_max_install_names: bool = false, - -/// (Darwin) Remove dylibs that are unreachable by the entry point or exported symbols. -dead_strip_dylibs: bool = false, - -/// Position Independent Code -force_pic: ?bool = null, - -/// Position Independent Executable -pie: ?bool = null, - -red_zone: ?bool = null, - -omit_frame_pointer: ?bool = null, -dll_export_fns: ?bool = null, - -subsystem: ?std.Target.SubSystem = null, - -entry_symbol_name: ?[]const u8 = null, - -/// Overrides the default stack size -stack_size: ?u64 = null, - -want_lto: ?bool = null, -use_llvm: ?bool = null, -use_lld: ?bool = null, - -output_path_source: GeneratedFile, -output_lib_path_source: GeneratedFile, -output_h_path_source: GeneratedFile, -output_pdb_path_source: GeneratedFile, - -pub const CSourceFiles = struct { - files: []const []const u8, - flags: []const []const u8, -}; - -pub const CSourceFile = struct { - source: FileSource, - args: []const []const u8, - - pub fn dupe(self: CSourceFile, b: *std.Build) CSourceFile { - return .{ - .source = self.source.dupe(b), - .args = b.dupeStrings(self.args), - }; - } -}; - -pub const LinkObject = union(enum) { - static_path: FileSource, - other_step: *LibExeObjStep, - system_lib: SystemLib, - assembly_file: FileSource, - c_source_file: *CSourceFile, - c_source_files: *CSourceFiles, -}; - -pub const SystemLib = struct { - name: []const u8, - needed: bool, - weak: bool, - use_pkg_config: enum { - /// Don't use pkg-config, just pass -lfoo where foo is name. - no, - /// Try to get information on how to link the library from pkg-config. - /// If that fails, fall back to passing -lfoo where foo is name. - yes, - /// Try to get information on how to link the library from pkg-config. - /// If that fails, error out. - force, - }, -}; - -const FrameworkLinkInfo = struct { - needed: bool = false, - weak: bool = false, -}; - -pub const IncludeDir = union(enum) { - raw_path: []const u8, - raw_path_system: []const u8, - other_step: *LibExeObjStep, - config_header_step: *ConfigHeaderStep, -}; - -pub const Options = struct { - name: []const u8, - root_source_file: ?FileSource = null, - target: CrossTarget, - optimize: std.builtin.Mode, - kind: Kind, - linkage: ?Linkage = null, - version: ?std.builtin.Version = null, -}; - -pub const Kind = enum { - exe, - lib, - obj, - @"test", - test_exe, -}; - -pub const Linkage = enum { dynamic, static }; - -pub const EmitOption = union(enum) { - default: void, - no_emit: void, - emit: void, - emit_to: []const u8, - - fn getArg(self: @This(), b: *std.Build, arg_name: []const u8) ?[]const u8 { - return switch (self) { - .no_emit => b.fmt("-fno-{s}", .{arg_name}), - .default => null, - .emit => b.fmt("-f{s}", .{arg_name}), - .emit_to => |path| b.fmt("-f{s}={s}", .{ arg_name, path }), - }; - } -}; - -pub fn create(builder: *std.Build, options: Options) *LibExeObjStep { - const name = builder.dupe(options.name); - const root_src: ?FileSource = if (options.root_source_file) |rsrc| rsrc.dupe(builder) else null; - if (mem.indexOf(u8, name, "/") != null or mem.indexOf(u8, name, "\\") != null) { - panic("invalid name: '{s}'. It looks like a file path, but it is supposed to be the library or application name.", .{name}); - } - - const self = builder.allocator.create(LibExeObjStep) catch unreachable; - self.* = LibExeObjStep{ - .strip = null, - .unwind_tables = null, - .builder = builder, - .verbose_link = false, - .verbose_cc = false, - .optimize = options.optimize, - .target = options.target, - .linkage = options.linkage, - .kind = options.kind, - .root_src = root_src, - .name = name, - .frameworks = StringHashMap(FrameworkLinkInfo).init(builder.allocator), - .step = Step.init(base_id, name, builder.allocator, make), - .version = options.version, - .out_filename = undefined, - .out_h_filename = builder.fmt("{s}.h", .{name}), - .out_lib_filename = undefined, - .out_pdb_filename = builder.fmt("{s}.pdb", .{name}), - .major_only_filename = null, - .name_only_filename = null, - .packages = ArrayList(Pkg).init(builder.allocator), - .include_dirs = ArrayList(IncludeDir).init(builder.allocator), - .link_objects = ArrayList(LinkObject).init(builder.allocator), - .c_macros = ArrayList([]const u8).init(builder.allocator), - .lib_paths = ArrayList([]const u8).init(builder.allocator), - .rpaths = ArrayList([]const u8).init(builder.allocator), - .framework_dirs = ArrayList([]const u8).init(builder.allocator), - .installed_headers = ArrayList(*Step).init(builder.allocator), - .object_src = undefined, - .c_std = std.Build.CStd.C99, - .override_lib_dir = null, - .main_pkg_path = null, - .exec_cmd_args = null, - .name_prefix = "", - .filter = null, - .test_runner = null, - .disable_stack_probing = false, - .disable_sanitize_c = false, - .sanitize_thread = false, - .rdynamic = false, - .output_dir = null, - .override_dest_dir = null, - .installed_path = null, - .install_step = null, - - .output_path_source = GeneratedFile{ .step = &self.step }, - .output_lib_path_source = GeneratedFile{ .step = &self.step }, - .output_h_path_source = GeneratedFile{ .step = &self.step }, - .output_pdb_path_source = GeneratedFile{ .step = &self.step }, - - .target_info = NativeTargetInfo.detect(self.target) catch unreachable, - }; - self.computeOutFileNames(); - if (root_src) |rs| rs.addStepDependencies(&self.step); - return self; -} - -fn computeOutFileNames(self: *LibExeObjStep) void { - const target = self.target_info.target; - - self.out_filename = std.zig.binNameAlloc(self.builder.allocator, .{ - .root_name = self.name, - .target = target, - .output_mode = switch (self.kind) { - .lib => .Lib, - .obj => .Obj, - .exe, .@"test", .test_exe => .Exe, - }, - .link_mode = if (self.linkage) |some| @as(std.builtin.LinkMode, switch (some) { - .dynamic => .Dynamic, - .static => .Static, - }) else null, - .version = self.version, - }) catch unreachable; - - if (self.kind == .lib) { - if (self.linkage != null and self.linkage.? == .static) { - self.out_lib_filename = self.out_filename; - } else if (self.version) |version| { - if (target.isDarwin()) { - self.major_only_filename = self.builder.fmt("lib{s}.{d}.dylib", .{ - self.name, - version.major, - }); - self.name_only_filename = self.builder.fmt("lib{s}.dylib", .{self.name}); - self.out_lib_filename = self.out_filename; - } else if (target.os.tag == .windows) { - self.out_lib_filename = self.builder.fmt("{s}.lib", .{self.name}); - } else { - self.major_only_filename = self.builder.fmt("lib{s}.so.{d}", .{ self.name, version.major }); - self.name_only_filename = self.builder.fmt("lib{s}.so", .{self.name}); - self.out_lib_filename = self.out_filename; - } - } else { - if (target.isDarwin()) { - self.out_lib_filename = self.out_filename; - } else if (target.os.tag == .windows) { - self.out_lib_filename = self.builder.fmt("{s}.lib", .{self.name}); - } else { - self.out_lib_filename = self.out_filename; - } - } - if (self.output_dir != null) { - self.output_lib_path_source.path = self.builder.pathJoin( - &.{ self.output_dir.?, self.out_lib_filename }, - ); - } - } -} - -pub fn setOutputDir(self: *LibExeObjStep, dir: []const u8) void { - self.output_dir = self.builder.dupePath(dir); -} - -pub fn install(self: *LibExeObjStep) void { - self.builder.installArtifact(self); -} - -pub fn installRaw(self: *LibExeObjStep, dest_filename: []const u8, options: InstallRawStep.CreateOptions) *InstallRawStep { - return self.builder.installRaw(self, dest_filename, options); -} - -pub fn installHeader(a: *LibExeObjStep, src_path: []const u8, dest_rel_path: []const u8) void { - const install_file = a.builder.addInstallHeaderFile(src_path, dest_rel_path); - a.builder.getInstallStep().dependOn(&install_file.step); - a.installed_headers.append(&install_file.step) catch unreachable; -} - -pub fn installHeadersDirectory( - a: *LibExeObjStep, - src_dir_path: []const u8, - dest_rel_path: []const u8, -) void { - return installHeadersDirectoryOptions(a, .{ - .source_dir = src_dir_path, - .install_dir = .header, - .install_subdir = dest_rel_path, - }); -} - -pub fn installHeadersDirectoryOptions( - a: *LibExeObjStep, - options: std.Build.InstallDirStep.Options, -) void { - const install_dir = a.builder.addInstallDirectory(options); - a.builder.getInstallStep().dependOn(&install_dir.step); - a.installed_headers.append(&install_dir.step) catch unreachable; -} - -pub fn installLibraryHeaders(a: *LibExeObjStep, l: *LibExeObjStep) void { - assert(l.kind == .lib); - const install_step = a.builder.getInstallStep(); - // Copy each element from installed_headers, modifying the builder - // to be the new parent's builder. - for (l.installed_headers.items) |step| { - const step_copy = switch (step.id) { - inline .install_file, .install_dir => |id| blk: { - const T = id.Type(); - const ptr = a.builder.allocator.create(T) catch unreachable; - ptr.* = step.cast(T).?.*; - ptr.override_source_builder = ptr.builder; - ptr.builder = a.builder; - break :blk &ptr.step; - }, - else => unreachable, - }; - a.installed_headers.append(step_copy) catch unreachable; - install_step.dependOn(step_copy); - } - a.installed_headers.appendSlice(l.installed_headers.items) catch unreachable; -} - -/// Creates a `RunStep` with an executable built with `addExecutable`. -/// Add command line arguments with `addArg`. -pub fn run(exe: *LibExeObjStep) *RunStep { - assert(exe.kind == .exe or exe.kind == .test_exe); - - // It doesn't have to be native. We catch that if you actually try to run it. - // Consider that this is declarative; the run step may not be run unless a user - // option is supplied. - const run_step = RunStep.create(exe.builder, exe.builder.fmt("run {s}", .{exe.step.name})); - run_step.addArtifactArg(exe); - - if (exe.kind == .test_exe) { - run_step.addArg(exe.builder.zig_exe); - } - - if (exe.vcpkg_bin_path) |path| { - run_step.addPathDir(path); - } - - return run_step; -} - -/// Creates an `EmulatableRunStep` with an executable built with `addExecutable`. -/// Allows running foreign binaries through emulation platforms such as Qemu or Rosetta. -/// When a binary cannot be ran through emulation or the option is disabled, a warning -/// will be printed and the binary will *NOT* be ran. -pub fn runEmulatable(exe: *LibExeObjStep) *EmulatableRunStep { - assert(exe.kind == .exe or exe.kind == .test_exe); - - const run_step = EmulatableRunStep.create(exe.builder, exe.builder.fmt("run {s}", .{exe.step.name}), exe); - if (exe.vcpkg_bin_path) |path| { - RunStep.addPathDirInternal(&run_step.step, exe.builder, path); - } - return run_step; -} - -pub fn checkObject(self: *LibExeObjStep, obj_format: std.Target.ObjectFormat) *CheckObjectStep { - return CheckObjectStep.create(self.builder, self.getOutputSource(), obj_format); -} - -pub fn setLinkerScriptPath(self: *LibExeObjStep, source: FileSource) void { - self.linker_script = source.dupe(self.builder); - source.addStepDependencies(&self.step); -} - -pub fn linkFramework(self: *LibExeObjStep, framework_name: []const u8) void { - self.frameworks.put(self.builder.dupe(framework_name), .{}) catch unreachable; -} - -pub fn linkFrameworkNeeded(self: *LibExeObjStep, framework_name: []const u8) void { - self.frameworks.put(self.builder.dupe(framework_name), .{ - .needed = true, - }) catch unreachable; -} - -pub fn linkFrameworkWeak(self: *LibExeObjStep, framework_name: []const u8) void { - self.frameworks.put(self.builder.dupe(framework_name), .{ - .weak = true, - }) catch unreachable; -} - -/// Returns whether the library, executable, or object depends on a particular system library. -pub fn dependsOnSystemLibrary(self: LibExeObjStep, name: []const u8) bool { - if (isLibCLibrary(name)) { - return self.is_linking_libc; - } - if (isLibCppLibrary(name)) { - return self.is_linking_libcpp; - } - for (self.link_objects.items) |link_object| { - switch (link_object) { - .system_lib => |lib| if (mem.eql(u8, lib.name, name)) return true, - else => continue, - } - } - return false; -} - -pub fn linkLibrary(self: *LibExeObjStep, lib: *LibExeObjStep) void { - assert(lib.kind == .lib); - self.linkLibraryOrObject(lib); -} - -pub fn isDynamicLibrary(self: *LibExeObjStep) bool { - return self.kind == .lib and self.linkage == Linkage.dynamic; -} - -pub fn isStaticLibrary(self: *LibExeObjStep) bool { - return self.kind == .lib and self.linkage != Linkage.dynamic; -} - -pub fn producesPdbFile(self: *LibExeObjStep) bool { - if (!self.target.isWindows() and !self.target.isUefi()) return false; - if (self.target.getObjectFormat() == .c) return false; - if (self.strip == true) return false; - return self.isDynamicLibrary() or self.kind == .exe or self.kind == .test_exe; -} - -pub fn linkLibC(self: *LibExeObjStep) void { - self.is_linking_libc = true; -} - -pub fn linkLibCpp(self: *LibExeObjStep) void { - self.is_linking_libcpp = true; -} - -/// If the value is omitted, it is set to 1. -/// `name` and `value` need not live longer than the function call. -pub fn defineCMacro(self: *LibExeObjStep, name: []const u8, value: ?[]const u8) void { - const macro = std.Build.constructCMacro(self.builder.allocator, name, value); - self.c_macros.append(macro) catch unreachable; -} - -/// name_and_value looks like [name]=[value]. If the value is omitted, it is set to 1. -pub fn defineCMacroRaw(self: *LibExeObjStep, name_and_value: []const u8) void { - self.c_macros.append(self.builder.dupe(name_and_value)) catch unreachable; -} - -/// This one has no integration with anything, it just puts -lname on the command line. -/// Prefer to use `linkSystemLibrary` instead. -pub fn linkSystemLibraryName(self: *LibExeObjStep, name: []const u8) void { - self.link_objects.append(.{ - .system_lib = .{ - .name = self.builder.dupe(name), - .needed = false, - .weak = false, - .use_pkg_config = .no, - }, - }) catch unreachable; -} - -/// This one has no integration with anything, it just puts -needed-lname on the command line. -/// Prefer to use `linkSystemLibraryNeeded` instead. -pub fn linkSystemLibraryNeededName(self: *LibExeObjStep, name: []const u8) void { - self.link_objects.append(.{ - .system_lib = .{ - .name = self.builder.dupe(name), - .needed = true, - .weak = false, - .use_pkg_config = .no, - }, - }) catch unreachable; -} - -/// Darwin-only. This one has no integration with anything, it just puts -weak-lname on the -/// command line. Prefer to use `linkSystemLibraryWeak` instead. -pub fn linkSystemLibraryWeakName(self: *LibExeObjStep, name: []const u8) void { - self.link_objects.append(.{ - .system_lib = .{ - .name = self.builder.dupe(name), - .needed = false, - .weak = true, - .use_pkg_config = .no, - }, - }) catch unreachable; -} - -/// This links against a system library, exclusively using pkg-config to find the library. -/// Prefer to use `linkSystemLibrary` instead. -pub fn linkSystemLibraryPkgConfigOnly(self: *LibExeObjStep, lib_name: []const u8) void { - self.link_objects.append(.{ - .system_lib = .{ - .name = self.builder.dupe(lib_name), - .needed = false, - .weak = false, - .use_pkg_config = .force, - }, - }) catch unreachable; -} - -/// This links against a system library, exclusively using pkg-config to find the library. -/// Prefer to use `linkSystemLibraryNeeded` instead. -pub fn linkSystemLibraryNeededPkgConfigOnly(self: *LibExeObjStep, lib_name: []const u8) void { - self.link_objects.append(.{ - .system_lib = .{ - .name = self.builder.dupe(lib_name), - .needed = true, - .weak = false, - .use_pkg_config = .force, - }, - }) catch unreachable; -} - -/// Run pkg-config for the given library name and parse the output, returning the arguments -/// that should be passed to zig to link the given library. -pub fn runPkgConfig(self: *LibExeObjStep, lib_name: []const u8) ![]const []const u8 { - const pkg_name = match: { - // First we have to map the library name to pkg config name. Unfortunately, - // there are several examples where this is not straightforward: - // -lSDL2 -> pkg-config sdl2 - // -lgdk-3 -> pkg-config gdk-3.0 - // -latk-1.0 -> pkg-config atk - const pkgs = try getPkgConfigList(self.builder); - - // Exact match means instant winner. - for (pkgs) |pkg| { - if (mem.eql(u8, pkg.name, lib_name)) { - break :match pkg.name; - } - } - - // Next we'll try ignoring case. - for (pkgs) |pkg| { - if (std.ascii.eqlIgnoreCase(pkg.name, lib_name)) { - break :match pkg.name; - } - } - - // Now try appending ".0". - for (pkgs) |pkg| { - if (std.ascii.indexOfIgnoreCase(pkg.name, lib_name)) |pos| { - if (pos != 0) continue; - if (mem.eql(u8, pkg.name[lib_name.len..], ".0")) { - break :match pkg.name; - } - } - } - - // Trimming "-1.0". - if (mem.endsWith(u8, lib_name, "-1.0")) { - const trimmed_lib_name = lib_name[0 .. lib_name.len - "-1.0".len]; - for (pkgs) |pkg| { - if (std.ascii.eqlIgnoreCase(pkg.name, trimmed_lib_name)) { - break :match pkg.name; - } - } - } - - return error.PackageNotFound; - }; - - var code: u8 = undefined; - const stdout = if (self.builder.execAllowFail(&[_][]const u8{ - "pkg-config", - pkg_name, - "--cflags", - "--libs", - }, &code, .Ignore)) |stdout| stdout else |err| switch (err) { - error.ProcessTerminated => return error.PkgConfigCrashed, - error.ExecNotSupported => return error.PkgConfigFailed, - error.ExitCodeFailure => return error.PkgConfigFailed, - error.FileNotFound => return error.PkgConfigNotInstalled, - error.ChildExecFailed => return error.PkgConfigFailed, - else => return err, - }; - - var zig_args = ArrayList([]const u8).init(self.builder.allocator); - defer zig_args.deinit(); - - var it = mem.tokenize(u8, stdout, " \r\n\t"); - while (it.next()) |tok| { - if (mem.eql(u8, tok, "-I")) { - const dir = it.next() orelse return error.PkgConfigInvalidOutput; - try zig_args.appendSlice(&[_][]const u8{ "-I", dir }); - } else if (mem.startsWith(u8, tok, "-I")) { - try zig_args.append(tok); - } else if (mem.eql(u8, tok, "-L")) { - const dir = it.next() orelse return error.PkgConfigInvalidOutput; - try zig_args.appendSlice(&[_][]const u8{ "-L", dir }); - } else if (mem.startsWith(u8, tok, "-L")) { - try zig_args.append(tok); - } else if (mem.eql(u8, tok, "-l")) { - const lib = it.next() orelse return error.PkgConfigInvalidOutput; - try zig_args.appendSlice(&[_][]const u8{ "-l", lib }); - } else if (mem.startsWith(u8, tok, "-l")) { - try zig_args.append(tok); - } else if (mem.eql(u8, tok, "-D")) { - const macro = it.next() orelse return error.PkgConfigInvalidOutput; - try zig_args.appendSlice(&[_][]const u8{ "-D", macro }); - } else if (mem.startsWith(u8, tok, "-D")) { - try zig_args.append(tok); - } else if (self.builder.verbose) { - log.warn("Ignoring pkg-config flag '{s}'", .{tok}); - } - } - - return zig_args.toOwnedSlice(); -} - -pub fn linkSystemLibrary(self: *LibExeObjStep, name: []const u8) void { - self.linkSystemLibraryInner(name, .{}); -} - -pub fn linkSystemLibraryNeeded(self: *LibExeObjStep, name: []const u8) void { - self.linkSystemLibraryInner(name, .{ .needed = true }); -} - -pub fn linkSystemLibraryWeak(self: *LibExeObjStep, name: []const u8) void { - self.linkSystemLibraryInner(name, .{ .weak = true }); -} - -fn linkSystemLibraryInner(self: *LibExeObjStep, name: []const u8, opts: struct { - needed: bool = false, - weak: bool = false, -}) void { - if (isLibCLibrary(name)) { - self.linkLibC(); - return; - } - if (isLibCppLibrary(name)) { - self.linkLibCpp(); - return; - } - - self.link_objects.append(.{ - .system_lib = .{ - .name = self.builder.dupe(name), - .needed = opts.needed, - .weak = opts.weak, - .use_pkg_config = .yes, - }, - }) catch unreachable; -} - -pub fn setNamePrefix(self: *LibExeObjStep, text: []const u8) void { - assert(self.kind == .@"test" or self.kind == .test_exe); - self.name_prefix = self.builder.dupe(text); -} - -pub fn setFilter(self: *LibExeObjStep, text: ?[]const u8) void { - assert(self.kind == .@"test" or self.kind == .test_exe); - self.filter = if (text) |t| self.builder.dupe(t) else null; -} - -pub fn setTestRunner(self: *LibExeObjStep, path: ?[]const u8) void { - assert(self.kind == .@"test" or self.kind == .test_exe); - self.test_runner = if (path) |p| self.builder.dupePath(p) else null; -} - -/// Handy when you have many C/C++ source files and want them all to have the same flags. -pub fn addCSourceFiles(self: *LibExeObjStep, files: []const []const u8, flags: []const []const u8) void { - const c_source_files = self.builder.allocator.create(CSourceFiles) catch unreachable; - - const files_copy = self.builder.dupeStrings(files); - const flags_copy = self.builder.dupeStrings(flags); - - c_source_files.* = .{ - .files = files_copy, - .flags = flags_copy, - }; - self.link_objects.append(.{ .c_source_files = c_source_files }) catch unreachable; -} - -pub fn addCSourceFile(self: *LibExeObjStep, file: []const u8, flags: []const []const u8) void { - self.addCSourceFileSource(.{ - .args = flags, - .source = .{ .path = file }, - }); -} - -pub fn addCSourceFileSource(self: *LibExeObjStep, source: CSourceFile) void { - const c_source_file = self.builder.allocator.create(CSourceFile) catch unreachable; - c_source_file.* = source.dupe(self.builder); - self.link_objects.append(.{ .c_source_file = c_source_file }) catch unreachable; - source.source.addStepDependencies(&self.step); -} - -pub fn setVerboseLink(self: *LibExeObjStep, value: bool) void { - self.verbose_link = value; -} - -pub fn setVerboseCC(self: *LibExeObjStep, value: bool) void { - self.verbose_cc = value; -} - -pub fn overrideZigLibDir(self: *LibExeObjStep, dir_path: []const u8) void { - self.override_lib_dir = self.builder.dupePath(dir_path); -} - -pub fn setMainPkgPath(self: *LibExeObjStep, dir_path: []const u8) void { - self.main_pkg_path = self.builder.dupePath(dir_path); -} - -pub fn setLibCFile(self: *LibExeObjStep, libc_file: ?FileSource) void { - self.libc_file = if (libc_file) |f| f.dupe(self.builder) else null; -} - -/// Returns the generated executable, library or object file. -/// To run an executable built with zig build, use `run`, or create an install step and invoke it. -pub fn getOutputSource(self: *LibExeObjStep) FileSource { - return FileSource{ .generated = &self.output_path_source }; -} - -/// Returns the generated import library. This function can only be called for libraries. -pub fn getOutputLibSource(self: *LibExeObjStep) FileSource { - assert(self.kind == .lib); - return FileSource{ .generated = &self.output_lib_path_source }; -} - -/// Returns the generated header file. -/// This function can only be called for libraries or object files which have `emit_h` set. -pub fn getOutputHSource(self: *LibExeObjStep) FileSource { - assert(self.kind != .exe and self.kind != .test_exe and self.kind != .@"test"); - assert(self.emit_h); - return FileSource{ .generated = &self.output_h_path_source }; -} - -/// Returns the generated PDB file. This function can only be called for Windows and UEFI. -pub fn getOutputPdbSource(self: *LibExeObjStep) FileSource { - // TODO: Is this right? Isn't PDB for *any* PE/COFF file? - assert(self.target.isWindows() or self.target.isUefi()); - return FileSource{ .generated = &self.output_pdb_path_source }; -} - -pub fn addAssemblyFile(self: *LibExeObjStep, path: []const u8) void { - self.link_objects.append(.{ - .assembly_file = .{ .path = self.builder.dupe(path) }, - }) catch unreachable; -} - -pub fn addAssemblyFileSource(self: *LibExeObjStep, source: FileSource) void { - const source_duped = source.dupe(self.builder); - self.link_objects.append(.{ .assembly_file = source_duped }) catch unreachable; - source_duped.addStepDependencies(&self.step); -} - -pub fn addObjectFile(self: *LibExeObjStep, source_file: []const u8) void { - self.addObjectFileSource(.{ .path = source_file }); -} - -pub fn addObjectFileSource(self: *LibExeObjStep, source: FileSource) void { - self.link_objects.append(.{ .static_path = source.dupe(self.builder) }) catch unreachable; - source.addStepDependencies(&self.step); -} - -pub fn addObject(self: *LibExeObjStep, obj: *LibExeObjStep) void { - assert(obj.kind == .obj); - self.linkLibraryOrObject(obj); -} - -pub const addSystemIncludeDir = @compileError("deprecated; use addSystemIncludePath"); -pub const addIncludeDir = @compileError("deprecated; use addIncludePath"); -pub const addLibPath = @compileError("deprecated, use addLibraryPath"); -pub const addFrameworkDir = @compileError("deprecated, use addFrameworkPath"); - -pub fn addSystemIncludePath(self: *LibExeObjStep, path: []const u8) void { - self.include_dirs.append(IncludeDir{ .raw_path_system = self.builder.dupe(path) }) catch unreachable; -} - -pub fn addIncludePath(self: *LibExeObjStep, path: []const u8) void { - self.include_dirs.append(IncludeDir{ .raw_path = self.builder.dupe(path) }) catch unreachable; -} - -pub fn addConfigHeader(self: *LibExeObjStep, config_header: *ConfigHeaderStep) void { - self.step.dependOn(&config_header.step); - self.include_dirs.append(.{ .config_header_step = config_header }) catch @panic("OOM"); -} - -pub fn addLibraryPath(self: *LibExeObjStep, path: []const u8) void { - self.lib_paths.append(self.builder.dupe(path)) catch unreachable; -} - -pub fn addRPath(self: *LibExeObjStep, path: []const u8) void { - self.rpaths.append(self.builder.dupe(path)) catch unreachable; -} - -pub fn addFrameworkPath(self: *LibExeObjStep, dir_path: []const u8) void { - self.framework_dirs.append(self.builder.dupe(dir_path)) catch unreachable; -} - -pub fn addPackage(self: *LibExeObjStep, package: Pkg) void { - self.packages.append(self.builder.dupePkg(package)) catch unreachable; - self.addRecursiveBuildDeps(package); -} - -pub fn addOptions(self: *LibExeObjStep, package_name: []const u8, options: *OptionsStep) void { - self.addPackage(options.getPackage(package_name)); -} - -fn addRecursiveBuildDeps(self: *LibExeObjStep, package: Pkg) void { - package.source.addStepDependencies(&self.step); - if (package.dependencies) |deps| { - for (deps) |dep| { - self.addRecursiveBuildDeps(dep); - } - } -} - -pub fn addPackagePath(self: *LibExeObjStep, name: []const u8, pkg_index_path: []const u8) void { - self.addPackage(Pkg{ - .name = self.builder.dupe(name), - .source = .{ .path = self.builder.dupe(pkg_index_path) }, - }); -} - -/// If Vcpkg was found on the system, it will be added to include and lib -/// paths for the specified target. -pub fn addVcpkgPaths(self: *LibExeObjStep, linkage: LibExeObjStep.Linkage) !void { - // Ideally in the Unattempted case we would call the function recursively - // after findVcpkgRoot and have only one switch statement, but the compiler - // cannot resolve the error set. - switch (self.builder.vcpkg_root) { - .unattempted => { - self.builder.vcpkg_root = if (try findVcpkgRoot(self.builder.allocator)) |root| - VcpkgRoot{ .found = root } - else - .not_found; - }, - .not_found => return error.VcpkgNotFound, - .found => {}, - } - - switch (self.builder.vcpkg_root) { - .unattempted => unreachable, - .not_found => return error.VcpkgNotFound, - .found => |root| { - const allocator = self.builder.allocator; - const triplet = try self.target.vcpkgTriplet(allocator, if (linkage == .static) .Static else .Dynamic); - defer self.builder.allocator.free(triplet); - - const include_path = self.builder.pathJoin(&.{ root, "installed", triplet, "include" }); - errdefer allocator.free(include_path); - try self.include_dirs.append(IncludeDir{ .raw_path = include_path }); - - const lib_path = self.builder.pathJoin(&.{ root, "installed", triplet, "lib" }); - try self.lib_paths.append(lib_path); - - self.vcpkg_bin_path = self.builder.pathJoin(&.{ root, "installed", triplet, "bin" }); - }, - } -} - -pub fn setExecCmd(self: *LibExeObjStep, args: []const ?[]const u8) void { - assert(self.kind == .@"test"); - const duped_args = self.builder.allocator.alloc(?[]u8, args.len) catch unreachable; - for (args) |arg, i| { - duped_args[i] = if (arg) |a| self.builder.dupe(a) else null; - } - self.exec_cmd_args = duped_args; -} - -fn linkLibraryOrObject(self: *LibExeObjStep, other: *LibExeObjStep) void { - self.step.dependOn(&other.step); - self.link_objects.append(.{ .other_step = other }) catch unreachable; - self.include_dirs.append(.{ .other_step = other }) catch unreachable; -} - -fn makePackageCmd(self: *LibExeObjStep, pkg: Pkg, zig_args: *ArrayList([]const u8)) error{OutOfMemory}!void { - const builder = self.builder; - - try zig_args.append("--pkg-begin"); - try zig_args.append(pkg.name); - try zig_args.append(builder.pathFromRoot(pkg.source.getPath(self.builder))); - - if (pkg.dependencies) |dependencies| { - for (dependencies) |sub_pkg| { - try self.makePackageCmd(sub_pkg, zig_args); - } - } - - try zig_args.append("--pkg-end"); -} - -fn make(step: *Step) !void { - const self = @fieldParentPtr(LibExeObjStep, "step", step); - const builder = self.builder; - - if (self.root_src == null and self.link_objects.items.len == 0) { - log.err("{s}: linker needs 1 or more objects to link", .{self.step.name}); - return error.NeedAnObject; - } - - var zig_args = ArrayList([]const u8).init(builder.allocator); - defer zig_args.deinit(); - - zig_args.append(builder.zig_exe) catch unreachable; - - const cmd = switch (self.kind) { - .lib => "build-lib", - .exe => "build-exe", - .obj => "build-obj", - .@"test" => "test", - .test_exe => "test", - }; - zig_args.append(cmd) catch unreachable; - - if (builder.color != .auto) { - try zig_args.append("--color"); - try zig_args.append(@tagName(builder.color)); - } - - if (builder.reference_trace) |some| { - try zig_args.append(try std.fmt.allocPrint(builder.allocator, "-freference-trace={d}", .{some})); - } - - try addFlag(&zig_args, "LLVM", self.use_llvm); - try addFlag(&zig_args, "LLD", self.use_lld); - - if (self.target.ofmt) |ofmt| { - try zig_args.append(try std.fmt.allocPrint(builder.allocator, "-ofmt={s}", .{@tagName(ofmt)})); - } - - if (self.entry_symbol_name) |entry| { - try zig_args.append("--entry"); - try zig_args.append(entry); - } - - if (self.stack_size) |stack_size| { - try zig_args.append("--stack"); - try zig_args.append(try std.fmt.allocPrint(builder.allocator, "{}", .{stack_size})); - } - - if (self.root_src) |root_src| try zig_args.append(root_src.getPath(builder)); - - // We will add link objects from transitive dependencies, but we want to keep - // all link objects in the same order provided. - // This array is used to keep self.link_objects immutable. - var transitive_deps: TransitiveDeps = .{ - .link_objects = ArrayList(LinkObject).init(builder.allocator), - .seen_system_libs = StringHashMap(void).init(builder.allocator), - .seen_steps = std.AutoHashMap(*const Step, void).init(builder.allocator), - .is_linking_libcpp = self.is_linking_libcpp, - .is_linking_libc = self.is_linking_libc, - .frameworks = &self.frameworks, - }; - - try transitive_deps.seen_steps.put(&self.step, {}); - try transitive_deps.add(self.link_objects.items); - - var prev_has_extra_flags = false; - - for (transitive_deps.link_objects.items) |link_object| { - switch (link_object) { - .static_path => |static_path| try zig_args.append(static_path.getPath(builder)), - - .other_step => |other| switch (other.kind) { - .exe => @panic("Cannot link with an executable build artifact"), - .test_exe => @panic("Cannot link with an executable build artifact"), - .@"test" => @panic("Cannot link with a test"), - .obj => { - try zig_args.append(other.getOutputSource().getPath(builder)); - }, - .lib => l: { - if (self.isStaticLibrary() and other.isStaticLibrary()) { - // Avoid putting a static library inside a static library. - break :l; - } - - const full_path_lib = other.getOutputLibSource().getPath(builder); - try zig_args.append(full_path_lib); - - if (other.linkage == Linkage.dynamic and !self.target.isWindows()) { - if (fs.path.dirname(full_path_lib)) |dirname| { - try zig_args.append("-rpath"); - try zig_args.append(dirname); - } - } - }, - }, - - .system_lib => |system_lib| { - const prefix: []const u8 = prefix: { - if (system_lib.needed) break :prefix "-needed-l"; - if (system_lib.weak) { - if (self.target.isDarwin()) break :prefix "-weak-l"; - log.warn("Weak library import used for a non-darwin target, this will be converted to normally library import `-lname`", .{}); - } - break :prefix "-l"; - }; - switch (system_lib.use_pkg_config) { - .no => try zig_args.append(builder.fmt("{s}{s}", .{ prefix, system_lib.name })), - .yes, .force => { - if (self.runPkgConfig(system_lib.name)) |args| { - try zig_args.appendSlice(args); - } else |err| switch (err) { - error.PkgConfigInvalidOutput, - error.PkgConfigCrashed, - error.PkgConfigFailed, - error.PkgConfigNotInstalled, - error.PackageNotFound, - => switch (system_lib.use_pkg_config) { - .yes => { - // pkg-config failed, so fall back to linking the library - // by name directly. - try zig_args.append(builder.fmt("{s}{s}", .{ - prefix, - system_lib.name, - })); - }, - .force => { - panic("pkg-config failed for library {s}", .{system_lib.name}); - }, - .no => unreachable, - }, - - else => |e| return e, - } - }, - } - }, - - .assembly_file => |asm_file| { - if (prev_has_extra_flags) { - try zig_args.append("-extra-cflags"); - try zig_args.append("--"); - prev_has_extra_flags = false; - } - try zig_args.append(asm_file.getPath(builder)); - }, - - .c_source_file => |c_source_file| { - if (c_source_file.args.len == 0) { - if (prev_has_extra_flags) { - try zig_args.append("-cflags"); - try zig_args.append("--"); - prev_has_extra_flags = false; - } - } else { - try zig_args.append("-cflags"); - for (c_source_file.args) |arg| { - try zig_args.append(arg); - } - try zig_args.append("--"); - } - try zig_args.append(c_source_file.source.getPath(builder)); - }, - - .c_source_files => |c_source_files| { - if (c_source_files.flags.len == 0) { - if (prev_has_extra_flags) { - try zig_args.append("-cflags"); - try zig_args.append("--"); - prev_has_extra_flags = false; - } - } else { - try zig_args.append("-cflags"); - for (c_source_files.flags) |flag| { - try zig_args.append(flag); - } - try zig_args.append("--"); - } - for (c_source_files.files) |file| { - try zig_args.append(builder.pathFromRoot(file)); - } - }, - } - } - - if (transitive_deps.is_linking_libcpp) { - try zig_args.append("-lc++"); - } - - if (transitive_deps.is_linking_libc) { - try zig_args.append("-lc"); - } - - if (self.image_base) |image_base| { - try zig_args.append("--image-base"); - try zig_args.append(builder.fmt("0x{x}", .{image_base})); - } - - if (self.filter) |filter| { - try zig_args.append("--test-filter"); - try zig_args.append(filter); - } - - if (self.test_evented_io) { - try zig_args.append("--test-evented-io"); - } - - if (self.name_prefix.len != 0) { - try zig_args.append("--test-name-prefix"); - try zig_args.append(self.name_prefix); - } - - if (self.test_runner) |test_runner| { - try zig_args.append("--test-runner"); - try zig_args.append(builder.pathFromRoot(test_runner)); - } - - for (builder.debug_log_scopes) |log_scope| { - try zig_args.append("--debug-log"); - try zig_args.append(log_scope); - } - - if (builder.debug_compile_errors) { - try zig_args.append("--debug-compile-errors"); - } - - if (builder.verbose_cimport) zig_args.append("--verbose-cimport") catch unreachable; - if (builder.verbose_air) zig_args.append("--verbose-air") catch unreachable; - if (builder.verbose_llvm_ir) zig_args.append("--verbose-llvm-ir") catch unreachable; - if (builder.verbose_link or self.verbose_link) zig_args.append("--verbose-link") catch unreachable; - if (builder.verbose_cc or self.verbose_cc) zig_args.append("--verbose-cc") catch unreachable; - if (builder.verbose_llvm_cpu_features) zig_args.append("--verbose-llvm-cpu-features") catch unreachable; - - if (self.emit_analysis.getArg(builder, "emit-analysis")) |arg| try zig_args.append(arg); - if (self.emit_asm.getArg(builder, "emit-asm")) |arg| try zig_args.append(arg); - if (self.emit_bin.getArg(builder, "emit-bin")) |arg| try zig_args.append(arg); - if (self.emit_docs.getArg(builder, "emit-docs")) |arg| try zig_args.append(arg); - if (self.emit_implib.getArg(builder, "emit-implib")) |arg| try zig_args.append(arg); - if (self.emit_llvm_bc.getArg(builder, "emit-llvm-bc")) |arg| try zig_args.append(arg); - if (self.emit_llvm_ir.getArg(builder, "emit-llvm-ir")) |arg| try zig_args.append(arg); - - if (self.emit_h) try zig_args.append("-femit-h"); - - try addFlag(&zig_args, "strip", self.strip); - try addFlag(&zig_args, "unwind-tables", self.unwind_tables); - - switch (self.compress_debug_sections) { - .none => {}, - .zlib => try zig_args.append("--compress-debug-sections=zlib"), - } - - if (self.link_eh_frame_hdr) { - try zig_args.append("--eh-frame-hdr"); - } - if (self.link_emit_relocs) { - try zig_args.append("--emit-relocs"); - } - if (self.link_function_sections) { - try zig_args.append("-ffunction-sections"); - } - if (self.link_gc_sections) |x| { - try zig_args.append(if (x) "--gc-sections" else "--no-gc-sections"); - } - if (self.linker_allow_shlib_undefined) |x| { - try zig_args.append(if (x) "-fallow-shlib-undefined" else "-fno-allow-shlib-undefined"); - } - if (self.link_z_notext) { - try zig_args.append("-z"); - try zig_args.append("notext"); - } - if (!self.link_z_relro) { - try zig_args.append("-z"); - try zig_args.append("norelro"); - } - if (self.link_z_lazy) { - try zig_args.append("-z"); - try zig_args.append("lazy"); - } - if (self.link_z_common_page_size) |size| { - try zig_args.append("-z"); - try zig_args.append(builder.fmt("common-page-size={d}", .{size})); - } - if (self.link_z_max_page_size) |size| { - try zig_args.append("-z"); - try zig_args.append(builder.fmt("max-page-size={d}", .{size})); - } - - if (self.libc_file) |libc_file| { - try zig_args.append("--libc"); - try zig_args.append(libc_file.getPath(builder)); - } else if (builder.libc_file) |libc_file| { - try zig_args.append("--libc"); - try zig_args.append(libc_file); - } - - switch (self.optimize) { - .Debug => {}, // Skip since it's the default. - else => zig_args.append(builder.fmt("-O{s}", .{@tagName(self.optimize)})) catch unreachable, - } - - try zig_args.append("--cache-dir"); - try zig_args.append(builder.pathFromRoot(builder.cache_root)); - - try zig_args.append("--global-cache-dir"); - try zig_args.append(builder.pathFromRoot(builder.global_cache_root)); - - zig_args.append("--name") catch unreachable; - zig_args.append(self.name) catch unreachable; - - if (self.linkage) |some| switch (some) { - .dynamic => try zig_args.append("-dynamic"), - .static => try zig_args.append("-static"), - }; - if (self.kind == .lib and self.linkage != null and self.linkage.? == .dynamic) { - if (self.version) |version| { - zig_args.append("--version") catch unreachable; - zig_args.append(builder.fmt("{}", .{version})) catch unreachable; - } - - if (self.target.isDarwin()) { - const install_name = self.install_name orelse builder.fmt("@rpath/{s}{s}{s}", .{ - self.target.libPrefix(), - self.name, - self.target.dynamicLibSuffix(), - }); - try zig_args.append("-install_name"); - try zig_args.append(install_name); - } - } - - if (self.entitlements) |entitlements| { - try zig_args.appendSlice(&[_][]const u8{ "--entitlements", entitlements }); - } - if (self.pagezero_size) |pagezero_size| { - const size = try std.fmt.allocPrint(builder.allocator, "{x}", .{pagezero_size}); - try zig_args.appendSlice(&[_][]const u8{ "-pagezero_size", size }); - } - if (self.search_strategy) |strat| switch (strat) { - .paths_first => try zig_args.append("-search_paths_first"), - .dylibs_first => try zig_args.append("-search_dylibs_first"), - }; - if (self.headerpad_size) |headerpad_size| { - const size = try std.fmt.allocPrint(builder.allocator, "{x}", .{headerpad_size}); - try zig_args.appendSlice(&[_][]const u8{ "-headerpad", size }); - } - if (self.headerpad_max_install_names) { - try zig_args.append("-headerpad_max_install_names"); - } - if (self.dead_strip_dylibs) { - try zig_args.append("-dead_strip_dylibs"); - } - - try addFlag(&zig_args, "compiler-rt", self.bundle_compiler_rt); - try addFlag(&zig_args, "single-threaded", self.single_threaded); - if (self.disable_stack_probing) { - try zig_args.append("-fno-stack-check"); - } - try addFlag(&zig_args, "stack-protector", self.stack_protector); - if (self.red_zone) |red_zone| { - if (red_zone) { - try zig_args.append("-mred-zone"); - } else { - try zig_args.append("-mno-red-zone"); - } - } - try addFlag(&zig_args, "omit-frame-pointer", self.omit_frame_pointer); - try addFlag(&zig_args, "dll-export-fns", self.dll_export_fns); - - if (self.disable_sanitize_c) { - try zig_args.append("-fno-sanitize-c"); - } - if (self.sanitize_thread) { - try zig_args.append("-fsanitize-thread"); - } - if (self.rdynamic) { - try zig_args.append("-rdynamic"); - } - if (self.import_memory) { - try zig_args.append("--import-memory"); - } - if (self.import_symbols) { - try zig_args.append("--import-symbols"); - } - if (self.import_table) { - try zig_args.append("--import-table"); - } - if (self.export_table) { - try zig_args.append("--export-table"); - } - if (self.initial_memory) |initial_memory| { - try zig_args.append(builder.fmt("--initial-memory={d}", .{initial_memory})); - } - if (self.max_memory) |max_memory| { - try zig_args.append(builder.fmt("--max-memory={d}", .{max_memory})); - } - if (self.shared_memory) { - try zig_args.append("--shared-memory"); - } - if (self.global_base) |global_base| { - try zig_args.append(builder.fmt("--global-base={d}", .{global_base})); - } - - if (self.code_model != .default) { - try zig_args.append("-mcmodel"); - try zig_args.append(@tagName(self.code_model)); - } - if (self.wasi_exec_model) |model| { - try zig_args.append(builder.fmt("-mexec-model={s}", .{@tagName(model)})); - } - for (self.export_symbol_names) |symbol_name| { - try zig_args.append(builder.fmt("--export={s}", .{symbol_name})); - } - - if (!self.target.isNative()) { - try zig_args.appendSlice(&.{ - "-target", try self.target.zigTriple(builder.allocator), - "-mcpu", try std.Build.serializeCpu(builder.allocator, self.target.getCpu()), - }); - - if (self.target.dynamic_linker.get()) |dynamic_linker| { - try zig_args.append("--dynamic-linker"); - try zig_args.append(dynamic_linker); - } - } - - if (self.linker_script) |linker_script| { - try zig_args.append("--script"); - try zig_args.append(linker_script.getPath(builder)); - } - - if (self.version_script) |version_script| { - try zig_args.append("--version-script"); - try zig_args.append(builder.pathFromRoot(version_script)); - } - - if (self.kind == .@"test") { - if (self.exec_cmd_args) |exec_cmd_args| { - for (exec_cmd_args) |cmd_arg| { - if (cmd_arg) |arg| { - try zig_args.append("--test-cmd"); - try zig_args.append(arg); - } else { - try zig_args.append("--test-cmd-bin"); - } - } - } else { - const need_cross_glibc = self.target.isGnuLibC() and transitive_deps.is_linking_libc; - - switch (builder.host.getExternalExecutor(self.target_info, .{ - .qemu_fixes_dl = need_cross_glibc and builder.glibc_runtimes_dir != null, - .link_libc = transitive_deps.is_linking_libc, - })) { - .native => {}, - .bad_dl, .bad_os_or_cpu => { - try zig_args.append("--test-no-exec"); - }, - .rosetta => if (builder.enable_rosetta) { - try zig_args.append("--test-cmd-bin"); - } else { - try zig_args.append("--test-no-exec"); - }, - .qemu => |bin_name| ok: { - if (builder.enable_qemu) qemu: { - const glibc_dir_arg = if (need_cross_glibc) - builder.glibc_runtimes_dir orelse break :qemu - else - null; - try zig_args.append("--test-cmd"); - try zig_args.append(bin_name); - if (glibc_dir_arg) |dir| { - // TODO look into making this a call to `linuxTriple`. This - // needs the directory to be called "i686" rather than - // "x86" which is why we do it manually here. - const fmt_str = "{s}" ++ fs.path.sep_str ++ "{s}-{s}-{s}"; - const cpu_arch = self.target.getCpuArch(); - const os_tag = self.target.getOsTag(); - const abi = self.target.getAbi(); - const cpu_arch_name: []const u8 = if (cpu_arch == .x86) - "i686" - else - @tagName(cpu_arch); - const full_dir = try std.fmt.allocPrint(builder.allocator, fmt_str, .{ - dir, cpu_arch_name, @tagName(os_tag), @tagName(abi), - }); - - try zig_args.append("--test-cmd"); - try zig_args.append("-L"); - try zig_args.append("--test-cmd"); - try zig_args.append(full_dir); - } - try zig_args.append("--test-cmd-bin"); - break :ok; - } - try zig_args.append("--test-no-exec"); - }, - .wine => |bin_name| if (builder.enable_wine) { - try zig_args.append("--test-cmd"); - try zig_args.append(bin_name); - try zig_args.append("--test-cmd-bin"); - } else { - try zig_args.append("--test-no-exec"); - }, - .wasmtime => |bin_name| if (builder.enable_wasmtime) { - try zig_args.append("--test-cmd"); - try zig_args.append(bin_name); - try zig_args.append("--test-cmd"); - try zig_args.append("--dir=."); - try zig_args.append("--test-cmd-bin"); - } else { - try zig_args.append("--test-no-exec"); - }, - .darling => |bin_name| if (builder.enable_darling) { - try zig_args.append("--test-cmd"); - try zig_args.append(bin_name); - try zig_args.append("--test-cmd-bin"); - } else { - try zig_args.append("--test-no-exec"); - }, - } - } - } else if (self.kind == .test_exe) { - try zig_args.append("--test-no-exec"); - } - - for (self.packages.items) |pkg| { - try self.makePackageCmd(pkg, &zig_args); - } - - for (self.include_dirs.items) |include_dir| { - switch (include_dir) { - .raw_path => |include_path| { - try zig_args.append("-I"); - try zig_args.append(builder.pathFromRoot(include_path)); - }, - .raw_path_system => |include_path| { - if (builder.sysroot != null) { - try zig_args.append("-iwithsysroot"); - } else { - try zig_args.append("-isystem"); - } - - const resolved_include_path = builder.pathFromRoot(include_path); - - const common_include_path = if (builtin.os.tag == .windows and builder.sysroot != null and fs.path.isAbsolute(resolved_include_path)) blk: { - // We need to check for disk designator and strip it out from dir path so - // that zig/clang can concat resolved_include_path with sysroot. - const disk_designator = fs.path.diskDesignatorWindows(resolved_include_path); - - if (mem.indexOf(u8, resolved_include_path, disk_designator)) |where| { - break :blk resolved_include_path[where + disk_designator.len ..]; - } - - break :blk resolved_include_path; - } else resolved_include_path; - - try zig_args.append(common_include_path); - }, - .other_step => |other| { - if (other.emit_h) { - const h_path = other.getOutputHSource().getPath(builder); - try zig_args.append("-isystem"); - try zig_args.append(fs.path.dirname(h_path).?); - } - if (other.installed_headers.items.len > 0) { - for (other.installed_headers.items) |install_step| { - try install_step.make(); - } - try zig_args.append("-I"); - try zig_args.append(builder.pathJoin(&.{ - other.builder.install_prefix, "include", - })); - } - }, - .config_header_step => |config_header| { - try zig_args.append("-I"); - try zig_args.append(config_header.output_dir); - }, - } - } - - for (self.lib_paths.items) |lib_path| { - try zig_args.append("-L"); - try zig_args.append(lib_path); - } - - for (self.rpaths.items) |rpath| { - try zig_args.append("-rpath"); - try zig_args.append(rpath); - } - - for (self.c_macros.items) |c_macro| { - try zig_args.append("-D"); - try zig_args.append(c_macro); - } - - if (self.target.isDarwin()) { - for (self.framework_dirs.items) |dir| { - if (builder.sysroot != null) { - try zig_args.append("-iframeworkwithsysroot"); - } else { - try zig_args.append("-iframework"); - } - try zig_args.append(dir); - try zig_args.append("-F"); - try zig_args.append(dir); - } - - var it = self.frameworks.iterator(); - while (it.next()) |entry| { - const name = entry.key_ptr.*; - const info = entry.value_ptr.*; - if (info.needed) { - zig_args.append("-needed_framework") catch unreachable; - } else if (info.weak) { - zig_args.append("-weak_framework") catch unreachable; - } else { - zig_args.append("-framework") catch unreachable; - } - zig_args.append(name) catch unreachable; - } - } else { - if (self.framework_dirs.items.len > 0) { - log.info("Framework directories have been added for a non-darwin target, this will have no affect on the build", .{}); - } - - if (self.frameworks.count() > 0) { - log.info("Frameworks have been added for a non-darwin target, this will have no affect on the build", .{}); - } - } - - if (builder.sysroot) |sysroot| { - try zig_args.appendSlice(&[_][]const u8{ "--sysroot", sysroot }); - } - - for (builder.search_prefixes.items) |search_prefix| { - try zig_args.append("-L"); - try zig_args.append(builder.pathJoin(&.{ - search_prefix, "lib", - })); - try zig_args.append("-I"); - try zig_args.append(builder.pathJoin(&.{ - search_prefix, "include", - })); - } - - try addFlag(&zig_args, "valgrind", self.valgrind_support); - try addFlag(&zig_args, "each-lib-rpath", self.each_lib_rpath); - try addFlag(&zig_args, "build-id", self.build_id); - - if (self.override_lib_dir) |dir| { - try zig_args.append("--zig-lib-dir"); - try zig_args.append(builder.pathFromRoot(dir)); - } else if (builder.override_lib_dir) |dir| { - try zig_args.append("--zig-lib-dir"); - try zig_args.append(builder.pathFromRoot(dir)); - } - - if (self.main_pkg_path) |dir| { - try zig_args.append("--main-pkg-path"); - try zig_args.append(builder.pathFromRoot(dir)); - } - - try addFlag(&zig_args, "PIC", self.force_pic); - try addFlag(&zig_args, "PIE", self.pie); - try addFlag(&zig_args, "lto", self.want_lto); - - if (self.subsystem) |subsystem| { - try zig_args.append("--subsystem"); - try zig_args.append(switch (subsystem) { - .Console => "console", - .Windows => "windows", - .Posix => "posix", - .Native => "native", - .EfiApplication => "efi_application", - .EfiBootServiceDriver => "efi_boot_service_driver", - .EfiRom => "efi_rom", - .EfiRuntimeDriver => "efi_runtime_driver", - }); - } - - try zig_args.append("--enable-cache"); - - // Windows has an argument length limit of 32,766 characters, macOS 262,144 and Linux - // 2,097,152. If our args exceed 30 KiB, we instead write them to a "response file" and - // pass that to zig, e.g. via 'zig build-lib @args.rsp' - // See @file syntax here: https://gcc.gnu.org/onlinedocs/gcc/Overall-Options.html - var args_length: usize = 0; - for (zig_args.items) |arg| { - args_length += arg.len + 1; // +1 to account for null terminator - } - if (args_length >= 30 * 1024) { - const args_dir = try fs.path.join( - builder.allocator, - &[_][]const u8{ builder.pathFromRoot("zig-cache"), "args" }, - ); - try std.fs.cwd().makePath(args_dir); - - var args_arena = std.heap.ArenaAllocator.init(builder.allocator); - defer args_arena.deinit(); - - const args_to_escape = zig_args.items[2..]; - var escaped_args = try ArrayList([]const u8).initCapacity(args_arena.allocator(), args_to_escape.len); - - arg_blk: for (args_to_escape) |arg| { - for (arg) |c, arg_idx| { - if (c == '\\' or c == '"') { - // Slow path for arguments that need to be escaped. We'll need to allocate and copy - var escaped = try ArrayList(u8).initCapacity(args_arena.allocator(), arg.len + 1); - const writer = escaped.writer(); - writer.writeAll(arg[0..arg_idx]) catch unreachable; - for (arg[arg_idx..]) |to_escape| { - if (to_escape == '\\' or to_escape == '"') try writer.writeByte('\\'); - try writer.writeByte(to_escape); - } - escaped_args.appendAssumeCapacity(escaped.items); - continue :arg_blk; - } - } - escaped_args.appendAssumeCapacity(arg); // no escaping needed so just use original argument - } - - // Write the args to zig-cache/args/ to avoid conflicts with - // other zig build commands running in parallel. - const partially_quoted = try std.mem.join(builder.allocator, "\" \"", escaped_args.items); - const args = try std.mem.concat(builder.allocator, u8, &[_][]const u8{ "\"", partially_quoted, "\"" }); - - var args_hash: [Sha256.digest_length]u8 = undefined; - Sha256.hash(args, &args_hash, .{}); - var args_hex_hash: [Sha256.digest_length * 2]u8 = undefined; - _ = try std.fmt.bufPrint( - &args_hex_hash, - "{s}", - .{std.fmt.fmtSliceHexLower(&args_hash)}, - ); - - const args_file = try fs.path.join(builder.allocator, &[_][]const u8{ args_dir, args_hex_hash[0..] }); - try std.fs.cwd().writeFile(args_file, args); - - zig_args.shrinkRetainingCapacity(2); - try zig_args.append(try std.mem.concat(builder.allocator, u8, &[_][]const u8{ "@", args_file })); - } - - const output_dir_nl = try builder.execFromStep(zig_args.items, &self.step); - const build_output_dir = mem.trimRight(u8, output_dir_nl, "\r\n"); - - if (self.output_dir) |output_dir| { - var src_dir = try std.fs.cwd().openIterableDir(build_output_dir, .{}); - defer src_dir.close(); - - // Create the output directory if it doesn't exist. - try std.fs.cwd().makePath(output_dir); - - var dest_dir = try std.fs.cwd().openDir(output_dir, .{}); - defer dest_dir.close(); - - var it = src_dir.iterate(); - while (try it.next()) |entry| { - // The compiler can put these files into the same directory, but we don't - // want to copy them over. - if (mem.eql(u8, entry.name, "llvm-ar.id") or - mem.eql(u8, entry.name, "libs.txt") or - mem.eql(u8, entry.name, "builtin.zig") or - mem.eql(u8, entry.name, "zld.id") or - mem.eql(u8, entry.name, "lld.id")) continue; - - _ = try src_dir.dir.updateFile(entry.name, dest_dir, entry.name, .{}); - } - } else { - self.output_dir = build_output_dir; - } - - // This will ensure all output filenames will now have the output_dir available! - self.computeOutFileNames(); - - // Update generated files - if (self.output_dir != null) { - self.output_path_source.path = builder.pathJoin( - &.{ self.output_dir.?, self.out_filename }, - ); - - if (self.emit_h) { - self.output_h_path_source.path = builder.pathJoin( - &.{ self.output_dir.?, self.out_h_filename }, - ); - } - - if (self.target.isWindows() or self.target.isUefi()) { - self.output_pdb_path_source.path = builder.pathJoin( - &.{ self.output_dir.?, self.out_pdb_filename }, - ); - } - } - - if (self.kind == .lib and self.linkage != null and self.linkage.? == .dynamic and self.version != null and self.target.wantSharedLibSymLinks()) { - try doAtomicSymLinks(builder.allocator, self.getOutputSource().getPath(builder), self.major_only_filename.?, self.name_only_filename.?); - } -} - -fn isLibCLibrary(name: []const u8) bool { - const libc_libraries = [_][]const u8{ "c", "m", "dl", "rt", "pthread" }; - for (libc_libraries) |libc_lib_name| { - if (mem.eql(u8, name, libc_lib_name)) - return true; - } - return false; -} - -fn isLibCppLibrary(name: []const u8) bool { - const libcpp_libraries = [_][]const u8{ "c++", "stdc++" }; - for (libcpp_libraries) |libcpp_lib_name| { - if (mem.eql(u8, name, libcpp_lib_name)) - return true; - } - return false; -} - -/// Returned slice must be freed by the caller. -fn findVcpkgRoot(allocator: Allocator) !?[]const u8 { - const appdata_path = try fs.getAppDataDir(allocator, "vcpkg"); - defer allocator.free(appdata_path); - - const path_file = try fs.path.join(allocator, &[_][]const u8{ appdata_path, "vcpkg.path.txt" }); - defer allocator.free(path_file); - - const file = fs.cwd().openFile(path_file, .{}) catch return null; - defer file.close(); - - const size = @intCast(usize, try file.getEndPos()); - const vcpkg_path = try allocator.alloc(u8, size); - const size_read = try file.read(vcpkg_path); - std.debug.assert(size == size_read); - - return vcpkg_path; -} - -pub fn doAtomicSymLinks(allocator: Allocator, output_path: []const u8, filename_major_only: []const u8, filename_name_only: []const u8) !void { - const out_dir = fs.path.dirname(output_path) orelse "."; - const out_basename = fs.path.basename(output_path); - // sym link for libfoo.so.1 to libfoo.so.1.2.3 - const major_only_path = fs.path.join( - allocator, - &[_][]const u8{ out_dir, filename_major_only }, - ) catch unreachable; - fs.atomicSymLink(allocator, out_basename, major_only_path) catch |err| { - log.err("Unable to symlink {s} -> {s}", .{ major_only_path, out_basename }); - return err; - }; - // sym link for libfoo.so to libfoo.so.1 - const name_only_path = fs.path.join( - allocator, - &[_][]const u8{ out_dir, filename_name_only }, - ) catch unreachable; - fs.atomicSymLink(allocator, filename_major_only, name_only_path) catch |err| { - log.err("Unable to symlink {s} -> {s}", .{ name_only_path, filename_major_only }); - return err; - }; -} - -fn execPkgConfigList(self: *std.Build, out_code: *u8) (PkgConfigError || ExecError)![]const PkgConfigPkg { - const stdout = try self.execAllowFail(&[_][]const u8{ "pkg-config", "--list-all" }, out_code, .Ignore); - var list = ArrayList(PkgConfigPkg).init(self.allocator); - errdefer list.deinit(); - var line_it = mem.tokenize(u8, stdout, "\r\n"); - while (line_it.next()) |line| { - if (mem.trim(u8, line, " \t").len == 0) continue; - var tok_it = mem.tokenize(u8, line, " \t"); - try list.append(PkgConfigPkg{ - .name = tok_it.next() orelse return error.PkgConfigInvalidOutput, - .desc = tok_it.rest(), - }); - } - return list.toOwnedSlice(); -} - -fn getPkgConfigList(self: *std.Build) ![]const PkgConfigPkg { - if (self.pkg_config_pkg_list) |res| { - return res; - } - var code: u8 = undefined; - if (execPkgConfigList(self, &code)) |list| { - self.pkg_config_pkg_list = list; - return list; - } else |err| { - const result = switch (err) { - error.ProcessTerminated => error.PkgConfigCrashed, - error.ExecNotSupported => error.PkgConfigFailed, - error.ExitCodeFailure => error.PkgConfigFailed, - error.FileNotFound => error.PkgConfigNotInstalled, - error.InvalidName => error.PkgConfigNotInstalled, - error.PkgConfigInvalidOutput => error.PkgConfigInvalidOutput, - error.ChildExecFailed => error.PkgConfigFailed, - else => return err, - }; - self.pkg_config_pkg_list = result; - return result; - } -} - -test "addPackage" { - if (builtin.os.tag == .wasi) return error.SkipZigTest; - - var arena = std.heap.ArenaAllocator.init(std.testing.allocator); - defer arena.deinit(); - - const host = try NativeTargetInfo.detect(.{}); - - var builder = try std.Build.create( - arena.allocator(), - "test", - "test", - "test", - "test", - host, - ); - defer builder.destroy(); - - const pkg_dep = Pkg{ - .name = "pkg_dep", - .source = .{ .path = "/not/a/pkg_dep.zig" }, - }; - const pkg_top = Pkg{ - .name = "pkg_dep", - .source = .{ .path = "/not/a/pkg_top.zig" }, - .dependencies = &[_]Pkg{pkg_dep}, - }; - - var exe = builder.addExecutable("not_an_executable", "/not/an/executable.zig"); - exe.addPackage(pkg_top); - - try std.testing.expectEqual(@as(usize, 1), exe.packages.items.len); - - const dupe = exe.packages.items[0]; - try std.testing.expectEqualStrings(pkg_top.name, dupe.name); -} - -fn addFlag(args: *ArrayList([]const u8), comptime name: []const u8, opt: ?bool) !void { - const cond = opt orelse return; - try args.ensureUnusedCapacity(1); - if (cond) { - args.appendAssumeCapacity("-f" ++ name); - } else { - args.appendAssumeCapacity("-fno-" ++ name); - } -} - -const TransitiveDeps = struct { - link_objects: ArrayList(LinkObject), - seen_system_libs: StringHashMap(void), - seen_steps: std.AutoHashMap(*const Step, void), - is_linking_libcpp: bool, - is_linking_libc: bool, - frameworks: *StringHashMap(FrameworkLinkInfo), - - fn add(td: *TransitiveDeps, link_objects: []const LinkObject) !void { - try td.link_objects.ensureUnusedCapacity(link_objects.len); - - for (link_objects) |link_object| { - try td.link_objects.append(link_object); - switch (link_object) { - .other_step => |other| try addInner(td, other, other.isDynamicLibrary()), - else => {}, - } - } - } - - fn addInner(td: *TransitiveDeps, other: *LibExeObjStep, dyn: bool) !void { - // Inherit dependency on libc and libc++ - td.is_linking_libcpp = td.is_linking_libcpp or other.is_linking_libcpp; - td.is_linking_libc = td.is_linking_libc or other.is_linking_libc; - - // Inherit dependencies on darwin frameworks - if (!dyn) { - var it = other.frameworks.iterator(); - while (it.next()) |framework| { - try td.frameworks.put(framework.key_ptr.*, framework.value_ptr.*); - } - } - - // Inherit dependencies on system libraries and static libraries. - for (other.link_objects.items) |other_link_object| { - switch (other_link_object) { - .system_lib => |system_lib| { - if ((try td.seen_system_libs.fetchPut(system_lib.name, {})) != null) - continue; - - if (dyn) - continue; - - try td.link_objects.append(other_link_object); - }, - .other_step => |inner_other| { - if ((try td.seen_steps.fetchPut(&inner_other.step, {})) != null) - continue; - - if (!dyn) - try td.link_objects.append(other_link_object); - - try addInner(td, inner_other, dyn or inner_other.isDynamicLibrary()); - }, - else => continue, - } - } - } -}; diff --git a/lib/std/Build/OptionsStep.zig b/lib/std/Build/OptionsStep.zig index 8e1a7ef2fc..94c41ce9e6 100644 --- a/lib/std/Build/OptionsStep.zig +++ b/lib/std/Build/OptionsStep.zig @@ -3,7 +3,7 @@ const builtin = @import("builtin"); const fs = std.fs; const Step = std.Build.Step; const GeneratedFile = std.Build.GeneratedFile; -const LibExeObjStep = std.Build.LibExeObjStep; +const CompileStep = std.Build.CompileStep; const FileSource = std.Build.FileSource; const OptionsStep = @This(); @@ -195,7 +195,7 @@ pub fn addOptionFileSource( /// The value is the path in the cache dir. /// Adds a dependency automatically. -pub fn addOptionArtifact(self: *OptionsStep, name: []const u8, artifact: *LibExeObjStep) void { +pub fn addOptionArtifact(self: *OptionsStep, name: []const u8, artifact: *CompileStep) void { self.artifact_args.append(.{ .name = self.builder.dupe(name), .artifact = artifact }) catch unreachable; self.step.dependOn(&artifact.step); } @@ -266,7 +266,7 @@ fn hashContentsToFileName(self: *OptionsStep) [64]u8 { const OptionArtifactArg = struct { name: []const u8, - artifact: *LibExeObjStep, + artifact: *CompileStep, }; const OptionFileSourceArg = struct { diff --git a/lib/std/Build/RunStep.zig b/lib/std/Build/RunStep.zig index 83e4f7f66c..9ab9ea6335 100644 --- a/lib/std/Build/RunStep.zig +++ b/lib/std/Build/RunStep.zig @@ -1,7 +1,7 @@ const std = @import("../std.zig"); const builtin = @import("builtin"); const Step = std.Build.Step; -const LibExeObjStep = std.Build.LibExeObjStep; +const CompileStep = std.Build.CompileStep; const WriteFileStep = std.Build.WriteFileStep; const fs = std.fs; const mem = std.mem; @@ -48,7 +48,7 @@ pub const StdIoAction = union(enum) { }; pub const Arg = union(enum) { - artifact: *LibExeObjStep, + artifact: *CompileStep, file_source: std.Build.FileSource, bytes: []u8, }; @@ -66,7 +66,7 @@ pub fn create(builder: *std.Build, name: []const u8) *RunStep { return self; } -pub fn addArtifactArg(self: *RunStep, artifact: *LibExeObjStep) void { +pub fn addArtifactArg(self: *RunStep, artifact: *CompileStep) void { self.argv.append(Arg{ .artifact = artifact }) catch unreachable; self.step.dependOn(&artifact.step); } @@ -355,13 +355,13 @@ fn printCmd(cwd: ?[]const u8, argv: []const []const u8) void { std.debug.print("\n", .{}); } -fn addPathForDynLibs(self: *RunStep, artifact: *LibExeObjStep) void { +fn addPathForDynLibs(self: *RunStep, artifact: *CompileStep) void { addPathForDynLibsInternal(&self.step, self.builder, artifact); } /// This should only be used for internal usage, this is called automatically /// for the user. -pub fn addPathForDynLibsInternal(step: *Step, builder: *std.Build, artifact: *LibExeObjStep) void { +pub fn addPathForDynLibsInternal(step: *Step, builder: *std.Build, artifact: *CompileStep) void { for (artifact.link_objects.items) |link_object| { switch (link_object) { .other_step => |other| { diff --git a/lib/std/Build/Step.zig b/lib/std/Build/Step.zig index 86d6645c29..aff8a49161 100644 --- a/lib/std/Build/Step.zig +++ b/lib/std/Build/Step.zig @@ -7,7 +7,7 @@ done_flag: bool, pub const Id = enum { top_level, - lib_exe_obj, + compile, install_artifact, install_file, install_dir, @@ -28,7 +28,7 @@ pub const Id = enum { pub fn Type(comptime id: Id) type { return switch (id) { .top_level => Build.TopLevelStep, - .lib_exe_obj => Build.LibExeObjStep, + .compile => Build.CompileStep, .install_artifact => Build.InstallArtifactStep, .install_file => Build.InstallFileStep, .install_dir => Build.InstallDirStep, diff --git a/lib/std/Build/TranslateCStep.zig b/lib/std/Build/TranslateCStep.zig index 5404747846..c59362aeee 100644 --- a/lib/std/Build/TranslateCStep.zig +++ b/lib/std/Build/TranslateCStep.zig @@ -1,6 +1,6 @@ const std = @import("../std.zig"); const Step = std.Build.Step; -const LibExeObjStep = std.Build.LibExeObjStep; +const CompileStep = std.Build.CompileStep; const CheckFileStep = std.Build.CheckFileStep; const fs = std.fs; const mem = std.mem; @@ -51,11 +51,11 @@ pub const AddExecutableOptions = struct { version: ?std.builtin.Version = null, target: ?CrossTarget = null, optimize: ?std.builtin.Mode = null, - linkage: ?LibExeObjStep.Linkage = null, + linkage: ?CompileStep.Linkage = null, }; /// Creates a step to build an executable from the translated source. -pub fn addExecutable(self: *TranslateCStep, options: AddExecutableOptions) *LibExeObjStep { +pub fn addExecutable(self: *TranslateCStep, options: AddExecutableOptions) *CompileStep { return self.builder.addExecutable(.{ .root_source_file = .{ .generated = &self.output_file }, .name = options.name orelse "translated_c", diff --git a/lib/std/std.zig b/lib/std/std.zig index e0318ceb43..7440b49662 100644 --- a/lib/std/std.zig +++ b/lib/std/std.zig @@ -97,8 +97,10 @@ pub const zig = @import("zig.zig"); pub const start = @import("start.zig"); ///// Deprecated. Use `std.Build` instead. +///// TODO: remove this after releasing 0.11.0 //pub const build = struct { // /// Deprecated. Use `std.Build` instead. +// /// TODO: remove this after releasing 0.11.0 // pub const Builder = Build; //}; diff --git a/test/link/macho/bugs/13457/build.zig b/test/link/macho/bugs/13457/build.zig index 6ca1f31b86..3560b4a168 100644 --- a/test/link/macho/bugs/13457/build.zig +++ b/test/link/macho/bugs/13457/build.zig @@ -1,5 +1,4 @@ const std = @import("std"); -const LibExeObjectStep = std.Build.LibExeObjStep; pub fn build(b: *std.Build) void { const optimize = b.standardOptimizeOption(.{}); diff --git a/test/link/macho/dead_strip/build.zig b/test/link/macho/dead_strip/build.zig index b6c3002492..d82c81edca 100644 --- a/test/link/macho/dead_strip/build.zig +++ b/test/link/macho/dead_strip/build.zig @@ -1,5 +1,4 @@ const std = @import("std"); -const LibExeObjectStep = std.Build.LibExeObjStep; pub fn build(b: *std.Build) void { const optimize = b.standardOptimizeOption(.{}); @@ -36,7 +35,11 @@ pub fn build(b: *std.Build) void { } } -fn createScenario(b: *std.Build, optimize: std.builtin.OptimizeMode, target: std.zig.CrossTarget) *LibExeObjectStep { +fn createScenario( + b: *std.Build, + optimize: std.builtin.OptimizeMode, + target: std.zig.CrossTarget, +) *std.Build.CompileStep { const exe = b.addExecutable(.{ .name = "test", .optimize = optimize, diff --git a/test/link/macho/dead_strip_dylibs/build.zig b/test/link/macho/dead_strip_dylibs/build.zig index f61b30ca4a..8b62cec6e6 100644 --- a/test/link/macho/dead_strip_dylibs/build.zig +++ b/test/link/macho/dead_strip_dylibs/build.zig @@ -1,5 +1,4 @@ const std = @import("std"); -const LibExeObjectStep = std.Build.LibExeObjStep; pub fn build(b: *std.Build) void { const optimize = b.standardOptimizeOption(.{}); @@ -35,7 +34,7 @@ pub fn build(b: *std.Build) void { } } -fn createScenario(b: *std.Build, optimize: std.builtin.OptimizeMode) *LibExeObjectStep { +fn createScenario(b: *std.Build, optimize: std.builtin.OptimizeMode) *std.Build.CompileStep { const exe = b.addExecutable(.{ .name = "test", .optimize = optimize, diff --git a/test/link/macho/headerpad/build.zig b/test/link/macho/headerpad/build.zig index 2b3c6abb8a..3ef17573f8 100644 --- a/test/link/macho/headerpad/build.zig +++ b/test/link/macho/headerpad/build.zig @@ -1,6 +1,5 @@ const std = @import("std"); const builtin = @import("builtin"); -const LibExeObjectStep = std.Build.LibExeObjStep; pub fn build(b: *std.Build) void { const optimize = b.standardOptimizeOption(.{}); @@ -93,7 +92,7 @@ pub fn build(b: *std.Build) void { } } -fn simpleExe(b: *std.Build, optimize: std.builtin.OptimizeMode) *LibExeObjectStep { +fn simpleExe(b: *std.Build, optimize: std.builtin.OptimizeMode) *std.Build.CompileStep { const exe = b.addExecutable(.{ .name = "main", .optimize = optimize, diff --git a/test/link/macho/needed_framework/build.zig b/test/link/macho/needed_framework/build.zig index 62b70b21f1..8b6e3dd87f 100644 --- a/test/link/macho/needed_framework/build.zig +++ b/test/link/macho/needed_framework/build.zig @@ -1,5 +1,4 @@ const std = @import("std"); -const LibExeObjectStep = std.Build.LibExeObjStep; pub fn build(b: *std.Build) void { const optimize = b.standardOptimizeOption(.{}); diff --git a/test/link/macho/needed_library/build.zig b/test/link/macho/needed_library/build.zig index cdad94357b..92a73d22b7 100644 --- a/test/link/macho/needed_library/build.zig +++ b/test/link/macho/needed_library/build.zig @@ -1,5 +1,4 @@ const std = @import("std"); -const LibExeObjectStep = std.Build.LibExeObjStep; pub fn build(b: *std.Build) void { const optimize = b.standardOptimizeOption(.{}); diff --git a/test/link/macho/search_strategy/build.zig b/test/link/macho/search_strategy/build.zig index eeda89446b..62757f885b 100644 --- a/test/link/macho/search_strategy/build.zig +++ b/test/link/macho/search_strategy/build.zig @@ -1,5 +1,4 @@ const std = @import("std"); -const LibExeObjectStep = std.Build.LibExeObjStep; pub fn build(b: *std.Build) void { const optimize = b.standardOptimizeOption(.{}); @@ -35,7 +34,11 @@ pub fn build(b: *std.Build) void { } } -fn createScenario(b: *std.Build, optimize: std.builtin.OptimizeMode, target: std.zig.CrossTarget) *LibExeObjectStep { +fn createScenario( + b: *std.Build, + optimize: std.builtin.OptimizeMode, + target: std.zig.CrossTarget, +) *std.Build.CompileStep { const static = b.addStaticLibrary(.{ .name = "a", .optimize = optimize, diff --git a/test/link/macho/strict_validation/build.zig b/test/link/macho/strict_validation/build.zig index 6eabc02b5f..408076657b 100644 --- a/test/link/macho/strict_validation/build.zig +++ b/test/link/macho/strict_validation/build.zig @@ -1,6 +1,5 @@ const std = @import("std"); const builtin = @import("builtin"); -const LibExeObjectStep = std.Build.LibExeObjStep; pub fn build(b: *std.Build) void { const optimize = b.standardOptimizeOption(.{}); diff --git a/test/link/macho/unwind_info/build.zig b/test/link/macho/unwind_info/build.zig index e43c002e2e..408f762f5d 100644 --- a/test/link/macho/unwind_info/build.zig +++ b/test/link/macho/unwind_info/build.zig @@ -1,6 +1,5 @@ const std = @import("std"); const builtin = @import("builtin"); -const LibExeObjectStep = std.Build.LibExeObjStep; pub fn build(b: *std.Build) void { const optimize = b.standardOptimizeOption(.{}); @@ -51,7 +50,11 @@ fn testUnwindInfo( test_step.dependOn(&run_cmd.step); } -fn createScenario(b: *std.Build, optimize: std.builtin.OptimizeMode, target: std.zig.CrossTarget) *LibExeObjectStep { +fn createScenario( + b: *std.Build, + optimize: std.builtin.OptimizeMode, + target: std.zig.CrossTarget, +) *std.Build.CompileStep { const exe = b.addExecutable(.{ .name = "test", .optimize = optimize, diff --git a/test/link/macho/uuid/build.zig b/test/link/macho/uuid/build.zig index 62c288f1a0..6a68263fbf 100644 --- a/test/link/macho/uuid/build.zig +++ b/test/link/macho/uuid/build.zig @@ -1,5 +1,4 @@ const std = @import("std"); -const LibExeObjectStep = std.Build.LibExeObjStep; pub fn build(b: *std.Build) void { const test_step = b.step("test", "Test"); @@ -51,7 +50,11 @@ fn testUuid( } } -fn simpleDylib(b: *std.Build, optimize: std.builtin.OptimizeMode, target: std.zig.CrossTarget) *LibExeObjectStep { +fn simpleDylib( + b: *std.Build, + optimize: std.builtin.OptimizeMode, + target: std.zig.CrossTarget, +) *std.Build.CompileStep { const dylib = b.addSharedLibrary(.{ .name = "test", .version = .{ .major = 1, .minor = 0 }, diff --git a/test/link/macho/weak_framework/build.zig b/test/link/macho/weak_framework/build.zig index 5be66991dd..ca28458d77 100644 --- a/test/link/macho/weak_framework/build.zig +++ b/test/link/macho/weak_framework/build.zig @@ -1,5 +1,4 @@ const std = @import("std"); -const LibExeObjectStep = std.Build.LibExeObjStep; pub fn build(b: *std.Build) void { const optimize = b.standardOptimizeOption(.{}); diff --git a/test/link/macho/weak_library/build.zig b/test/link/macho/weak_library/build.zig index 505ab5ae96..de5aa45e30 100644 --- a/test/link/macho/weak_library/build.zig +++ b/test/link/macho/weak_library/build.zig @@ -1,5 +1,4 @@ const std = @import("std"); -const LibExeObjectStep = std.Build.LibExeObjStep; pub fn build(b: *std.Build) void { const optimize = b.standardOptimizeOption(.{}); diff --git a/test/tests.zig b/test/tests.zig index de25528dde..0f36c8eb94 100644 --- a/test/tests.zig +++ b/test/tests.zig @@ -8,7 +8,7 @@ const mem = std.mem; const fmt = std.fmt; const ArrayList = std.ArrayList; const OptimizeMode = std.builtin.OptimizeMode; -const LibExeObjStep = std.Build.LibExeObjStep; +const CompileStep = std.Build.CompileStep; const Allocator = mem.Allocator; const ExecError = std.Build.ExecError; const Step = std.Build.Step; @@ -842,7 +842,7 @@ pub const StackTracesContext = struct { step: Step, context: *StackTracesContext, - exe: *LibExeObjStep, + exe: *CompileStep, name: []const u8, optimize_mode: OptimizeMode, expect_output: []const u8, @@ -850,7 +850,7 @@ pub const StackTracesContext = struct { pub fn create( context: *StackTracesContext, - exe: *LibExeObjStep, + exe: *CompileStep, name: []const u8, optimize_mode: OptimizeMode, expect_output: []const u8, @@ -1180,14 +1180,14 @@ pub const GenHContext = struct { const GenHCmpOutputStep = struct { step: Step, context: *GenHContext, - obj: *LibExeObjStep, + obj: *CompileStep, name: []const u8, test_index: usize, case: *const TestCase, pub fn create( context: *GenHContext, - obj: *LibExeObjStep, + obj: *CompileStep, name: []const u8, case: *const TestCase, ) *GenHCmpOutputStep { -- cgit v1.2.3 From 90e48d4b3469fb4f8dd2f3b52e05453029d45fdc Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 31 Jan 2023 14:02:32 -0700 Subject: std.Build: avoid use of catch unreachable Usage of `catch unreachable` in build scripts is completely harmless because build scripts are always run in Debug mode, however, it sets a poor example for beginners to learn from. --- lib/std/Build.zig | 69 ++++++++++--------- lib/std/Build/CheckFileStep.zig | 2 +- lib/std/Build/CheckObjectStep.zig | 12 ++-- lib/std/Build/CompileStep.zig | 121 ++++++++++++++++++---------------- lib/std/Build/EmulatableRunStep.zig | 8 +-- lib/std/Build/FmtStep.zig | 4 +- lib/std/Build/InstallArtifactStep.zig | 2 +- lib/std/Build/InstallRawStep.zig | 4 +- lib/std/Build/OptionsStep.zig | 62 +++++++++-------- lib/std/Build/RunStep.zig | 24 +++---- lib/std/Build/Step.zig | 4 +- lib/std/Build/TranslateCStep.zig | 12 ++-- lib/std/Build/WriteFileStep.zig | 6 +- 13 files changed, 169 insertions(+), 161 deletions(-) (limited to 'lib/std/Build/InstallRawStep.zig') diff --git a/lib/std/Build.zig b/lib/std/Build.zig index 008ae5f4d7..d695637fc3 100644 --- a/lib/std/Build.zig +++ b/lib/std/Build.zig @@ -564,12 +564,12 @@ pub fn addConfigHeader( /// Allocator.dupe without the need to handle out of memory. pub fn dupe(self: *Build, bytes: []const u8) []u8 { - return self.allocator.dupe(u8, bytes) catch unreachable; + return self.allocator.dupe(u8, bytes) catch @panic("OOM"); } /// Duplicates an array of strings without the need to handle out of memory. pub fn dupeStrings(self: *Build, strings: []const []const u8) [][]u8 { - const array = self.allocator.alloc([]u8, strings.len) catch unreachable; + const array = self.allocator.alloc([]u8, strings.len) catch @panic("OOM"); for (strings) |s, i| { array[i] = self.dupe(s); } @@ -596,7 +596,7 @@ pub fn dupePkg(self: *Build, package: Pkg) Pkg { }; if (package.dependencies) |dependencies| { - const new_dependencies = self.allocator.alloc(Pkg, dependencies.len) catch unreachable; + const new_dependencies = self.allocator.alloc(Pkg, dependencies.len) catch @panic("OOM"); the_copy.dependencies = new_dependencies; for (dependencies) |dep_package, i| { @@ -613,20 +613,20 @@ pub fn addWriteFile(self: *Build, file_path: []const u8, data: []const u8) *Writ } pub fn addWriteFiles(self: *Build) *WriteFileStep { - const write_file_step = self.allocator.create(WriteFileStep) catch unreachable; + const write_file_step = self.allocator.create(WriteFileStep) catch @panic("OOM"); write_file_step.* = WriteFileStep.init(self); return write_file_step; } pub fn addLog(self: *Build, comptime format: []const u8, args: anytype) *LogStep { const data = self.fmt(format, args); - const log_step = self.allocator.create(LogStep) catch unreachable; + const log_step = self.allocator.create(LogStep) catch @panic("OOM"); log_step.* = LogStep.init(self, data); return log_step; } pub fn addRemoveDirTree(self: *Build, dir_path: []const u8) *RemoveDirStep { - const remove_dir_step = self.allocator.create(RemoveDirStep) catch unreachable; + const remove_dir_step = self.allocator.create(RemoveDirStep) catch @panic("OOM"); remove_dir_step.* = RemoveDirStep.init(self, dir_path); return remove_dir_step; } @@ -719,13 +719,13 @@ pub fn option(self: *Build, comptime T: type, name_raw: []const u8, description_ const type_id = comptime typeToEnum(T); const enum_options = if (type_id == .@"enum") blk: { const fields = comptime std.meta.fields(T); - var options = ArrayList([]const u8).initCapacity(self.allocator, fields.len) catch unreachable; + var options = ArrayList([]const u8).initCapacity(self.allocator, fields.len) catch @panic("OOM"); inline for (fields) |field| { options.appendAssumeCapacity(field.name); } - break :blk options.toOwnedSlice() catch unreachable; + break :blk options.toOwnedSlice() catch @panic("OOM"); } else null; const available_option = AvailableOption{ .name = name, @@ -733,10 +733,10 @@ pub fn option(self: *Build, comptime T: type, name_raw: []const u8, description_ .description = description, .enum_options = enum_options, }; - if ((self.available_options_map.fetchPut(name, available_option) catch unreachable) != null) { + if ((self.available_options_map.fetchPut(name, available_option) catch @panic("OOM")) != null) { panic("Option '{s}' declared twice", .{name}); } - self.available_options_list.append(available_option) catch unreachable; + self.available_options_list.append(available_option) catch @panic("OOM"); const option_ptr = self.user_input_options.getPtr(name) orelse return null; option_ptr.used = true; @@ -840,7 +840,7 @@ pub fn option(self: *Build, comptime T: type, name_raw: []const u8, description_ return null; }, .scalar => |s| { - return self.allocator.dupe([]const u8, &[_][]const u8{s}) catch unreachable; + return self.allocator.dupe([]const u8, &[_][]const u8{s}) catch @panic("OOM"); }, .list => |lst| return lst.items, }, @@ -848,12 +848,12 @@ pub fn option(self: *Build, comptime T: type, name_raw: []const u8, description_ } pub fn step(self: *Build, name: []const u8, description: []const u8) *Step { - const step_info = self.allocator.create(TopLevelStep) catch unreachable; + const step_info = self.allocator.create(TopLevelStep) catch @panic("OOM"); step_info.* = TopLevelStep{ .step = Step.initNoOp(.top_level, name, self.allocator), .description = self.dupe(description), }; - self.top_level_steps.append(step_info) catch unreachable; + self.top_level_steps.append(step_info) catch @panic("OOM"); return &step_info.step; } @@ -949,7 +949,7 @@ pub fn standardTargetOptions(self: *Build, args: StandardTargetOptionsArgs) Cros }, }; - const selected_canonicalized_triple = selected_target.zigTriple(self.allocator) catch unreachable; + const selected_canonicalized_triple = selected_target.zigTriple(self.allocator) catch @panic("OOM"); if (args.whitelist) |list| whitelist_check: { // Make sure it's a match of one of the list. @@ -960,7 +960,7 @@ pub fn standardTargetOptions(self: *Build, args: StandardTargetOptionsArgs) Cros mismatch_cpu_features = true; mismatch_triple = true; - const t_triple = t.zigTriple(self.allocator) catch unreachable; + const t_triple = t.zigTriple(self.allocator) catch @panic("OOM"); if (mem.eql(u8, t_triple, selected_canonicalized_triple)) { mismatch_triple = false; whitelist_item = t; @@ -977,7 +977,7 @@ pub fn standardTargetOptions(self: *Build, args: StandardTargetOptionsArgs) Cros selected_canonicalized_triple, }); for (list) |t| { - const t_triple = t.zigTriple(self.allocator) catch unreachable; + const t_triple = t.zigTriple(self.allocator) catch @panic("OOM"); log.err(" {s}", .{t_triple}); } } else { @@ -1033,22 +1033,22 @@ pub fn addUserInputOption(self: *Build, name_raw: []const u8, value_raw: []const .scalar => |s| { // turn it into a list var list = ArrayList([]const u8).init(self.allocator); - list.append(s) catch unreachable; - list.append(value) catch unreachable; - self.user_input_options.put(name, .{ + try list.append(s); + try list.append(value); + try self.user_input_options.put(name, .{ .name = name, .value = .{ .list = list }, .used = false, - }) catch unreachable; + }); }, .list => |*list| { // append to the list - list.append(value) catch unreachable; - self.user_input_options.put(name, .{ + try list.append(value); + try self.user_input_options.put(name, .{ .name = name, .value = .{ .list = list.* }, .used = false, - }) catch unreachable; + }); }, .flag => { log.warn("Option '-D{s}={s}' conflicts with flag '-D{s}'.", .{ name, value, name }); @@ -1240,13 +1240,13 @@ pub fn addInstallFileWithDir( if (dest_rel_path.len == 0) { panic("dest_rel_path must be non-empty", .{}); } - const install_step = self.allocator.create(InstallFileStep) catch unreachable; + const install_step = self.allocator.create(InstallFileStep) catch @panic("OOM"); install_step.* = InstallFileStep.init(self, source.dupe(self), install_dir, dest_rel_path); return install_step; } pub fn addInstallDirectory(self: *Build, options: InstallDirectoryOptions) *InstallDirStep { - const install_step = self.allocator.create(InstallDirStep) catch unreachable; + const install_step = self.allocator.create(InstallDirStep) catch @panic("OOM"); install_step.* = InstallDirStep.init(self, options); return install_step; } @@ -1256,7 +1256,7 @@ pub fn pushInstalledFile(self: *Build, dir: InstallDir, dest_rel_path: []const u .dir = dir, .path = dest_rel_path, }; - self.installed_files.append(file.dupe(self)) catch unreachable; + self.installed_files.append(file.dupe(self)) catch @panic("OOM"); } pub fn updateFile(self: *Build, source_path: []const u8, dest_path: []const u8) !void { @@ -1289,16 +1289,15 @@ pub fn truncateFile(self: *Build, dest_path: []const u8) !void { } pub fn pathFromRoot(self: *Build, rel_path: []const u8) []u8 { - return fs.path.resolve(self.allocator, &[_][]const u8{ self.build_root, rel_path }) catch unreachable; + return fs.path.resolve(self.allocator, &[_][]const u8{ self.build_root, rel_path }) catch @panic("OOM"); } -/// Shorthand for `std.fs.path.join(Build.allocator, paths) catch unreachable` pub fn pathJoin(self: *Build, paths: []const []const u8) []u8 { - return fs.path.join(self.allocator, paths) catch unreachable; + return fs.path.join(self.allocator, paths) catch @panic("OOM"); } pub fn fmt(self: *Build, comptime format: []const u8, args: anytype) []u8 { - return fmt_lib.allocPrint(self.allocator, format, args) catch unreachable; + return fmt_lib.allocPrint(self.allocator, format, args) catch @panic("OOM"); } pub fn findProgram(self: *Build, names: []const []const u8, paths: []const []const u8) ![]const u8 { @@ -1442,7 +1441,7 @@ pub fn exec(self: *Build, argv: []const []const u8) ![]u8 { } pub fn addSearchPrefix(self: *Build, search_prefix: []const u8) void { - self.search_prefixes.append(self.dupePath(search_prefix)) catch unreachable; + self.search_prefixes.append(self.dupePath(search_prefix)) catch @panic("OOM"); } pub fn getInstallPath(self: *Build, dir: InstallDir, dest_rel_path: []const u8) []const u8 { @@ -1457,7 +1456,7 @@ pub fn getInstallPath(self: *Build, dir: InstallDir, dest_rel_path: []const u8) return fs.path.resolve( self.allocator, &[_][]const u8{ base_dir, dest_rel_path }, - ) catch unreachable; + ) catch @panic("OOM"); } pub const Dependency = struct { @@ -1509,14 +1508,14 @@ fn dependencyInner( comptime build_zig: type, args: anytype, ) *Dependency { - const sub_builder = b.createChild(name, build_root, args) catch unreachable; - sub_builder.runBuild(build_zig) catch unreachable; + const sub_builder = b.createChild(name, build_root, args) catch @panic("unhandled error"); + sub_builder.runBuild(build_zig) catch @panic("unhandled error"); if (sub_builder.validateUserInputDidItFail()) { std.debug.dumpCurrentStackTrace(@returnAddress()); } - const dep = b.allocator.create(Dependency) catch unreachable; + const dep = b.allocator.create(Dependency) catch @panic("OOM"); dep.* = .{ .builder = sub_builder }; return dep; } diff --git a/lib/std/Build/CheckFileStep.zig b/lib/std/Build/CheckFileStep.zig index 3b8cfe5263..b08a797e84 100644 --- a/lib/std/Build/CheckFileStep.zig +++ b/lib/std/Build/CheckFileStep.zig @@ -18,7 +18,7 @@ pub fn create( source: std.Build.FileSource, expected_matches: []const []const u8, ) *CheckFileStep { - const self = builder.allocator.create(CheckFileStep) catch unreachable; + const self = builder.allocator.create(CheckFileStep) catch @panic("OOM"); self.* = CheckFileStep{ .builder = builder, .step = Step.init(.check_file, "CheckFile", builder.allocator, make), diff --git a/lib/std/Build/CheckObjectStep.zig b/lib/std/Build/CheckObjectStep.zig index bfa5338927..5cb096581f 100644 --- a/lib/std/Build/CheckObjectStep.zig +++ b/lib/std/Build/CheckObjectStep.zig @@ -24,7 +24,7 @@ obj_format: std.Target.ObjectFormat, pub fn create(builder: *std.Build, source: std.Build.FileSource, obj_format: std.Target.ObjectFormat) *CheckObjectStep { const gpa = builder.allocator; - const self = gpa.create(CheckObjectStep) catch unreachable; + const self = gpa.create(CheckObjectStep) catch @panic("OOM"); self.* = .{ .builder = builder, .step = Step.init(.check_file, "CheckObject", gpa, make), @@ -228,14 +228,14 @@ const Check = struct { self.actions.append(.{ .tag = .match, .phrase = self.builder.dupe(phrase), - }) catch unreachable; + }) catch @panic("OOM"); } fn notPresent(self: *Check, phrase: []const u8) void { self.actions.append(.{ .tag = .not_present, .phrase = self.builder.dupe(phrase), - }) catch unreachable; + }) catch @panic("OOM"); } fn computeCmp(self: *Check, phrase: []const u8, expected: ComputeCompareExpected) void { @@ -243,7 +243,7 @@ const Check = struct { .tag = .compute_cmp, .phrase = self.builder.dupe(phrase), .expected = expected, - }) catch unreachable; + }) catch @panic("OOM"); } }; @@ -251,7 +251,7 @@ const Check = struct { pub fn checkStart(self: *CheckObjectStep, phrase: []const u8) void { var new_check = Check.create(self.builder); new_check.match(phrase); - self.checks.append(new_check) catch unreachable; + self.checks.append(new_check) catch @panic("OOM"); } /// Adds another searched phrase to the latest created Check with `CheckObjectStep.checkStart(...)`. @@ -293,7 +293,7 @@ pub fn checkComputeCompare( ) void { var new_check = Check.create(self.builder); new_check.computeCmp(program, expected); - self.checks.append(new_check) catch unreachable; + self.checks.append(new_check) catch @panic("OOM"); } fn make(step: *Step) !void { diff --git a/lib/std/Build/CompileStep.zig b/lib/std/Build/CompileStep.zig index 5257c85a5c..7396a5ce66 100644 --- a/lib/std/Build/CompileStep.zig +++ b/lib/std/Build/CompileStep.zig @@ -312,7 +312,7 @@ pub fn create(builder: *std.Build, options: Options) *CompileStep { panic("invalid name: '{s}'. It looks like a file path, but it is supposed to be the library or application name.", .{name}); } - const self = builder.allocator.create(CompileStep) catch unreachable; + const self = builder.allocator.create(CompileStep) catch @panic("OOM"); self.* = CompileStep{ .strip = null, .unwind_tables = null, @@ -364,7 +364,7 @@ pub fn create(builder: *std.Build, options: Options) *CompileStep { .output_h_path_source = GeneratedFile{ .step = &self.step }, .output_pdb_path_source = GeneratedFile{ .step = &self.step }, - .target_info = NativeTargetInfo.detect(self.target) catch unreachable, + .target_info = NativeTargetInfo.detect(self.target) catch @panic("unhandled error"), }; self.computeOutFileNames(); if (root_src) |rs| rs.addStepDependencies(&self.step); @@ -387,7 +387,7 @@ fn computeOutFileNames(self: *CompileStep) void { .static => .Static, }) else null, .version = self.version, - }) catch unreachable; + }) catch @panic("OOM"); if (self.kind == .lib) { if (self.linkage != null and self.linkage.? == .static) { @@ -439,7 +439,7 @@ pub fn installRaw(self: *CompileStep, dest_filename: []const u8, options: Instal pub fn installHeader(a: *CompileStep, src_path: []const u8, dest_rel_path: []const u8) void { const install_file = a.builder.addInstallHeaderFile(src_path, dest_rel_path); a.builder.getInstallStep().dependOn(&install_file.step); - a.installed_headers.append(&install_file.step) catch unreachable; + a.installed_headers.append(&install_file.step) catch @panic("OOM"); } pub fn installHeadersDirectory( @@ -460,7 +460,7 @@ pub fn installHeadersDirectoryOptions( ) void { const install_dir = a.builder.addInstallDirectory(options); a.builder.getInstallStep().dependOn(&install_dir.step); - a.installed_headers.append(&install_dir.step) catch unreachable; + a.installed_headers.append(&install_dir.step) catch @panic("OOM"); } pub fn installLibraryHeaders(a: *CompileStep, l: *CompileStep) void { @@ -472,7 +472,7 @@ pub fn installLibraryHeaders(a: *CompileStep, l: *CompileStep) void { const step_copy = switch (step.id) { inline .install_file, .install_dir => |id| blk: { const T = id.Type(); - const ptr = a.builder.allocator.create(T) catch unreachable; + const ptr = a.builder.allocator.create(T) catch @panic("OOM"); ptr.* = step.cast(T).?.*; ptr.override_source_builder = ptr.builder; ptr.builder = a.builder; @@ -480,10 +480,10 @@ pub fn installLibraryHeaders(a: *CompileStep, l: *CompileStep) void { }, else => unreachable, }; - a.installed_headers.append(step_copy) catch unreachable; + a.installed_headers.append(step_copy) catch @panic("OOM"); install_step.dependOn(step_copy); } - a.installed_headers.appendSlice(l.installed_headers.items) catch unreachable; + a.installed_headers.appendSlice(l.installed_headers.items) catch @panic("OOM"); } /// Creates a `RunStep` with an executable built with `addExecutable`. @@ -532,19 +532,19 @@ pub fn setLinkerScriptPath(self: *CompileStep, source: FileSource) void { } pub fn linkFramework(self: *CompileStep, framework_name: []const u8) void { - self.frameworks.put(self.builder.dupe(framework_name), .{}) catch unreachable; + self.frameworks.put(self.builder.dupe(framework_name), .{}) catch @panic("OOM"); } pub fn linkFrameworkNeeded(self: *CompileStep, framework_name: []const u8) void { self.frameworks.put(self.builder.dupe(framework_name), .{ .needed = true, - }) catch unreachable; + }) catch @panic("OOM"); } pub fn linkFrameworkWeak(self: *CompileStep, framework_name: []const u8) void { self.frameworks.put(self.builder.dupe(framework_name), .{ .weak = true, - }) catch unreachable; + }) catch @panic("OOM"); } /// Returns whether the library, executable, or object depends on a particular system library. @@ -596,12 +596,12 @@ pub fn linkLibCpp(self: *CompileStep) void { /// `name` and `value` need not live longer than the function call. pub fn defineCMacro(self: *CompileStep, name: []const u8, value: ?[]const u8) void { const macro = std.Build.constructCMacro(self.builder.allocator, name, value); - self.c_macros.append(macro) catch unreachable; + self.c_macros.append(macro) catch @panic("OOM"); } /// name_and_value looks like [name]=[value]. If the value is omitted, it is set to 1. pub fn defineCMacroRaw(self: *CompileStep, name_and_value: []const u8) void { - self.c_macros.append(self.builder.dupe(name_and_value)) catch unreachable; + self.c_macros.append(self.builder.dupe(name_and_value)) catch @panic("OOM"); } /// This one has no integration with anything, it just puts -lname on the command line. @@ -614,7 +614,7 @@ pub fn linkSystemLibraryName(self: *CompileStep, name: []const u8) void { .weak = false, .use_pkg_config = .no, }, - }) catch unreachable; + }) catch @panic("OOM"); } /// This one has no integration with anything, it just puts -needed-lname on the command line. @@ -627,7 +627,7 @@ pub fn linkSystemLibraryNeededName(self: *CompileStep, name: []const u8) void { .weak = false, .use_pkg_config = .no, }, - }) catch unreachable; + }) catch @panic("OOM"); } /// Darwin-only. This one has no integration with anything, it just puts -weak-lname on the @@ -640,7 +640,7 @@ pub fn linkSystemLibraryWeakName(self: *CompileStep, name: []const u8) void { .weak = true, .use_pkg_config = .no, }, - }) catch unreachable; + }) catch @panic("OOM"); } /// This links against a system library, exclusively using pkg-config to find the library. @@ -653,7 +653,7 @@ pub fn linkSystemLibraryPkgConfigOnly(self: *CompileStep, lib_name: []const u8) .weak = false, .use_pkg_config = .force, }, - }) catch unreachable; + }) catch @panic("OOM"); } /// This links against a system library, exclusively using pkg-config to find the library. @@ -666,7 +666,7 @@ pub fn linkSystemLibraryNeededPkgConfigOnly(self: *CompileStep, lib_name: []cons .weak = false, .use_pkg_config = .force, }, - }) catch unreachable; + }) catch @panic("OOM"); } /// Run pkg-config for the given library name and parse the output, returning the arguments @@ -797,7 +797,7 @@ fn linkSystemLibraryInner(self: *CompileStep, name: []const u8, opts: struct { .weak = opts.weak, .use_pkg_config = .yes, }, - }) catch unreachable; + }) catch @panic("OOM"); } pub fn setNamePrefix(self: *CompileStep, text: []const u8) void { @@ -817,7 +817,7 @@ pub fn setTestRunner(self: *CompileStep, path: ?[]const u8) void { /// Handy when you have many C/C++ source files and want them all to have the same flags. pub fn addCSourceFiles(self: *CompileStep, files: []const []const u8, flags: []const []const u8) void { - const c_source_files = self.builder.allocator.create(CSourceFiles) catch unreachable; + const c_source_files = self.builder.allocator.create(CSourceFiles) catch @panic("OOM"); const files_copy = self.builder.dupeStrings(files); const flags_copy = self.builder.dupeStrings(flags); @@ -826,7 +826,7 @@ pub fn addCSourceFiles(self: *CompileStep, files: []const []const u8, flags: []c .files = files_copy, .flags = flags_copy, }; - self.link_objects.append(.{ .c_source_files = c_source_files }) catch unreachable; + self.link_objects.append(.{ .c_source_files = c_source_files }) catch @panic("OOM"); } pub fn addCSourceFile(self: *CompileStep, file: []const u8, flags: []const []const u8) void { @@ -837,9 +837,9 @@ pub fn addCSourceFile(self: *CompileStep, file: []const u8, flags: []const []con } pub fn addCSourceFileSource(self: *CompileStep, source: CSourceFile) void { - const c_source_file = self.builder.allocator.create(CSourceFile) catch unreachable; + const c_source_file = self.builder.allocator.create(CSourceFile) catch @panic("OOM"); c_source_file.* = source.dupe(self.builder); - self.link_objects.append(.{ .c_source_file = c_source_file }) catch unreachable; + self.link_objects.append(.{ .c_source_file = c_source_file }) catch @panic("OOM"); source.source.addStepDependencies(&self.step); } @@ -893,12 +893,12 @@ pub fn getOutputPdbSource(self: *CompileStep) FileSource { pub fn addAssemblyFile(self: *CompileStep, path: []const u8) void { self.link_objects.append(.{ .assembly_file = .{ .path = self.builder.dupe(path) }, - }) catch unreachable; + }) catch @panic("OOM"); } pub fn addAssemblyFileSource(self: *CompileStep, source: FileSource) void { const source_duped = source.dupe(self.builder); - self.link_objects.append(.{ .assembly_file = source_duped }) catch unreachable; + self.link_objects.append(.{ .assembly_file = source_duped }) catch @panic("OOM"); source_duped.addStepDependencies(&self.step); } @@ -907,7 +907,7 @@ pub fn addObjectFile(self: *CompileStep, source_file: []const u8) void { } pub fn addObjectFileSource(self: *CompileStep, source: FileSource) void { - self.link_objects.append(.{ .static_path = source.dupe(self.builder) }) catch unreachable; + self.link_objects.append(.{ .static_path = source.dupe(self.builder) }) catch @panic("OOM"); source.addStepDependencies(&self.step); } @@ -922,11 +922,11 @@ pub const addLibPath = @compileError("deprecated, use addLibraryPath"); pub const addFrameworkDir = @compileError("deprecated, use addFrameworkPath"); pub fn addSystemIncludePath(self: *CompileStep, path: []const u8) void { - self.include_dirs.append(IncludeDir{ .raw_path_system = self.builder.dupe(path) }) catch unreachable; + self.include_dirs.append(IncludeDir{ .raw_path_system = self.builder.dupe(path) }) catch @panic("OOM"); } pub fn addIncludePath(self: *CompileStep, path: []const u8) void { - self.include_dirs.append(IncludeDir{ .raw_path = self.builder.dupe(path) }) catch unreachable; + self.include_dirs.append(IncludeDir{ .raw_path = self.builder.dupe(path) }) catch @panic("OOM"); } pub fn addConfigHeader(self: *CompileStep, config_header: *ConfigHeaderStep) void { @@ -935,19 +935,19 @@ pub fn addConfigHeader(self: *CompileStep, config_header: *ConfigHeaderStep) voi } pub fn addLibraryPath(self: *CompileStep, path: []const u8) void { - self.lib_paths.append(self.builder.dupe(path)) catch unreachable; + self.lib_paths.append(self.builder.dupe(path)) catch @panic("OOM"); } pub fn addRPath(self: *CompileStep, path: []const u8) void { - self.rpaths.append(self.builder.dupe(path)) catch unreachable; + self.rpaths.append(self.builder.dupe(path)) catch @panic("OOM"); } pub fn addFrameworkPath(self: *CompileStep, dir_path: []const u8) void { - self.framework_dirs.append(self.builder.dupe(dir_path)) catch unreachable; + self.framework_dirs.append(self.builder.dupe(dir_path)) catch @panic("OOM"); } pub fn addPackage(self: *CompileStep, package: Pkg) void { - self.packages.append(self.builder.dupePkg(package)) catch unreachable; + self.packages.append(self.builder.dupePkg(package)) catch @panic("OOM"); self.addRecursiveBuildDeps(package); } @@ -1010,7 +1010,7 @@ pub fn addVcpkgPaths(self: *CompileStep, linkage: CompileStep.Linkage) !void { pub fn setExecCmd(self: *CompileStep, args: []const ?[]const u8) void { assert(self.kind == .@"test"); - const duped_args = self.builder.allocator.alloc(?[]u8, args.len) catch unreachable; + const duped_args = self.builder.allocator.alloc(?[]u8, args.len) catch @panic("OOM"); for (args) |arg, i| { duped_args[i] = if (arg) |a| self.builder.dupe(a) else null; } @@ -1019,8 +1019,8 @@ pub fn setExecCmd(self: *CompileStep, args: []const ?[]const u8) void { fn linkLibraryOrObject(self: *CompileStep, other: *CompileStep) void { self.step.dependOn(&other.step); - self.link_objects.append(.{ .other_step = other }) catch unreachable; - self.include_dirs.append(.{ .other_step = other }) catch unreachable; + self.link_objects.append(.{ .other_step = other }) catch @panic("OOM"); + self.include_dirs.append(.{ .other_step = other }) catch @panic("OOM"); } fn makePackageCmd(self: *CompileStep, pkg: Pkg, zig_args: *ArrayList([]const u8)) error{OutOfMemory}!void { @@ -1051,7 +1051,7 @@ fn make(step: *Step) !void { var zig_args = ArrayList([]const u8).init(builder.allocator); defer zig_args.deinit(); - zig_args.append(builder.zig_exe) catch unreachable; + try zig_args.append(builder.zig_exe); const cmd = switch (self.kind) { .lib => "build-lib", @@ -1060,7 +1060,7 @@ fn make(step: *Step) !void { .@"test" => "test", .test_exe => "test", }; - zig_args.append(cmd) catch unreachable; + try zig_args.append(cmd); if (builder.color != .auto) { try zig_args.append("--color"); @@ -1265,12 +1265,12 @@ fn make(step: *Step) !void { try zig_args.append("--debug-compile-errors"); } - if (builder.verbose_cimport) zig_args.append("--verbose-cimport") catch unreachable; - if (builder.verbose_air) zig_args.append("--verbose-air") catch unreachable; - if (builder.verbose_llvm_ir) zig_args.append("--verbose-llvm-ir") catch unreachable; - if (builder.verbose_link or self.verbose_link) zig_args.append("--verbose-link") catch unreachable; - if (builder.verbose_cc or self.verbose_cc) zig_args.append("--verbose-cc") catch unreachable; - if (builder.verbose_llvm_cpu_features) zig_args.append("--verbose-llvm-cpu-features") catch unreachable; + if (builder.verbose_cimport) try zig_args.append("--verbose-cimport"); + if (builder.verbose_air) try zig_args.append("--verbose-air"); + if (builder.verbose_llvm_ir) try zig_args.append("--verbose-llvm-ir"); + if (builder.verbose_link or self.verbose_link) try zig_args.append("--verbose-link"); + if (builder.verbose_cc or self.verbose_cc) try zig_args.append("--verbose-cc"); + if (builder.verbose_llvm_cpu_features) try zig_args.append("--verbose-llvm-cpu-features"); if (self.emit_analysis.getArg(builder, "emit-analysis")) |arg| try zig_args.append(arg); if (self.emit_asm.getArg(builder, "emit-asm")) |arg| try zig_args.append(arg); @@ -1336,7 +1336,7 @@ fn make(step: *Step) !void { switch (self.optimize) { .Debug => {}, // Skip since it's the default. - else => zig_args.append(builder.fmt("-O{s}", .{@tagName(self.optimize)})) catch unreachable, + else => try zig_args.append(builder.fmt("-O{s}", .{@tagName(self.optimize)})), } try zig_args.append("--cache-dir"); @@ -1345,8 +1345,8 @@ fn make(step: *Step) !void { try zig_args.append("--global-cache-dir"); try zig_args.append(builder.pathFromRoot(builder.global_cache_root)); - zig_args.append("--name") catch unreachable; - zig_args.append(self.name) catch unreachable; + try zig_args.append("--name"); + try zig_args.append(self.name); if (self.linkage) |some| switch (some) { .dynamic => try zig_args.append("-dynamic"), @@ -1354,8 +1354,8 @@ fn make(step: *Step) !void { }; if (self.kind == .lib and self.linkage != null and self.linkage.? == .dynamic) { if (self.version) |version| { - zig_args.append("--version") catch unreachable; - zig_args.append(builder.fmt("{}", .{version})) catch unreachable; + try zig_args.append("--version"); + try zig_args.append(builder.fmt("{}", .{version})); } if (self.target.isDarwin()) { @@ -1651,13 +1651,13 @@ fn make(step: *Step) !void { const name = entry.key_ptr.*; const info = entry.value_ptr.*; if (info.needed) { - zig_args.append("-needed_framework") catch unreachable; + try zig_args.append("-needed_framework"); } else if (info.weak) { - zig_args.append("-weak_framework") catch unreachable; + try zig_args.append("-weak_framework"); } else { - zig_args.append("-framework") catch unreachable; + try zig_args.append("-framework"); } - zig_args.append(name) catch unreachable; + try zig_args.append(name); } } else { if (self.framework_dirs.items.len > 0) { @@ -1748,7 +1748,7 @@ fn make(step: *Step) !void { // Slow path for arguments that need to be escaped. We'll need to allocate and copy var escaped = try ArrayList(u8).initCapacity(args_arena.allocator(), arg.len + 1); const writer = escaped.writer(); - writer.writeAll(arg[0..arg_idx]) catch unreachable; + try writer.writeAll(arg[0..arg_idx]); for (arg[arg_idx..]) |to_escape| { if (to_escape == '\\' or to_escape == '"') try writer.writeByte('\\'); try writer.writeByte(to_escape); @@ -1874,23 +1874,28 @@ fn findVcpkgRoot(allocator: Allocator) !?[]const u8 { return vcpkg_path; } -pub fn doAtomicSymLinks(allocator: Allocator, output_path: []const u8, filename_major_only: []const u8, filename_name_only: []const u8) !void { +pub fn doAtomicSymLinks( + allocator: Allocator, + output_path: []const u8, + filename_major_only: []const u8, + filename_name_only: []const u8, +) !void { const out_dir = fs.path.dirname(output_path) orelse "."; const out_basename = fs.path.basename(output_path); // sym link for libfoo.so.1 to libfoo.so.1.2.3 - const major_only_path = fs.path.join( + const major_only_path = try fs.path.join( allocator, &[_][]const u8{ out_dir, filename_major_only }, - ) catch unreachable; + ); fs.atomicSymLink(allocator, out_basename, major_only_path) catch |err| { log.err("Unable to symlink {s} -> {s}", .{ major_only_path, out_basename }); return err; }; // sym link for libfoo.so to libfoo.so.1 - const name_only_path = fs.path.join( + const name_only_path = try fs.path.join( allocator, &[_][]const u8{ out_dir, filename_name_only }, - ) catch unreachable; + ); fs.atomicSymLink(allocator, filename_major_only, name_only_path) catch |err| { log.err("Unable to symlink {s} -> {s}", .{ name_only_path, filename_major_only }); return err; diff --git a/lib/std/Build/EmulatableRunStep.zig b/lib/std/Build/EmulatableRunStep.zig index 26804b2fc8..5517f7f9aa 100644 --- a/lib/std/Build/EmulatableRunStep.zig +++ b/lib/std/Build/EmulatableRunStep.zig @@ -47,7 +47,7 @@ hide_foreign_binaries_warning: bool, /// Asserts given artifact is an executable. pub fn create(builder: *std.Build, name: []const u8, artifact: *CompileStep) *EmulatableRunStep { std.debug.assert(artifact.kind == .exe or artifact.kind == .test_exe); - const self = builder.allocator.create(EmulatableRunStep) catch unreachable; + const self = builder.allocator.create(EmulatableRunStep) catch @panic("OOM"); const option_name = "hide-foreign-warnings"; const hide_warnings = if (builder.available_options_map.get(option_name) == null) warn: { @@ -154,9 +154,9 @@ fn warnAboutForeignBinaries(step: *EmulatableRunStep) void { const builder = step.builder; const artifact = step.exe; - const host_name = builder.host.target.zigTriple(builder.allocator) catch unreachable; - const foreign_name = artifact.target.zigTriple(builder.allocator) catch unreachable; - const target_info = std.zig.system.NativeTargetInfo.detect(artifact.target) catch unreachable; + const host_name = builder.host.target.zigTriple(builder.allocator) catch @panic("unhandled error"); + const foreign_name = artifact.target.zigTriple(builder.allocator) catch @panic("unhandled error"); + const target_info = std.zig.system.NativeTargetInfo.detect(artifact.target) catch @panic("unhandled error"); const need_cross_glibc = artifact.target.isGnuLibC() and artifact.is_linking_libc; switch (builder.host.getExternalExecutor(target_info, .{ .qemu_fixes_dl = need_cross_glibc and builder.glibc_runtimes_dir != null, diff --git a/lib/std/Build/FmtStep.zig b/lib/std/Build/FmtStep.zig index 44a93dee66..6404d22f13 100644 --- a/lib/std/Build/FmtStep.zig +++ b/lib/std/Build/FmtStep.zig @@ -9,12 +9,12 @@ builder: *std.Build, argv: [][]const u8, pub fn create(builder: *std.Build, paths: []const []const u8) *FmtStep { - const self = builder.allocator.create(FmtStep) catch unreachable; + const self = builder.allocator.create(FmtStep) catch @panic("OOM"); const name = "zig fmt"; self.* = FmtStep{ .step = Step.init(.fmt, name, builder.allocator, make), .builder = builder, - .argv = builder.allocator.alloc([]u8, paths.len + 2) catch unreachable, + .argv = builder.allocator.alloc([]u8, paths.len + 2) catch @panic("OOM"), }; self.argv[0] = builder.zig_exe; diff --git a/lib/std/Build/InstallArtifactStep.zig b/lib/std/Build/InstallArtifactStep.zig index 898b9e85e7..8ee739a41c 100644 --- a/lib/std/Build/InstallArtifactStep.zig +++ b/lib/std/Build/InstallArtifactStep.zig @@ -16,7 +16,7 @@ h_dir: ?InstallDir, pub fn create(builder: *std.Build, artifact: *CompileStep) *InstallArtifactStep { if (artifact.install_step) |s| return s; - const self = builder.allocator.create(InstallArtifactStep) catch unreachable; + const self = builder.allocator.create(InstallArtifactStep) catch @panic("OOM"); self.* = InstallArtifactStep{ .builder = builder, .step = Step.init(.install_artifact, builder.fmt("install {s}", .{artifact.step.name}), builder.allocator, make), diff --git a/lib/std/Build/InstallRawStep.zig b/lib/std/Build/InstallRawStep.zig index e7ffe78878..014c44f287 100644 --- a/lib/std/Build/InstallRawStep.zig +++ b/lib/std/Build/InstallRawStep.zig @@ -44,7 +44,7 @@ pub fn create( dest_filename: []const u8, options: CreateOptions, ) *InstallRawStep { - const self = builder.allocator.create(InstallRawStep) catch unreachable; + const self = builder.allocator.create(InstallRawStep) catch @panic("OOM"); self.* = InstallRawStep{ .step = Step.init(.install_raw, builder.fmt("install raw binary {s}", .{artifact.step.name}), builder.allocator, make), .builder = builder, @@ -82,7 +82,7 @@ fn make(step: *Step) !void { const full_dest_path = b.getInstallPath(self.dest_dir, self.dest_filename); self.output_file.path = full_dest_path; - fs.cwd().makePath(b.getInstallPath(self.dest_dir, "")) catch unreachable; + try fs.cwd().makePath(b.getInstallPath(self.dest_dir, "")); var argv_list = std.ArrayList([]const u8).init(b.allocator); try argv_list.appendSlice(&.{ b.zig_exe, "objcopy" }); diff --git a/lib/std/Build/OptionsStep.zig b/lib/std/Build/OptionsStep.zig index 94c41ce9e6..a353737512 100644 --- a/lib/std/Build/OptionsStep.zig +++ b/lib/std/Build/OptionsStep.zig @@ -19,7 +19,7 @@ artifact_args: std.ArrayList(OptionArtifactArg), file_source_args: std.ArrayList(OptionFileSourceArg), pub fn create(builder: *std.Build) *OptionsStep { - const self = builder.allocator.create(OptionsStep) catch unreachable; + const self = builder.allocator.create(OptionsStep) catch @panic("OOM"); self.* = .{ .builder = builder, .step = Step.init(.options, "options", builder.allocator, make), @@ -34,44 +34,48 @@ pub fn create(builder: *std.Build) *OptionsStep { } pub fn addOption(self: *OptionsStep, comptime T: type, name: []const u8, value: T) void { + return addOptionFallible(self, T, name, value) catch @panic("unhandled error"); +} + +fn addOptionFallible(self: *OptionsStep, comptime T: type, name: []const u8, value: T) !void { const out = self.contents.writer(); switch (T) { []const []const u8 => { - out.print("pub const {}: []const []const u8 = &[_][]const u8{{\n", .{std.zig.fmtId(name)}) catch unreachable; + try out.print("pub const {}: []const []const u8 = &[_][]const u8{{\n", .{std.zig.fmtId(name)}); for (value) |slice| { - out.print(" \"{}\",\n", .{std.zig.fmtEscapes(slice)}) catch unreachable; + try out.print(" \"{}\",\n", .{std.zig.fmtEscapes(slice)}); } - out.writeAll("};\n") catch unreachable; + try out.writeAll("};\n"); return; }, [:0]const u8 => { - out.print("pub const {}: [:0]const u8 = \"{}\";\n", .{ std.zig.fmtId(name), std.zig.fmtEscapes(value) }) catch unreachable; + try out.print("pub const {}: [:0]const u8 = \"{}\";\n", .{ std.zig.fmtId(name), std.zig.fmtEscapes(value) }); return; }, []const u8 => { - out.print("pub const {}: []const u8 = \"{}\";\n", .{ std.zig.fmtId(name), std.zig.fmtEscapes(value) }) catch unreachable; + try out.print("pub const {}: []const u8 = \"{}\";\n", .{ std.zig.fmtId(name), std.zig.fmtEscapes(value) }); return; }, ?[:0]const u8 => { - out.print("pub const {}: ?[:0]const u8 = ", .{std.zig.fmtId(name)}) catch unreachable; + try out.print("pub const {}: ?[:0]const u8 = ", .{std.zig.fmtId(name)}); if (value) |payload| { - out.print("\"{}\";\n", .{std.zig.fmtEscapes(payload)}) catch unreachable; + try out.print("\"{}\";\n", .{std.zig.fmtEscapes(payload)}); } else { - out.writeAll("null;\n") catch unreachable; + try out.writeAll("null;\n"); } return; }, ?[]const u8 => { - out.print("pub const {}: ?[]const u8 = ", .{std.zig.fmtId(name)}) catch unreachable; + try out.print("pub const {}: ?[]const u8 = ", .{std.zig.fmtId(name)}); if (value) |payload| { - out.print("\"{}\";\n", .{std.zig.fmtEscapes(payload)}) catch unreachable; + try out.print("\"{}\";\n", .{std.zig.fmtEscapes(payload)}); } else { - out.writeAll("null;\n") catch unreachable; + try out.writeAll("null;\n"); } return; }, std.builtin.Version => { - out.print( + try out.print( \\pub const {}: @import("std").builtin.Version = .{{ \\ .major = {d}, \\ .minor = {d}, @@ -84,11 +88,11 @@ pub fn addOption(self: *OptionsStep, comptime T: type, name: []const u8, value: value.major, value.minor, value.patch, - }) catch unreachable; + }); return; }, std.SemanticVersion => { - out.print( + try out.print( \\pub const {}: @import("std").SemanticVersion = .{{ \\ .major = {d}, \\ .minor = {d}, @@ -100,38 +104,38 @@ pub fn addOption(self: *OptionsStep, comptime T: type, name: []const u8, value: value.major, value.minor, value.patch, - }) catch unreachable; + }); if (value.pre) |some| { - out.print(" .pre = \"{}\",\n", .{std.zig.fmtEscapes(some)}) catch unreachable; + try out.print(" .pre = \"{}\",\n", .{std.zig.fmtEscapes(some)}); } if (value.build) |some| { - out.print(" .build = \"{}\",\n", .{std.zig.fmtEscapes(some)}) catch unreachable; + try out.print(" .build = \"{}\",\n", .{std.zig.fmtEscapes(some)}); } - out.writeAll("};\n") catch unreachable; + try out.writeAll("};\n"); return; }, else => {}, } switch (@typeInfo(T)) { .Enum => |enum_info| { - out.print("pub const {} = enum {{\n", .{std.zig.fmtId(@typeName(T))}) catch unreachable; + try out.print("pub const {} = enum {{\n", .{std.zig.fmtId(@typeName(T))}); inline for (enum_info.fields) |field| { - out.print(" {},\n", .{std.zig.fmtId(field.name)}) catch unreachable; + try out.print(" {},\n", .{std.zig.fmtId(field.name)}); } - out.writeAll("};\n") catch unreachable; - out.print("pub const {}: {s} = {s}.{s};\n", .{ + try out.writeAll("};\n"); + try out.print("pub const {}: {s} = {s}.{s};\n", .{ std.zig.fmtId(name), std.zig.fmtId(@typeName(T)), std.zig.fmtId(@typeName(T)), std.zig.fmtId(@tagName(value)), - }) catch unreachable; + }); return; }, else => {}, } - out.print("pub const {}: {s} = ", .{ std.zig.fmtId(name), @typeName(T) }) catch unreachable; - printLiteral(out, value, 0) catch unreachable; - out.writeAll(";\n") catch unreachable; + try out.print("pub const {}: {s} = ", .{ std.zig.fmtId(name), @typeName(T) }); + try printLiteral(out, value, 0); + try out.writeAll(";\n"); } // TODO: non-recursive? @@ -189,14 +193,14 @@ pub fn addOptionFileSource( self.file_source_args.append(.{ .name = name, .source = source.dupe(self.builder), - }) catch unreachable; + }) catch @panic("OOM"); source.addStepDependencies(&self.step); } /// The value is the path in the cache dir. /// Adds a dependency automatically. pub fn addOptionArtifact(self: *OptionsStep, name: []const u8, artifact: *CompileStep) void { - self.artifact_args.append(.{ .name = self.builder.dupe(name), .artifact = artifact }) catch unreachable; + self.artifact_args.append(.{ .name = self.builder.dupe(name), .artifact = artifact }) catch @panic("OOM"); self.step.dependOn(&artifact.step); } diff --git a/lib/std/Build/RunStep.zig b/lib/std/Build/RunStep.zig index 9ab9ea6335..07f2363623 100644 --- a/lib/std/Build/RunStep.zig +++ b/lib/std/Build/RunStep.zig @@ -54,7 +54,7 @@ pub const Arg = union(enum) { }; pub fn create(builder: *std.Build, name: []const u8) *RunStep { - const self = builder.allocator.create(RunStep) catch unreachable; + const self = builder.allocator.create(RunStep) catch @panic("OOM"); self.* = RunStep{ .builder = builder, .step = Step.init(base_id, name, builder.allocator, make), @@ -67,19 +67,19 @@ pub fn create(builder: *std.Build, name: []const u8) *RunStep { } pub fn addArtifactArg(self: *RunStep, artifact: *CompileStep) void { - self.argv.append(Arg{ .artifact = artifact }) catch unreachable; + self.argv.append(Arg{ .artifact = artifact }) catch @panic("OOM"); self.step.dependOn(&artifact.step); } pub fn addFileSourceArg(self: *RunStep, file_source: std.Build.FileSource) void { self.argv.append(Arg{ .file_source = file_source.dupe(self.builder), - }) catch unreachable; + }) catch @panic("OOM"); file_source.addStepDependencies(&self.step); } pub fn addArg(self: *RunStep, arg: []const u8) void { - self.argv.append(Arg{ .bytes = self.builder.dupe(arg) }) catch unreachable; + self.argv.append(Arg{ .bytes = self.builder.dupe(arg) }) catch @panic("OOM"); } pub fn addArgs(self: *RunStep, args: []const []const u8) void { @@ -89,7 +89,7 @@ pub fn addArgs(self: *RunStep, args: []const []const u8) void { } pub fn clearEnvironment(self: *RunStep) void { - const new_env_map = self.builder.allocator.create(EnvMap) catch unreachable; + const new_env_map = self.builder.allocator.create(EnvMap) catch @panic("OOM"); new_env_map.* = EnvMap.init(self.builder.allocator); self.env_map = new_env_map; } @@ -107,9 +107,9 @@ pub fn addPathDirInternal(step: *Step, builder: *std.Build, search_path: []const if (prev_path) |pp| { const new_path = builder.fmt("{s}" ++ [1]u8{fs.path.delimiter} ++ "{s}", .{ pp, search_path }); - env_map.put(key, new_path) catch unreachable; + env_map.put(key, new_path) catch @panic("OOM"); } else { - env_map.put(key, builder.dupePath(search_path)) catch unreachable; + env_map.put(key, builder.dupePath(search_path)) catch @panic("OOM"); } } @@ -124,8 +124,8 @@ fn getEnvMapInternal(step: *Step, allocator: Allocator) *EnvMap { else => unreachable, }; return maybe_env_map orelse { - const env_map = allocator.create(EnvMap) catch unreachable; - env_map.* = process.getEnvMap(allocator) catch unreachable; + const env_map = allocator.create(EnvMap) catch @panic("OOM"); + env_map.* = process.getEnvMap(allocator) catch @panic("unhandled error"); switch (step.id) { .run => step.cast(RunStep).?.env_map = env_map, .emulatable_run => step.cast(RunStep).?.env_map = env_map, @@ -140,7 +140,7 @@ pub fn setEnvironmentVariable(self: *RunStep, key: []const u8, value: []const u8 env_map.put( self.builder.dupe(key), self.builder.dupe(value), - ) catch unreachable; + ) catch @panic("unhandled error"); } pub fn expectStdErrEqual(self: *RunStep, bytes: []const u8) void { @@ -234,7 +234,7 @@ pub fn runCommand( switch (stdout_action) { .expect_exact, .expect_matches => { - stdout = child.stdout.?.reader().readAllAlloc(builder.allocator, max_stdout_size) catch unreachable; + stdout = try child.stdout.?.reader().readAllAlloc(builder.allocator, max_stdout_size); }, .inherit, .ignore => {}, } @@ -244,7 +244,7 @@ pub fn runCommand( switch (stderr_action) { .expect_exact, .expect_matches => { - stderr = child.stderr.?.reader().readAllAlloc(builder.allocator, max_stdout_size) catch unreachable; + stderr = try child.stderr.?.reader().readAllAlloc(builder.allocator, max_stdout_size); }, .inherit, .ignore => {}, } diff --git a/lib/std/Build/Step.zig b/lib/std/Build/Step.zig index aff8a49161..ff0ceb2a51 100644 --- a/lib/std/Build/Step.zig +++ b/lib/std/Build/Step.zig @@ -57,7 +57,7 @@ pub fn init( ) Step { return Step{ .id = id, - .name = allocator.dupe(u8, name) catch unreachable, + .name = allocator.dupe(u8, name) catch @panic("OOM"), .makeFn = makeFn, .dependencies = std.ArrayList(*Step).init(allocator), .loop_flag = false, @@ -77,7 +77,7 @@ pub fn make(self: *Step) !void { } pub fn dependOn(self: *Step, other: *Step) void { - self.dependencies.append(other) catch unreachable; + self.dependencies.append(other) catch @panic("OOM"); } fn makeNoOp(self: *Step) anyerror!void { diff --git a/lib/std/Build/TranslateCStep.zig b/lib/std/Build/TranslateCStep.zig index c59362aeee..d9874142d8 100644 --- a/lib/std/Build/TranslateCStep.zig +++ b/lib/std/Build/TranslateCStep.zig @@ -28,7 +28,7 @@ pub const Options = struct { }; pub fn create(builder: *std.Build, options: Options) *TranslateCStep { - const self = builder.allocator.create(TranslateCStep) catch unreachable; + const self = builder.allocator.create(TranslateCStep) catch @panic("OOM"); const source = options.source_file.dupe(builder); self.* = TranslateCStep{ .step = Step.init(.translate_c, "translate-c", builder.allocator, make), @@ -67,7 +67,7 @@ pub fn addExecutable(self: *TranslateCStep, options: AddExecutableOptions) *Comp } pub fn addIncludeDir(self: *TranslateCStep, include_dir: []const u8) void { - self.include_dirs.append(self.builder.dupePath(include_dir)) catch unreachable; + self.include_dirs.append(self.builder.dupePath(include_dir)) catch @panic("OOM"); } pub fn addCheckFile(self: *TranslateCStep, expected_matches: []const []const u8) *CheckFileStep { @@ -78,12 +78,12 @@ pub fn addCheckFile(self: *TranslateCStep, expected_matches: []const []const u8) /// `name` and `value` need not live longer than the function call. pub fn defineCMacro(self: *TranslateCStep, name: []const u8, value: ?[]const u8) void { const macro = std.Build.constructCMacro(self.builder.allocator, name, value); - self.c_macros.append(macro) catch unreachable; + self.c_macros.append(macro) catch @panic("OOM"); } /// name_and_value looks like [name]=[value]. If the value is omitted, it is set to 1. pub fn defineCMacroRaw(self: *TranslateCStep, name_and_value: []const u8) void { - self.c_macros.append(self.builder.dupe(name_and_value)) catch unreachable; + self.c_macros.append(self.builder.dupe(name_and_value)) catch @panic("OOM"); } fn make(step: *Step) !void { @@ -129,8 +129,8 @@ fn make(step: *Step) !void { self.output_dir = fs.path.dirname(output_path).?; } - self.output_file.path = fs.path.join( + self.output_file.path = try fs.path.join( self.builder.allocator, &[_][]const u8{ self.output_dir.?, self.out_basename }, - ) catch unreachable; + ); } diff --git a/lib/std/Build/WriteFileStep.zig b/lib/std/Build/WriteFileStep.zig index 59ac568221..9e8fcdc203 100644 --- a/lib/std/Build/WriteFileStep.zig +++ b/lib/std/Build/WriteFileStep.zig @@ -28,7 +28,7 @@ pub fn init(builder: *std.Build) WriteFileStep { } pub fn add(self: *WriteFileStep, basename: []const u8, bytes: []const u8) void { - const node = self.builder.allocator.create(std.TailQueue(File).Node) catch unreachable; + const node = self.builder.allocator.create(std.TailQueue(File).Node) catch @panic("unhandled error"); node.* = .{ .data = .{ .source = std.Build.GeneratedFile{ .step = &self.step }, @@ -106,10 +106,10 @@ fn make(step: *Step) !void { }); return err; }; - node.data.source.path = fs.path.join( + node.data.source.path = try fs.path.join( self.builder.allocator, &[_][]const u8{ self.output_dir, node.data.basename }, - ) catch unreachable; + ); } } } -- cgit v1.2.3