From 59fc4b69f521acef3199a18f16283b0bb1b72210 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20=28xq=29=20Quei=C3=9Fner?= Date: Tue, 25 Jul 2023 13:26:32 +0200 Subject: [PATCH] Introduces `Compile.getEmittedX()` functions, drops `Compile.emit_X`. Resolves #14971 --- build.zig | 5 - lib/std/Build.zig | 52 +--- lib/std/Build/Step.zig | 5 +- lib/std/Build/Step/Compile.zig | 262 ++++++++++++------ lib/std/Build/Step/InstallArtifact.zig | 16 +- lib/std/Build/Step/Run.zig | 7 +- lib/std/Build/util.zig | 53 ++++ test/link/glibc_compat/build.zig | 1 + test/link/macho/needed_library/build.zig | 1 + test/link/macho/search_strategy/build.zig | 2 + test/src/Cases.zig | 5 +- test/standalone.zig | 18 +- .../standalone/embed_generated_file/build.zig | 2 + test/standalone/emit_asm_and_bin/build.zig | 4 +- test/standalone/issue_12588/build.zig | 5 +- test/standalone/issue_339/build.zig | 2 + test/standalone/issue_5825/build.zig | 2 + test/standalone/issue_794/build.zig | 1 + test/standalone/main_pkg_path/build.zig | 2 +- test/standalone/strip_empty_loop/build.zig | 3 + test/standalone/use_alias/build.zig | 2 +- test/tests.zig | 2 + 22 files changed, 289 insertions(+), 163 deletions(-) create mode 100644 lib/std/Build/util.zig diff --git a/build.zig b/build.zig index b7db2d7aa81d..07130e84b051 100644 --- a/build.zig +++ b/build.zig @@ -59,7 +59,6 @@ pub fn build(b: *std.Build) !void { .target = target, }); autodoc_test.overrideZigLibDir(.{ .path = "lib" }); - autodoc_test.emit_bin = .no_emit; // https://github.com/ziglang/zig/issues/16351 const install_std_docs = b.addInstallDirectory(.{ .source_dir = autodoc_test.getEmittedDocs(), .install_dir = .prefix, @@ -196,10 +195,6 @@ pub fn build(b: *std.Build) !void { exe.pie = pie; exe.sanitize_thread = sanitize_thread; exe.entitlements = entitlements; - // TODO -femit-bin/-fno-emit-bin should be inferred by the build system - // based on whether or not the exe is run or installed. - // https://github.com/ziglang/zig/issues/16351 - if (no_bin) exe.emit_bin = .no_emit; exe.build_id = b.option( std.Build.Step.Compile.BuildId, diff --git a/lib/std/Build.zig b/lib/std/Build.zig index e68a860b8218..08da650d640b 100644 --- a/lib/std/Build.zig +++ b/lib/std/Build.zig @@ -19,6 +19,8 @@ const NativeTargetInfo = std.zig.system.NativeTargetInfo; const Sha256 = std.crypto.hash.sha2.Sha256; const Build = @This(); +const build_util = @import("Build/util.zig"); + pub const Cache = @import("Build/Cache.zig"); /// deprecated: use `Step.Compile`. @@ -1679,7 +1681,7 @@ pub const LazyPath = union(enum) { .generated => |gen| return gen.path orelse { std.debug.getStderrMutex().lock(); const stderr = std.io.getStdErr(); - dumpBadGetPathHelp(gen.step, stderr, src_builder, asking_step) catch {}; + build_util.dumpBadGetPathHelp(gen.step, stderr, src_builder, asking_step) catch {}; @panic("misconfigured build script"); }, } @@ -1694,54 +1696,6 @@ pub const LazyPath = union(enum) { } }; -/// In this function the stderr mutex has already been locked. -fn dumpBadGetPathHelp( - s: *Step, - stderr: fs.File, - src_builder: *Build, - asking_step: ?*Step, -) anyerror!void { - const w = stderr.writer(); - try w.print( - \\getPath() was called on a GeneratedFile that wasn't built yet. - \\ source package path: {s} - \\ Is there a missing Step dependency on step '{s}'? - \\ - , .{ - src_builder.build_root.path orelse ".", - s.name, - }); - - const tty_config = std.io.tty.detectConfig(stderr); - tty_config.setColor(w, .red) catch {}; - try stderr.writeAll(" The step was created by this stack trace:\n"); - tty_config.setColor(w, .reset) catch {}; - - const debug_info = std.debug.getSelfDebugInfo() catch |err| { - try w.print("Unable to dump stack trace: Unable to open debug info: {s}\n", .{@errorName(err)}); - return; - }; - const ally = debug_info.allocator; - std.debug.writeStackTrace(s.getStackTrace(), w, ally, debug_info, tty_config) catch |err| { - try stderr.writer().print("Unable to dump stack trace: {s}\n", .{@errorName(err)}); - return; - }; - if (asking_step) |as| { - tty_config.setColor(w, .red) catch {}; - try stderr.writeAll(" The step that is missing a dependency on the above step was created by this stack trace:\n"); - tty_config.setColor(w, .reset) catch {}; - - std.debug.writeStackTrace(as.getStackTrace(), w, ally, debug_info, tty_config) catch |err| { - try stderr.writer().print("Unable to dump stack trace: {s}\n", .{@errorName(err)}); - return; - }; - } - - tty_config.setColor(w, .red) catch {}; - try stderr.writeAll(" Hope that helps. Proceeding to panic.\n"); - tty_config.setColor(w, .reset) catch {}; -} - /// Allocates a new string for assigning a value to a named macro. /// If the value is omitted, it is set to 1. /// `name` and `value` need not live longer than the function call. diff --git a/lib/std/Build/Step.zig b/lib/std/Build/Step.zig index 991283dbc526..274188f7f2f3 100644 --- a/lib/std/Build/Step.zig +++ b/lib/std/Build/Step.zig @@ -423,7 +423,10 @@ pub fn evalZigProcess( }); } - if (s.cast(Compile)) |compile| if (compile.emit_bin == .no_emit) return result; + if (s.cast(Compile)) |compile| { + if (compile.generated_bin == null) // TODO(xq): How to handle this properly?! + return result; + } return result orelse return s.fail( "the following command failed to communicate the compilation result:\n{s}", diff --git a/lib/std/Build/Step/Compile.zig b/lib/std/Build/Step/Compile.zig index e3d4aa4155e4..d6eae499d262 100644 --- a/lib/std/Build/Step/Compile.zig +++ b/lib/std/Build/Step/Compile.zig @@ -21,6 +21,8 @@ const InstallDir = std.Build.InstallDir; const GeneratedFile = std.Build.GeneratedFile; const Compile = @This(); +const build_util = @import("../util.zig"); + pub const base_id: Step.Id = .compile; step: Step, @@ -46,14 +48,6 @@ framework_dirs: ArrayList(LazyPath), frameworks: StringHashMap(FrameworkLinkInfo), verbose_link: bool, verbose_cc: bool, -emit_asm: EmitOption = .default, -emit_bin: 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, stack_protector: ?bool = null, @@ -87,6 +81,9 @@ export_symbol_names: []const []const u8 = &.{}, root_src: ?LazyPath, out_h_filename: []const u8, +out_ll_filename: []const u8, +out_bc_filename: []const u8, +out_asm_filename: []const u8, out_lib_filename: []const u8, out_pdb_filename: []const u8, modules: std.StringArrayHashMap(*Module), @@ -210,12 +207,16 @@ use_lld: ?bool, /// otherwise. expect_errors: []const []const u8 = &.{}, -output_path_source: GeneratedFile, -output_lib_path_source: GeneratedFile, -output_h_path_source: GeneratedFile, -output_pdb_path_source: GeneratedFile, -output_dirname_source: GeneratedFile, +emit_directory: GeneratedFile, + generated_docs: ?*GeneratedFile, +generated_asm: ?*GeneratedFile, +generated_bin: ?*GeneratedFile, +generated_pdb: ?*GeneratedFile, +generated_implib: ?*GeneratedFile, +generated_llvm_bc: ?*GeneratedFile, +generated_llvm_ir: ?*GeneratedFile, +generated_h: ?*GeneratedFile, pub const CSourceFiles = struct { files: []const []const u8, @@ -373,22 +374,6 @@ pub const Kind = enum { 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(owner: *std.Build, options: Options) *Compile { const name = owner.dupe(options.name); const root_src: ?LazyPath = if (options.root_source_file) |rsrc| rsrc.dupe(owner) else null; @@ -454,6 +439,9 @@ pub fn create(owner: *std.Build, options: Options) *Compile { .version = options.version, .out_filename = out_filename, .out_h_filename = owner.fmt("{s}.h", .{name}), + .out_ll_filename = owner.fmt("{s}.bc", .{name}), + .out_bc_filename = owner.fmt("{s}.ll", .{name}), + .out_asm_filename = owner.fmt("{s}.s", .{name}), .out_lib_filename = undefined, .out_pdb_filename = owner.fmt("{s}.pdb", .{name}), .major_only_filename = null, @@ -480,12 +468,16 @@ pub fn create(owner: *std.Build, options: Options) *Compile { .installed_path = null, .force_undefined_symbols = StringHashMap(void).init(owner.allocator), - .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 }, - .output_dirname_source = GeneratedFile{ .step = &self.step }, + .emit_directory = GeneratedFile{ .step = &self.step }, + .generated_docs = null, + .generated_asm = null, + .generated_bin = null, + .generated_pdb = null, + .generated_implib = null, + .generated_llvm_bc = null, + .generated_llvm_ir = null, + .generated_h = null, .target_info = target_info, @@ -692,6 +684,7 @@ pub fn isStaticLibrary(self: *Compile) bool { } pub fn producesPdbFile(self: *Compile) bool { + // TODO: Is this right? Isn't PDB for *any* PE/COFF file? if (!self.target.isWindows() and !self.target.isUefi()) return false; if (self.target.getObjectFormat() == .c) return false; if (self.strip == true or (self.strip == null and self.optimize == .ReleaseSmall)) return false; @@ -968,54 +961,75 @@ pub fn setLibCFile(self: *Compile, libc_file: ?LazyPath) void { self.libc_file = if (libc_file) |f| f.dupe(b) else null; } -pub const getOutputSource = getEmittedBin; // DEPRECATED, use getEmittedBin - -/// 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 getEmittedBin(self: *Compile) LazyPath { - return .{ .generated = &self.output_path_source }; +fn getEmittedFileGeneric(self: *Compile, output_file: *?*GeneratedFile) LazyPath { + if (output_file.*) |g| { + return .{ .generated = g }; + } + const arena = self.step.owner.allocator; + const generated_file = arena.create(GeneratedFile) catch @panic("OOM"); + generated_file.* = .{ .step = &self.step }; + output_file.* = generated_file; + return .{ .generated = generated_file }; } pub const getOutputDirectorySource = getEmitDirectory; // DEPRECATED, use getEmitDirectory +/// Returns the path to the output directory. pub fn getEmitDirectory(self: *Compile) LazyPath { - return .{ .generated = &self.output_dirname_source }; + return .{ .generated = &self.emit_directory }; +} + +pub const getOutputSource = getEmittedBin; // DEPRECATED, use getEmittedBin + +/// Returns the path to 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 getEmittedBin(self: *Compile) LazyPath { + return self.getEmittedFileGeneric(&self.generated_bin); } pub const getOutputLibSource = getEmittedImplib; // DEPRECATED, use getEmittedImplib -/// Returns the generated import library. This function can only be called for libraries. +/// Returns the path to the generated import library. This function can only be called for libraries. pub fn getEmittedImplib(self: *Compile) LazyPath { assert(self.kind == .lib); - return .{ .generated = &self.output_lib_path_source }; + return self.getEmittedFileGeneric(&self.generated_implib); } pub const getOutputHSource = getEmittedH; // DEPRECATED, use getEmittedH -/// Returns the generated header file. -/// This function can only be called for libraries or object files which have `emit_h` set. +/// Returns the path to the generated header file. +/// This function can only be called for libraries or objects. pub fn getEmittedH(self: *Compile) LazyPath { assert(self.kind != .exe and self.kind != .@"test"); - assert(self.emit_h); - return .{ .generated = &self.output_h_path_source }; + return self.getEmittedFileGeneric(&self.generated_h); } pub const getOutputPdbSource = getEmittedPdb; // DEPRECATED, use getEmittedPdb /// Returns the generated PDB file. This function can only be called for Windows and UEFI. pub fn getEmittedPdb(self: *Compile) LazyPath { - // TODO: Is this right? Isn't PDB for *any* PE/COFF file? - assert(self.target.isWindows() or self.target.isUefi()); - return .{ .generated = &self.output_pdb_path_source }; + assert(self.producesPdbFile()); + return self.getEmittedFileGeneric(&self.generated_pdb); } +/// Returns the path to the generated documentation directory. pub fn getEmittedDocs(self: *Compile) LazyPath { - if (self.generated_docs) |g| return .{ .generated = g }; - const arena = self.step.owner.allocator; - const generated_file = arena.create(GeneratedFile) catch @panic("OOM"); - generated_file.* = .{ .step = &self.step }; - self.generated_docs = generated_file; - return .{ .generated = generated_file }; + return self.getEmittedFileGeneric(&self.generated_docs); +} + +/// Returns the path to the generated assembly code. +pub fn getEmittedAsm(self: *Compile) LazyPath { + return self.getEmittedFileGeneric(&self.generated_asm); +} + +/// Returns the path to the generated LLVM IR. +pub fn getEmittedLlvmIr(self: *Compile) LazyPath { + return self.getEmittedFileGeneric(&self.generated_llvm_ir); +} + +/// Returns the path to the generated LLVM BC. +pub fn getEmittedLlvmBc(self: *Compile) LazyPath { + return self.getEmittedFileGeneric(&self.generated_llvm_bc); } pub fn addAssemblyFile(self: *Compile, source: LazyPath) void { @@ -1149,6 +1163,12 @@ pub fn setExecCmd(self: *Compile, args: []const ?[]const u8) void { } fn linkLibraryOrObject(self: *Compile, other: *Compile) void { + _ = other.getEmittedBin(); // Force emission of the binary + + if (other.kind == .lib and other.target.isWindows()) { // TODO(xq): Is this the correct logic here? + _ = other.getEmittedImplib(); // Force emission of the binary + } + self.step.dependOn(&other.step); self.link_objects.append(.{ .other_step = other }) catch @panic("OOM"); self.include_dirs.append(.{ .other_step = other }) catch @panic("OOM"); @@ -1268,6 +1288,30 @@ fn constructDepString( } } +fn getGeneratedFilePath(self: *Compile, comptime tag_name: []const u8, asking_step: ?*Step) []const u8 { + const maybe_path: ?*GeneratedFile = @field(self, tag_name); + + const generated_file = maybe_path orelse { + std.debug.getStderrMutex().lock(); + const stderr = std.io.getStdErr(); + + build_util.dumpBadGetPathHelp(&self.step, stderr, self.step.owner, asking_step) catch {}; + + @panic("missing emit option for " ++ tag_name); + }; + + const path = generated_file.path orelse { + std.debug.getStderrMutex().lock(); + const stderr = std.io.getStdErr(); + + build_util.dumpBadGetPathHelp(&self.step, stderr, self.step.owner, asking_step) catch {}; + + @panic(tag_name ++ " is null. Is there a missing step dependency?"); + }; + + return path; +} + fn make(step: *Step, prog_node: *std.Progress.Node) !void { const b = step.owner; const self = @fieldParentPtr(Compile, "step", step); @@ -1353,7 +1397,11 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { break :l; } - const full_path_lib = other.getEmittedImplib().getPath(b); + // TODO(xq): Is that the right way? + const full_path_lib = if (other.isDynamicLibrary() and other.target.isWindows()) + other.getGeneratedFilePath("generated_implib", &self.step) + else + other.getGeneratedFilePath("generated_bin", &self.step); try zig_args.append(full_path_lib); if (other.linkage == Linkage.dynamic and !self.target.isWindows()) { @@ -1496,14 +1544,36 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { if (b.verbose_cc or self.verbose_cc) try zig_args.append("--verbose-cc"); if (b.verbose_llvm_cpu_features) try zig_args.append("--verbose-llvm-cpu-features"); - if (self.emit_asm.getArg(b, "emit-asm")) |arg| try zig_args.append(arg); - if (self.emit_bin.getArg(b, "emit-bin")) |arg| try zig_args.append(arg); - if (self.generated_docs != null) try zig_args.append("-femit-docs"); - if (self.emit_implib.getArg(b, "emit-implib")) |arg| try zig_args.append(arg); - if (self.emit_llvm_bc.getArg(b, "emit-llvm-bc")) |arg| try zig_args.append(arg); - if (self.emit_llvm_ir.getArg(b, "emit-llvm-ir")) |arg| try zig_args.append(arg); + const Emitter = struct { + ptr: ?*GeneratedFile, + emit_suffix: []const u8, + }; - if (self.emit_h) try zig_args.append("-femit-h"); + const generated_files = [_]Emitter{ + .{ .ptr = self.generated_asm, .emit_suffix = "asm" }, + .{ .ptr = self.generated_bin, .emit_suffix = "bin" }, + .{ .ptr = self.generated_docs, .emit_suffix = "docs" }, + .{ .ptr = self.generated_implib, .emit_suffix = "implib" }, + .{ .ptr = self.generated_llvm_bc, .emit_suffix = "llvm-bc" }, + .{ .ptr = self.generated_llvm_ir, .emit_suffix = "llvm-ir" }, + .{ .ptr = self.generated_h, .emit_suffix = "h" }, + }; + var any_emitted_file = false; + for (generated_files) |file| { + try zig_args.append(if (file.ptr != null) + b.fmt("-femit-{s}", .{file.emit_suffix}) + else + b.fmt("-fno-emit-{s}", .{file.emit_suffix})); + + if (file.ptr != null) any_emitted_file = true; + } + + if (!any_emitted_file) { + std.debug.getStderrMutex().lock(); + const stderr = std.io.getStdErr(); + build_util.dumpBadGetPathHelp(&self.step, stderr, self.step.owner, null) catch {}; + std.debug.panic("Artifact '{s}' has no emit options set, but it is made. Did you forget to call `getEmitted*()`?.", .{self.name}); + } try addFlag(&zig_args, "strip", self.strip); try addFlag(&zig_args, "unwind-tables", self.unwind_tables); @@ -1755,10 +1825,9 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { try zig_args.append(common_include_path); }, .other_step => |other| { - if (other.emit_h) { - const h_path = other.getEmittedH().getPath(b); + if (other.generated_h) |header| { try zig_args.append("-isystem"); - try zig_args.append(fs.path.dirname(h_path).?); + try zig_args.append(fs.path.dirname(header.path.?).?); } if (other.installed_headers.items.len > 0) { try zig_args.append("-I"); @@ -1989,33 +2058,64 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { if (maybe_output_bin_path) |output_bin_path| { const output_dir = fs.path.dirname(output_bin_path).?; - self.output_dirname_source.path = output_dir; - - self.output_path_source.path = b.pathJoin( - &.{ output_dir, self.out_filename }, - ); + self.emit_directory.path = output_dir; - if (self.kind == .lib) { - self.output_lib_path_source.path = b.pathJoin( - &.{ output_dir, self.out_lib_filename }, + // -femit-bin[=path] (default) Output machine code + if (self.generated_bin) |bin| { + bin.path = b.pathJoin( + &.{ output_dir, self.out_filename }, ); } - if (self.emit_h) { - self.output_h_path_source.path = b.pathJoin( - &.{ output_dir, self.out_h_filename }, + // output PDB if someone requested it + if (self.generated_pdb) |pdb| { + std.debug.assert(self.producesPdbFile()); + pdb.path = b.pathJoin( + &.{ output_dir, self.out_pdb_filename }, ); } - if (self.target.isWindows() or self.target.isUefi()) { - self.output_pdb_path_source.path = b.pathJoin( - &.{ output_dir, self.out_pdb_filename }, + // -femit-implib[=path] (default) Produce an import .lib when building a Windows DLL + if (self.kind == .lib) { + if (self.generated_implib) |lib| { + lib.path = b.pathJoin( + &.{ output_dir, self.out_lib_filename }, + ); + } + } + + // -femit-h[=path] Generate a C header file (.h) + if (self.generated_h) |lazy_path| { + lazy_path.path = b.pathJoin( + &.{ output_dir, self.out_h_filename }, ); } + // -femit-docs[=path] Create a docs/ dir with html documentation if (self.generated_docs) |generated_docs| { generated_docs.path = b.pathJoin(&.{ output_dir, "docs" }); } + + // -femit-asm[=path] Output .s (assembly code) + if (self.generated_asm) |lazy_path| { + lazy_path.path = b.pathJoin( + &.{ output_dir, self.out_asm_filename }, + ); + } + + // -femit-llvm-ir[=path] Produce a .ll file with optimized LLVM IR (requires LLVM extensions) + if (self.generated_llvm_ir) |lazy_path| { + lazy_path.path = b.pathJoin( + &.{ output_dir, self.out_ll_filename }, + ); + } + + // -femit-llvm-bc[=path] Produce an optimized LLVM module as a .bc file (requires LLVM extensions) + if (self.generated_llvm_bc) |lazy_path| { + lazy_path.path = b.pathJoin( + &.{ output_dir, self.out_bc_filename }, + ); + } } if (self.kind == .lib and self.linkage != null and self.linkage.? == .dynamic and diff --git a/lib/std/Build/Step/InstallArtifact.zig b/lib/std/Build/Step/InstallArtifact.zig index ab01e03f98e5..7d65e3b59483 100644 --- a/lib/std/Build/Step/InstallArtifact.zig +++ b/lib/std/Build/Step/InstallArtifact.zig @@ -37,11 +37,12 @@ pub fn create(owner: *std.Build, artifact: *Step.Compile) *InstallArtifact { break :blk InstallDir{ .lib = {} }; } } else null, - .h_dir = if (artifact.kind == .lib and artifact.emit_h) .header else null, + .h_dir = if (artifact.kind == .lib and artifact.generated_h != null) .header else null, .dest_sub_path = null, }; self.step.dependOn(&artifact.step); + _ = artifact.getEmittedBin(); // force creation owner.pushInstalledFile(self.dest_dir, artifact.out_filename); if (self.artifact.isDynamicLibrary()) { if (artifact.major_only_filename) |name| { @@ -55,9 +56,11 @@ pub fn create(owner: *std.Build, artifact: *Step.Compile) *InstallArtifact { } } if (self.pdb_dir) |pdb_dir| { + _ = artifact.getEmittedPdb(); // force creation owner.pushInstalledFile(pdb_dir, artifact.out_pdb_filename); } if (self.h_dir) |h_dir| { + _ = artifact.getEmittedH(); // force creation owner.pushInstalledFile(h_dir, artifact.out_h_filename); } return self; @@ -66,7 +69,6 @@ pub fn create(owner: *std.Build, artifact: *Step.Compile) *InstallArtifact { fn make(step: *Step, prog_node: *std.Progress.Node) !void { _ = prog_node; const self = @fieldParentPtr(InstallArtifact, "step", step); - const src_builder = self.artifact.step.owner; const dest_builder = step.owner; const dest_sub_path = if (self.dest_sub_path) |sub_path| sub_path else self.artifact.out_filename; @@ -76,7 +78,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { var all_cached = true; { - const full_src_path = self.artifact.getEmittedBin().getPath(src_builder); + const full_src_path = self.artifact.generated_bin.?.path.?; const p = fs.Dir.updateFile(cwd, full_src_path, cwd, full_dest_path, .{}) catch |err| { return step.fail("unable to update file from '{s}' to '{s}': {s}", .{ full_src_path, full_dest_path, @errorName(err), @@ -93,9 +95,9 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { } if (self.artifact.isDynamicLibrary() and self.artifact.target.isWindows() and - self.artifact.emit_implib != .no_emit) + self.artifact.generated_implib != null) { - const full_src_path = self.artifact.getEmittedImplib().getPath(src_builder); + const full_src_path = self.artifact.generated_implib.?.path.?; const full_implib_path = dest_builder.getInstallPath(self.dest_dir, self.artifact.out_lib_filename); const p = fs.Dir.updateFile(cwd, full_src_path, cwd, full_implib_path, .{}) catch |err| { return step.fail("unable to update file from '{s}' to '{s}': {s}", .{ @@ -105,7 +107,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { all_cached = all_cached and p == .fresh; } if (self.pdb_dir) |pdb_dir| { - const full_src_path = self.artifact.getEmittedPdb().getPath(src_builder); + const full_src_path = self.artifact.generated_pdb.?.path.?; const full_pdb_path = dest_builder.getInstallPath(pdb_dir, self.artifact.out_pdb_filename); const p = fs.Dir.updateFile(cwd, full_src_path, cwd, full_pdb_path, .{}) catch |err| { return step.fail("unable to update file from '{s}' to '{s}': {s}", .{ @@ -115,7 +117,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { all_cached = all_cached and p == .fresh; } if (self.h_dir) |h_dir| { - const full_src_path = self.artifact.getEmittedH().getPath(src_builder); + const full_src_path = self.artifact.generated_h.?.path.?; const full_h_path = dest_builder.getInstallPath(h_dir, self.artifact.out_h_filename); const p = fs.Dir.updateFile(cwd, full_src_path, cwd, full_h_path, .{}) catch |err| { return step.fail("unable to update file from '{s}' to '{s}': {s}", .{ diff --git a/lib/std/Build/Step/Run.zig b/lib/std/Build/Step/Run.zig index 9c94138704c3..3b24656a02f6 100644 --- a/lib/std/Build/Step/Run.zig +++ b/lib/std/Build/Step/Run.zig @@ -164,6 +164,10 @@ pub fn enableTestRunnerMode(self: *Run) void { } pub fn addArtifactArg(self: *Run, artifact: *Step.Compile) void { + // enforce creation of the binary file by invoking getEmittedBin + const bin_file = artifact.getEmittedBin(); + bin_file.addStepDependencies(&self.step); + self.argv.append(Arg{ .artifact = artifact }) catch @panic("OOM"); self.step.dependOn(&artifact.step); } @@ -456,8 +460,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { // On Windows we don't have rpaths so we have to add .dll search paths to PATH self.addPathForDynLibs(artifact); } - const file_path = artifact.installed_path orelse - artifact.getEmittedBin().getPath(b); + const file_path = artifact.installed_path orelse artifact.generated_bin.?.path.?; // the path is guaranteed to be set try argv_list.append(file_path); diff --git a/lib/std/Build/util.zig b/lib/std/Build/util.zig new file mode 100644 index 000000000000..5c1b91f3d646 --- /dev/null +++ b/lib/std/Build/util.zig @@ -0,0 +1,53 @@ +const std = @import("std"); +const fs = std.fs; + +const Build = std.Build; +const Step = std.Build.Step; + +/// In this function the stderr mutex has already been locked. +pub fn dumpBadGetPathHelp( + s: *Step, + stderr: fs.File, + src_builder: *Build, + asking_step: ?*Step, +) anyerror!void { + const w = stderr.writer(); + try w.print( + \\getPath() was called on a GeneratedFile that wasn't built yet. + \\ source package path: {s} + \\ Is there a missing Step dependency on step '{s}'? + \\ + , .{ + src_builder.build_root.path orelse ".", + s.name, + }); + + const tty_config = std.io.tty.detectConfig(stderr); + tty_config.setColor(w, .red) catch {}; + try stderr.writeAll(" The step was created by this stack trace:\n"); + tty_config.setColor(w, .reset) catch {}; + + const debug_info = std.debug.getSelfDebugInfo() catch |err| { + try w.print("Unable to dump stack trace: Unable to open debug info: {s}\n", .{@errorName(err)}); + return; + }; + const ally = debug_info.allocator; + std.debug.writeStackTrace(s.getStackTrace(), w, ally, debug_info, tty_config) catch |err| { + try stderr.writer().print("Unable to dump stack trace: {s}\n", .{@errorName(err)}); + return; + }; + if (asking_step) |as| { + tty_config.setColor(w, .red) catch {}; + try stderr.writeAll(" The step that is missing a dependency on the above step was created by this stack trace:\n"); + tty_config.setColor(w, .reset) catch {}; + + std.debug.writeStackTrace(as.getStackTrace(), w, ally, debug_info, tty_config) catch |err| { + try stderr.writer().print("Unable to dump stack trace: {s}\n", .{@errorName(err)}); + return; + }; + } + + tty_config.setColor(w, .red) catch {}; + try stderr.writeAll(" Hope that helps. Proceeding to panic.\n"); + tty_config.setColor(w, .reset) catch {}; +} diff --git a/test/link/glibc_compat/build.zig b/test/link/glibc_compat/build.zig index 8d850cf350cc..dfef8c49239d 100644 --- a/test/link/glibc_compat/build.zig +++ b/test/link/glibc_compat/build.zig @@ -13,6 +13,7 @@ pub fn build(b: *std.Build) void { ) catch unreachable, }); exe.linkLibC(); + _ = exe.getEmittedBin(); // force emission test_step.dependOn(&exe.step); } } diff --git a/test/link/macho/needed_library/build.zig b/test/link/macho/needed_library/build.zig index 1c2cd9b45215..cdc78d860095 100644 --- a/test/link/macho/needed_library/build.zig +++ b/test/link/macho/needed_library/build.zig @@ -23,6 +23,7 @@ fn add(b: *std.Build, test_step: *std.Build.Step, optimize: std.builtin.Optimize }); dylib.addCSourceFile(.{ .file = .{ .path = "a.c" }, .flags = &.{} }); dylib.linkLibC(); + _ = dylib.getEmittedBin(); // enforce emission // -dead_strip_dylibs // -needed-la diff --git a/test/link/macho/search_strategy/build.zig b/test/link/macho/search_strategy/build.zig index 3ecefc306bb0..150e4e0436d8 100644 --- a/test/link/macho/search_strategy/build.zig +++ b/test/link/macho/search_strategy/build.zig @@ -60,6 +60,7 @@ fn createScenario( static.override_dest_dir = std.Build.InstallDir{ .custom = "static", }; + _ = static.getEmittedBin(); // enforce emission const dylib = b.addSharedLibrary(.{ .name = name, @@ -72,6 +73,7 @@ fn createScenario( dylib.override_dest_dir = std.Build.InstallDir{ .custom = "dynamic", }; + _ = dylib.getEmittedBin(); // enforce emission const exe = b.addExecutable(.{ .name = name, diff --git a/test/src/Cases.zig b/test/src/Cases.zig index 694c2800872b..112b4f6dfb0a 100644 --- a/test/src/Cases.zig +++ b/test/src/Cases.zig @@ -551,7 +551,10 @@ pub fn lowerToBuildSteps( }), }; - artifact.emit_bin = if (case.emit_bin) .default else .no_emit; + if (case.emit_bin) + _ = artifact.getEmittedBin(); + + _ = artifact.getEmittedBin(); // TODO(xq): The test cases break if we set all to -fno-emit-X if (case.link_libc) artifact.linkLibC(); diff --git a/test/standalone.zig b/test/standalone.zig index 7cbe45293491..b01c77c2bc1c 100644 --- a/test/standalone.zig +++ b/test/standalone.zig @@ -140,16 +140,14 @@ pub const build_cases = [_]BuildCase{ .build_root = "test/standalone/install_raw_hex", .import = @import("standalone/install_raw_hex/build.zig"), }, - // TODO take away EmitOption.emit_to option and make it give a FileSource - //.{ - // .build_root = "test/standalone/emit_asm_and_bin", - // .import = @import("standalone/emit_asm_and_bin/build.zig"), - //}, - // TODO take away EmitOption.emit_to option and make it give a FileSource - //.{ - // .build_root = "test/standalone/issue_12588", - // .import = @import("standalone/issue_12588/build.zig"), - //}, + .{ + .build_root = "test/standalone/emit_asm_and_bin", + .import = @import("standalone/emit_asm_and_bin/build.zig"), + }, + .{ + .build_root = "test/standalone/issue_12588", + .import = @import("standalone/issue_12588/build.zig"), + }, .{ .build_root = "test/standalone/child_process", .import = @import("standalone/child_process/build.zig"), diff --git a/test/standalone/embed_generated_file/build.zig b/test/standalone/embed_generated_file/build.zig index b914b9ffab3f..7e8c4a54dfdb 100644 --- a/test/standalone/embed_generated_file/build.zig +++ b/test/standalone/embed_generated_file/build.zig @@ -22,5 +22,7 @@ pub fn build(b: *std.Build) void { .source_file = bootloader.getEmittedBin(), }); + _ = exe.getEmittedBin(); // enforce emission + test_step.dependOn(&exe.step); } diff --git a/test/standalone/emit_asm_and_bin/build.zig b/test/standalone/emit_asm_and_bin/build.zig index 1a608b40a925..edc4a12fffd0 100644 --- a/test/standalone/emit_asm_and_bin/build.zig +++ b/test/standalone/emit_asm_and_bin/build.zig @@ -8,8 +8,8 @@ pub fn build(b: *std.Build) void { .root_source_file = .{ .path = "main.zig" }, .optimize = b.standardOptimizeOption(.{}), }); - main.emit_asm = .{ .emit_to = b.pathFromRoot("main.s") }; - main.emit_bin = .{ .emit_to = b.pathFromRoot("main") }; + _ = main.getEmittedBin(); // main.emit_asm = .{ .emit_to = b.pathFromRoot("main.s") }; + _ = main.getEmittedAsm(); // main.emit_bin = .{ .emit_to = b.pathFromRoot("main") }; test_step.dependOn(&b.addRunArtifact(main).step); } diff --git a/test/standalone/issue_12588/build.zig b/test/standalone/issue_12588/build.zig index fa22252fcc83..e6d01192d3cf 100644 --- a/test/standalone/issue_12588/build.zig +++ b/test/standalone/issue_12588/build.zig @@ -13,9 +13,8 @@ pub fn build(b: *std.Build) void { .optimize = optimize, .target = target, }); - obj.emit_llvm_ir = .{ .emit_to = b.pathFromRoot("main.ll") }; - obj.emit_llvm_bc = .{ .emit_to = b.pathFromRoot("main.bc") }; - obj.emit_bin = .no_emit; + _ = obj.getEmittedLlvmIr(); + _ = obj.getEmittedLlvmBc(); b.default_step.dependOn(&obj.step); test_step.dependOn(&obj.step); diff --git a/test/standalone/issue_339/build.zig b/test/standalone/issue_339/build.zig index f4215dbb8bc2..0d2b4954d7da 100644 --- a/test/standalone/issue_339/build.zig +++ b/test/standalone/issue_339/build.zig @@ -14,5 +14,7 @@ pub fn build(b: *std.Build) void { .optimize = optimize, }); + _ = obj.getEmittedBin(); // enforce emission + test_step.dependOn(&obj.step); } diff --git a/test/standalone/issue_5825/build.zig b/test/standalone/issue_5825/build.zig index e8e8d4877230..9c9c913f83a5 100644 --- a/test/standalone/issue_5825/build.zig +++ b/test/standalone/issue_5825/build.zig @@ -27,5 +27,7 @@ pub fn build(b: *std.Build) void { exe.linkSystemLibrary("ntdll"); exe.addObject(obj); + _ = exe.getEmittedBin(); // enforce emission + test_step.dependOn(&exe.step); } diff --git a/test/standalone/issue_794/build.zig b/test/standalone/issue_794/build.zig index 1a319798cb65..75ec033d2102 100644 --- a/test/standalone/issue_794/build.zig +++ b/test/standalone/issue_794/build.zig @@ -8,6 +8,7 @@ pub fn build(b: *std.Build) void { .root_source_file = .{ .path = "main.zig" }, }); test_artifact.addIncludePath(.{ .path = "a_directory" }); + _ = test_artifact.getEmittedBin(); // enforce emission test_step.dependOn(&test_artifact.step); } diff --git a/test/standalone/main_pkg_path/build.zig b/test/standalone/main_pkg_path/build.zig index 9f7f67255454..bbe7cfb53b52 100644 --- a/test/standalone/main_pkg_path/build.zig +++ b/test/standalone/main_pkg_path/build.zig @@ -7,7 +7,7 @@ pub fn build(b: *std.Build) void { const test_exe = b.addTest(.{ .root_source_file = .{ .path = "a/test.zig" }, }); - test_exe.setMainPkgPath(.{.path="."}); + test_exe.setMainPkgPath(.{ .path = "." }); test_step.dependOn(&b.addRunArtifact(test_exe).step); } diff --git a/test/standalone/strip_empty_loop/build.zig b/test/standalone/strip_empty_loop/build.zig index 73c70d661627..b6752029ce81 100644 --- a/test/standalone/strip_empty_loop/build.zig +++ b/test/standalone/strip_empty_loop/build.zig @@ -14,5 +14,8 @@ pub fn build(b: *std.Build) void { .target = target, }); main.strip = true; + + _ = main.getEmittedBin(); // enforce emission + test_step.dependOn(&main.step); } diff --git a/test/standalone/use_alias/build.zig b/test/standalone/use_alias/build.zig index b20c20d5066f..0511cd3935fc 100644 --- a/test/standalone/use_alias/build.zig +++ b/test/standalone/use_alias/build.zig @@ -10,7 +10,7 @@ pub fn build(b: *std.Build) void { .root_source_file = .{ .path = "main.zig" }, .optimize = optimize, }); - main.addIncludePath(.{.path="."}); + main.addIncludePath(.{ .path = "." }); test_step.dependOn(&b.addRunArtifact(main).step); } diff --git a/test/tests.zig b/test/tests.zig index b0aa63e8beb6..d0393cbf05ed 100644 --- a/test/tests.zig +++ b/test/tests.zig @@ -588,6 +588,8 @@ pub fn addStandaloneTests( }); if (case.link_libc) exe.linkLibC(); + _ = exe.getEmittedBin(); // Force emission + step.dependOn(&exe.step); }