zig/lib/std/Build/ObjCopyStep.zig
Andrew Kelley 58edefc6d1 zig build: many enhancements related to parallel building
Rework std.Build.Step to have an `owner: *Build` field. This
simplified the implementation of installation steps, as well as provided
some much-needed common API for the new parallelized build system.

--verbose is now defined very concretely: it prints to stderr just
before spawning a child process.

Child process execution is updated to conform to the new
parallel-friendly make() function semantics.

DRY up the failWithCacheError handling code. It now integrates properly
with the step graph instead of incorrectly dumping to stderr and calling
process exit.

In the main CLI, fix `zig fmt` crash when there are no errors and stdin
is used.

Deleted steps:
 * EmulatableRunStep - this entire thing can be removed in favor of a
   flag added to std.Build.RunStep called `skip_foreign_checks`.
 * LogStep - this doesn't really fit with a multi-threaded build runner
   and is effectively superseded by the new build summary output.

build runner:
 * add -fsummary and -fno-summary to override the default behavior,
   which is to print a summary if any of the build steps fail.
 * print the dep prefix when emitting error messages for steps.

std.Build.FmtStep:
 * This step now supports exclude paths as well as a check flag.
 * The check flag decides between two modes, modify mode, and check
   mode. These can be used to update source files in place, or to fail
   the build, respectively.

Zig's own build.zig:
 * The `test-fmt` step will do all the `zig fmt` checking that we expect
   to be done. Since the `test` step depends on this one, we can simply
   remove the explicit call to `zig fmt` in the CI.
 * The new `fmt` step will actually perform `zig fmt` and update source
   files in place.

std.Build.RunStep:
 * expose max_stdio_size is a field (previously an unchangeable
   hard-coded value).
 * rework the API. Instead of configuring each stream independently,
   there is a `stdio` field where you can choose between
   `infer_from_args`, `inherit`, or `check`. These determine whether the
   RunStep is considered to have side-effects or not. The previous
   field, `condition` is gone.
 * when stdio mode is set to `check` there is a slice of any number of
   checks to make, which include things like exit code, stderr matching,
   or stdout matching.
 * remove the ill-defined `print` field.
 * when adding an output arg, it takes the opportunity to give itself a
   better name.
 * The flag `skip_foreign_checks` is added. If this is true, a RunStep
   which is configured to check the output of the executed binary will
   not fail the build if the binary cannot be executed due to being for
   a foreign binary to the host system which is running the build graph.
   Command-line arguments such as -fqemu and -fwasmtime may affect
   whether a binary is detected as foreign, as well as system
   configuration such as Rosetta (macOS) and binfmt_misc (Linux).
   - This makes EmulatableRunStep no longer needed.
 * Fix the child process handling to properly integrate with the new
   bulid API and to avoid deadlocks in stdout/stderr streams by polling
   if necessary.

std.Build.RemoveDirStep now uses the open build_root directory handle
instead of an absolute path.
2023-03-15 10:48:13 -07:00

122 lines
3.6 KiB
Zig

const std = @import("std");
const ObjCopyStep = @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 CompileStep = std.Build.CompileStep;
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: Step.Id = .objcopy;
pub const RawFormat = enum {
bin,
hex,
};
step: Step,
file_source: std.Build.FileSource,
basename: []const u8,
output_file: std.Build.GeneratedFile,
format: ?RawFormat,
only_section: ?[]const u8,
pad_to: ?u64,
pub const Options = struct {
basename: ?[]const u8 = null,
format: ?RawFormat = null,
only_section: ?[]const u8 = null,
pad_to: ?u64 = null,
};
pub fn create(
owner: *std.Build,
file_source: std.Build.FileSource,
options: Options,
) *ObjCopyStep {
const self = owner.allocator.create(ObjCopyStep) catch @panic("OOM");
self.* = ObjCopyStep{
.step = Step.init(.{
.id = base_id,
.name = owner.fmt("objcopy {s}", .{file_source.getDisplayName()}),
.owner = owner,
.makeFn = make,
}),
.file_source = file_source,
.basename = options.basename orelse file_source.getDisplayName(),
.output_file = std.Build.GeneratedFile{ .step = &self.step },
.format = options.format,
.only_section = options.only_section,
.pad_to = options.pad_to,
};
file_source.addStepDependencies(&self.step);
return self;
}
pub fn getOutputSource(self: *const ObjCopyStep) std.Build.FileSource {
return .{ .generated = &self.output_file };
}
fn make(step: *Step, prog_node: *std.Progress.Node) !void {
const b = step.owner;
const self = @fieldParentPtr(ObjCopyStep, "step", step);
var man = b.cache.obtain();
defer man.deinit();
// Random bytes to make ObjCopyStep unique. Refresh this with new random
// bytes when ObjCopyStep implementation is modified incompatibly.
man.hash.add(@as(u32, 0xe18b7baf));
const full_src_path = self.file_source.getPath(b);
_ = try man.addFile(full_src_path, null);
man.hash.addOptionalBytes(self.only_section);
man.hash.addOptional(self.pad_to);
man.hash.addOptional(self.format);
if (try step.cacheHit(&man)) {
// Cache hit, skip subprocess execution.
const digest = man.final();
self.output_file.path = try b.cache_root.join(b.allocator, &.{
"o", &digest, self.basename,
});
return;
}
const digest = man.final();
const full_dest_path = try b.cache_root.join(b.allocator, &.{ "o", &digest, self.basename });
const cache_path = "o" ++ fs.path.sep_str ++ digest;
b.cache_root.handle.makePath(cache_path) catch |err| {
std.debug.print("unable to make path {s}: {s}\n", .{ cache_path, @errorName(err) });
return err;
};
var argv = std.ArrayList([]const u8).init(b.allocator);
try argv.appendSlice(&.{ b.zig_exe, "objcopy" });
if (self.only_section) |only_section| {
try argv.appendSlice(&.{ "-j", only_section });
}
if (self.pad_to) |pad_to| {
try argv.appendSlice(&.{ "--pad-to", b.fmt("{d}", .{pad_to}) });
}
if (self.format) |format| switch (format) {
.bin => try argv.appendSlice(&.{ "-O", "binary" }),
.hex => try argv.appendSlice(&.{ "-O", "hex" }),
};
try argv.appendSlice(&.{ full_src_path, full_dest_path });
_ = try step.spawnZigProcess(argv.items, prog_node);
self.output_file.path = full_dest_path;
try man.writeManifest();
}