Commit 56cb0b5ca0

Felix (xq) Queißner <git@mq32.de>
2021-02-22 23:46:29
Moves files to file-global struct layout.
1 parent 8501bb0
lib/std/build/check_file.zig
@@ -1,57 +0,0 @@
-// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2021 Zig Contributors
-// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
-// The MIT license requires this copyright notice to be included in all copies
-// and substantial portions of the software.
-const std = @import("../std.zig");
-const build = std.build;
-const Step = build.Step;
-const Builder = build.Builder;
-const fs = std.fs;
-const mem = std.mem;
-const warn = std.debug.warn;
-
-pub const CheckFileStep = struct {
-    step: Step,
-    builder: *Builder,
-    expected_matches: []const []const u8,
-    source: build.FileSource,
-    max_bytes: usize = 20 * 1024 * 1024,
-
-    pub fn create(
-        builder: *Builder,
-        source: build.FileSource,
-        expected_matches: []const []const u8,
-    ) *CheckFileStep {
-        const self = builder.allocator.create(CheckFileStep) catch unreachable;
-        self.* = CheckFileStep{
-            .builder = builder,
-            .step = Step.init(.CheckFile, "CheckFile", builder.allocator, make),
-            .source = source.dupe(builder),
-            .expected_matches = builder.dupeStrings(expected_matches),
-        };
-        self.source.addStepDependencies(&self.step);
-        return self;
-    }
-
-    fn make(step: *Step) !void {
-        const self = @fieldParentPtr(CheckFileStep, "step", step);
-
-        const src_path = self.source.getPath(self.builder);
-        const contents = try fs.cwd().readFileAlloc(self.builder.allocator, src_path, self.max_bytes);
-
-        for (self.expected_matches) |expected_match| {
-            if (mem.indexOf(u8, contents, expected_match) == null) {
-                warn(
-                    \\
-                    \\========= Expected to find: ===================
-                    \\{s}
-                    \\========= But file does not contain it: =======
-                    \\{s}
-                    \\
-                , .{ expected_match, contents });
-                return error.TestFailed;
-            }
-        }
-    }
-};
lib/std/build/CheckFileStep.zig
@@ -0,0 +1,57 @@
+// SPDX-License-Identifier: MIT
+// Copyright (c) 2015-2021 Zig Contributors
+// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
+// The MIT license requires this copyright notice to be included in all copies
+// and substantial portions of the software.
+const std = @import("../std.zig");
+const build = std.build;
+const Step = build.Step;
+const Builder = build.Builder;
+const fs = std.fs;
+const mem = std.mem;
+const warn = std.debug.warn;
+
+const CheckFileStep = @This();
+
+step: Step,
+builder: *Builder,
+expected_matches: []const []const u8,
+source: build.FileSource,
+max_bytes: usize = 20 * 1024 * 1024,
+
+pub fn create(
+    builder: *Builder,
+    source: build.FileSource,
+    expected_matches: []const []const u8,
+) *CheckFileStep {
+    const self = builder.allocator.create(CheckFileStep) catch unreachable;
+    self.* = CheckFileStep{
+        .builder = builder,
+        .step = Step.init(.CheckFile, "CheckFile", builder.allocator, make),
+        .source = source.dupe(builder),
+        .expected_matches = builder.dupeStrings(expected_matches),
+    };
+    self.source.addStepDependencies(&self.step);
+    return self;
+}
+
+fn make(step: *Step) !void {
+    const self = @fieldParentPtr(CheckFileStep, "step", step);
+
+    const src_path = self.source.getPath(self.builder);
+    const contents = try fs.cwd().readFileAlloc(self.builder.allocator, src_path, self.max_bytes);
+
+    for (self.expected_matches) |expected_match| {
+        if (mem.indexOf(u8, contents, expected_match) == null) {
+            warn(
+                \\
+                \\========= Expected to find: ===================
+                \\{s}
+                \\========= But file does not contain it: =======
+                \\{s}
+                \\
+            , .{ expected_match, contents });
+            return error.TestFailed;
+        }
+    }
+}
lib/std/build/fmt.zig
@@ -1,40 +0,0 @@
-// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2021 Zig Contributors
-// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
-// The MIT license requires this copyright notice to be included in all copies
-// and substantial portions of the software.
-const std = @import("../std.zig");
-const build = @import("../build.zig");
-const Step = build.Step;
-const Builder = build.Builder;
-const BufMap = std.BufMap;
-const mem = std.mem;
-
-pub const FmtStep = struct {
-    step: Step,
-    builder: *Builder,
-    argv: [][]const u8,
-
-    pub fn create(builder: *Builder, paths: []const []const u8) *FmtStep {
-        const self = builder.allocator.create(FmtStep) catch unreachable;
-        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,
-        };
-
-        self.argv[0] = builder.zig_exe;
-        self.argv[1] = "fmt";
-        for (paths) |path, i| {
-            self.argv[2 + i] = builder.pathFromRoot(path);
-        }
-        return self;
-    }
-
-    fn make(step: *Step) !void {
-        const self = @fieldParentPtr(FmtStep, "step", step);
-
-        return self.builder.spawnChild(self.argv);
-    }
-};
lib/std/build/FmtStep.zig
@@ -0,0 +1,40 @@
+// SPDX-License-Identifier: MIT
+// Copyright (c) 2015-2021 Zig Contributors
+// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
+// The MIT license requires this copyright notice to be included in all copies
+// and substantial portions of the software.
+const std = @import("../std.zig");
+const build = @import("../build.zig");
+const Step = build.Step;
+const Builder = build.Builder;
+const BufMap = std.BufMap;
+const mem = std.mem;
+
+const FmtStep = @This();
+
+step: Step,
+builder: *Builder,
+argv: [][]const u8,
+
+pub fn create(builder: *Builder, paths: []const []const u8) *FmtStep {
+    const self = builder.allocator.create(FmtStep) catch unreachable;
+    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,
+    };
+
+    self.argv[0] = builder.zig_exe;
+    self.argv[1] = "fmt";
+    for (paths) |path, i| {
+        self.argv[2 + i] = builder.pathFromRoot(path);
+    }
+    return self;
+}
+
+fn make(step: *Step) !void {
+    const self = @fieldParentPtr(FmtStep, "step", step);
+
+    return self.builder.spawnChild(self.argv);
+}
lib/std/build/emit_raw.zig → lib/std/build/InstallRawStep.zig
@@ -177,51 +177,49 @@ fn emitRaw(allocator: *Allocator, elf_path: []const u8, raw_path: []const u8) !v
     }
 }
 
-pub const InstallRawStep = struct {
-    step: Step,
-    builder: *Builder,
-    artifact: *LibExeObjStep,
-    dest_dir: InstallDir,
-    dest_filename: []const u8,
-
-    const Self = @This();
+const InstallRawStep = @This();
+
+step: Step,
+builder: *Builder,
+artifact: *LibExeObjStep,
+dest_dir: InstallDir,
+dest_filename: []const u8,
+
+pub fn create(builder: *Builder, artifact: *LibExeObjStep, dest_filename: []const u8) *InstallRawStep {
+    const self = builder.allocator.create(InstallRawStep) catch unreachable;
+    self.* = InstallRawStep{
+        .step = Step.init(.InstallRaw, builder.fmt("install raw binary {s}", .{artifact.step.name}), builder.allocator, make),
+        .builder = builder,
+        .artifact = artifact,
+        .dest_dir = switch (artifact.kind) {
+            .Obj => unreachable,
+            .Test => unreachable,
+            .Exe => .Bin,
+            .Lib => unreachable,
+        },
+        .dest_filename = dest_filename,
+    };
+    self.step.dependOn(&artifact.step);
+
+    builder.pushInstalledFile(self.dest_dir, dest_filename);
+    return self;
+}
 
-    pub fn create(builder: *Builder, artifact: *LibExeObjStep, dest_filename: []const u8) *Self {
-        const self = builder.allocator.create(Self) catch unreachable;
-        self.* = Self{
-            .step = Step.init(.InstallRaw, builder.fmt("install raw binary {s}", .{artifact.step.name}), builder.allocator, make),
-            .builder = builder,
-            .artifact = artifact,
-            .dest_dir = switch (artifact.kind) {
-                .Obj => unreachable,
-                .Test => unreachable,
-                .Exe => .Bin,
-                .Lib => unreachable,
-            },
-            .dest_filename = dest_filename,
-        };
-        self.step.dependOn(&artifact.step);
+fn make(step: *Step) !void {
+    const self = @fieldParentPtr(InstallRawStep, "step", step);
+    const builder = self.builder;
 
-        builder.pushInstalledFile(self.dest_dir, dest_filename);
-        return self;
+    if (self.artifact.target.getObjectFormat() != .elf) {
+        warn("InstallRawStep only works with ELF format.\n", .{});
+        return error.InvalidObjectFormat;
     }
 
-    fn make(step: *Step) !void {
-        const self = @fieldParentPtr(Self, "step", step);
-        const builder = self.builder;
+    const full_src_path = self.artifact.getOutputPath();
+    const full_dest_path = builder.getInstallPath(self.dest_dir, self.dest_filename);
 
-        if (self.artifact.target.getObjectFormat() != .elf) {
-            warn("InstallRawStep only works with ELF format.\n", .{});
-            return error.InvalidObjectFormat;
-        }
-
-        const full_src_path = self.artifact.getOutputPath();
-        const full_dest_path = builder.getInstallPath(self.dest_dir, self.dest_filename);
-
-        fs.cwd().makePath(builder.getInstallPath(self.dest_dir, "")) catch unreachable;
-        try emitRaw(builder.allocator, full_src_path, full_dest_path);
-    }
-};
+    fs.cwd().makePath(builder.getInstallPath(self.dest_dir, "")) catch unreachable;
+    try emitRaw(builder.allocator, full_src_path, full_dest_path);
+}
 
 test {
     std.testing.refAllDecls(InstallRawStep);
lib/std/build/RunStep.zig
@@ -0,0 +1,322 @@
+// SPDX-License-Identifier: MIT
+// Copyright (c) 2015-2021 Zig Contributors
+// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
+// The MIT license requires this copyright notice to be included in all copies
+// and substantial portions of the software.
+const std = @import("../std.zig");
+const builtin = std.builtin;
+const build = std.build;
+const Step = build.Step;
+const Builder = build.Builder;
+const LibExeObjStep = build.LibExeObjStep;
+const WriteFileStep = build.WriteFileStep;
+const fs = std.fs;
+const mem = std.mem;
+const process = std.process;
+const ArrayList = std.ArrayList;
+const BufMap = std.BufMap;
+const warn = std.debug.warn;
+
+const max_stdout_size = 1 * 1024 * 1024; // 1 MiB
+
+const RunStep = @This();
+
+step: Step,
+builder: *Builder,
+
+/// See also addArg and addArgs to modifying this directly
+argv: ArrayList(Arg),
+
+/// Set this to modify the current working directory
+cwd: ?[]const u8,
+
+/// Override this field to modify the environment, or use setEnvironmentVariable
+env_map: ?*BufMap,
+
+stdout_action: StdIoAction = .inherit,
+stderr_action: StdIoAction = .inherit,
+
+stdin_behavior: std.ChildProcess.StdIo = .Inherit,
+
+expected_exit_code: u8 = 0,
+
+pub const StdIoAction = union(enum) {
+    inherit,
+    ignore,
+    expect_exact: []const u8,
+    expect_matches: []const []const u8,
+};
+
+pub const Arg = union(enum) {
+    artifact: *LibExeObjStep,
+    file_source: build.FileSource,
+    bytes: []u8,
+};
+
+pub fn create(builder: *Builder, name: []const u8) *RunStep {
+    const self = builder.allocator.create(RunStep) catch unreachable;
+    self.* = RunStep{
+        .builder = builder,
+        .step = Step.init(.Run, name, builder.allocator, make),
+        .argv = ArrayList(Arg).init(builder.allocator),
+        .cwd = null,
+        .env_map = null,
+    };
+    return self;
+}
+
+pub fn addArtifactArg(self: *RunStep, artifact: *LibExeObjStep) void {
+    self.argv.append(Arg{ .artifact = artifact }) catch unreachable;
+    self.step.dependOn(&artifact.step);
+}
+
+pub fn addFileSourceArg(self: *RunStep, file_source: build.FileSource) void {
+    self.argv.append(Arg{
+        .file_source = file_source.dupe(self.builder),
+    }) catch unreachable;
+    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;
+}
+
+pub fn addArgs(self: *RunStep, args: []const []const u8) void {
+    for (args) |arg| {
+        self.addArg(arg);
+    }
+}
+
+pub fn clearEnvironment(self: *RunStep) void {
+    const new_env_map = self.builder.allocator.create(BufMap) catch unreachable;
+    new_env_map.* = BufMap.init(self.builder.allocator);
+    self.env_map = new_env_map;
+}
+
+pub fn addPathDir(self: *RunStep, search_path: []const u8) void {
+    const env_map = self.getEnvMap();
+
+    var key: []const u8 = undefined;
+    var prev_path: ?[]const u8 = undefined;
+    if (builtin.os.tag == .windows) {
+        key = "Path";
+        prev_path = env_map.get(key);
+        if (prev_path == null) {
+            key = "PATH";
+            prev_path = env_map.get(key);
+        }
+    } else {
+        key = "PATH";
+        prev_path = env_map.get(key);
+    }
+
+    if (prev_path) |pp| {
+        const new_path = self.builder.fmt("{s}" ++ [1]u8{fs.path.delimiter} ++ "{s}", .{ pp, search_path });
+        env_map.set(key, new_path) catch unreachable;
+    } else {
+        env_map.set(key, self.builder.dupePath(search_path)) catch unreachable;
+    }
+}
+
+pub fn getEnvMap(self: *RunStep) *BufMap {
+    return self.env_map orelse {
+        const env_map = self.builder.allocator.create(BufMap) catch unreachable;
+        env_map.* = process.getEnvMap(self.builder.allocator) catch unreachable;
+        self.env_map = env_map;
+        return env_map;
+    };
+}
+
+pub fn setEnvironmentVariable(self: *RunStep, key: []const u8, value: []const u8) void {
+    const env_map = self.getEnvMap();
+    env_map.set(
+        self.builder.dupe(key),
+        self.builder.dupe(value),
+    ) catch unreachable;
+}
+
+pub fn expectStdErrEqual(self: *RunStep, bytes: []const u8) void {
+    self.stderr_action = .{ .expect_exact = self.builder.dupe(bytes) };
+}
+
+pub fn expectStdOutEqual(self: *RunStep, bytes: []const u8) void {
+    self.stdout_action = .{ .expect_exact = self.builder.dupe(bytes) };
+}
+
+fn stdIoActionToBehavior(action: StdIoAction) std.ChildProcess.StdIo {
+    return switch (action) {
+        .ignore => .Ignore,
+        .inherit => .Inherit,
+        .expect_exact, .expect_matches => .Pipe,
+    };
+}
+
+fn make(step: *Step) !void {
+    const self = @fieldParentPtr(RunStep, "step", step);
+
+    const cwd = if (self.cwd) |cwd| self.builder.pathFromRoot(cwd) else self.builder.build_root;
+
+    var argv_list = ArrayList([]const u8).init(self.builder.allocator);
+    for (self.argv.items) |arg| {
+        switch (arg) {
+            .bytes => |bytes| try argv_list.append(bytes),
+            .file_source => |file| try argv_list.append(file.getPath(self.builder)),
+            .artifact => |artifact| {
+                if (artifact.target.isWindows()) {
+                    // On Windows we don't have rpaths so we have to add .dll search paths to PATH
+                    self.addPathForDynLibs(artifact);
+                }
+                const executable_path = artifact.installed_path orelse artifact.getOutputPath();
+                try argv_list.append(executable_path);
+            },
+        }
+    }
+
+    const argv = argv_list.items;
+
+    const child = std.ChildProcess.init(argv, self.builder.allocator) catch unreachable;
+    defer child.deinit();
+
+    child.cwd = cwd;
+    child.env_map = self.env_map orelse self.builder.env_map;
+
+    child.stdin_behavior = self.stdin_behavior;
+    child.stdout_behavior = stdIoActionToBehavior(self.stdout_action);
+    child.stderr_behavior = stdIoActionToBehavior(self.stderr_action);
+
+    child.spawn() catch |err| {
+        warn("Unable to spawn {s}: {s}\n", .{ argv[0], @errorName(err) });
+        return err;
+    };
+
+    // TODO need to poll to read these streams to prevent a deadlock (or rely on evented I/O).
+
+    var stdout: ?[]const u8 = null;
+    defer if (stdout) |s| self.builder.allocator.free(s);
+
+    switch (self.stdout_action) {
+        .expect_exact, .expect_matches => {
+            stdout = child.stdout.?.reader().readAllAlloc(self.builder.allocator, max_stdout_size) catch unreachable;
+        },
+        .inherit, .ignore => {},
+    }
+
+    var stderr: ?[]const u8 = null;
+    defer if (stderr) |s| self.builder.allocator.free(s);
+
+    switch (self.stderr_action) {
+        .expect_exact, .expect_matches => {
+            stderr = child.stderr.?.reader().readAllAlloc(self.builder.allocator, max_stdout_size) catch unreachable;
+        },
+        .inherit, .ignore => {},
+    }
+
+    const term = child.wait() catch |err| {
+        warn("Unable to spawn {s}: {s}\n", .{ argv[0], @errorName(err) });
+        return err;
+    };
+
+    switch (term) {
+        .Exited => |code| {
+            if (code != self.expected_exit_code) {
+                warn("The following command exited with error code {} (expected {}):\n", .{
+                    code,
+                    self.expected_exit_code,
+                });
+                printCmd(cwd, argv);
+                return error.UncleanExit;
+            }
+        },
+        else => {
+            warn("The following command terminated unexpectedly:\n", .{});
+            printCmd(cwd, argv);
+            return error.UncleanExit;
+        },
+    }
+
+    switch (self.stderr_action) {
+        .inherit, .ignore => {},
+        .expect_exact => |expected_bytes| {
+            if (!mem.eql(u8, expected_bytes, stderr.?)) {
+                warn(
+                    \\
+                    \\========= Expected this stderr: =========
+                    \\{s}
+                    \\========= But found: ====================
+                    \\{s}
+                    \\
+                , .{ expected_bytes, stderr.? });
+                printCmd(cwd, argv);
+                return error.TestFailed;
+            }
+        },
+        .expect_matches => |matches| for (matches) |match| {
+            if (mem.indexOf(u8, stderr.?, match) == null) {
+                warn(
+                    \\
+                    \\========= Expected to find in stderr: =========
+                    \\{s}
+                    \\========= But stderr does not contain it: =====
+                    \\{s}
+                    \\
+                , .{ match, stderr.? });
+                printCmd(cwd, argv);
+                return error.TestFailed;
+            }
+        },
+    }
+
+    switch (self.stdout_action) {
+        .inherit, .ignore => {},
+        .expect_exact => |expected_bytes| {
+            if (!mem.eql(u8, expected_bytes, stdout.?)) {
+                warn(
+                    \\
+                    \\========= Expected this stdout: =========
+                    \\{s}
+                    \\========= But found: ====================
+                    \\{s}
+                    \\
+                , .{ expected_bytes, stdout.? });
+                printCmd(cwd, argv);
+                return error.TestFailed;
+            }
+        },
+        .expect_matches => |matches| for (matches) |match| {
+            if (mem.indexOf(u8, stdout.?, match) == null) {
+                warn(
+                    \\
+                    \\========= Expected to find in stdout: =========
+                    \\{s}
+                    \\========= But stdout does not contain it: =====
+                    \\{s}
+                    \\
+                , .{ match, stdout.? });
+                printCmd(cwd, argv);
+                return error.TestFailed;
+            }
+        },
+    }
+}
+
+fn printCmd(cwd: ?[]const u8, argv: []const []const u8) void {
+    if (cwd) |yes_cwd| warn("cd {s} && ", .{yes_cwd});
+    for (argv) |arg| {
+        warn("{s} ", .{arg});
+    }
+    warn("\n", .{});
+}
+
+fn addPathForDynLibs(self: *RunStep, artifact: *LibExeObjStep) void {
+    for (artifact.link_objects.items) |link_object| {
+        switch (link_object) {
+            .other_step => |other| {
+                if (other.target.isWindows() and other.isDynamicLibrary()) {
+                    self.addPathDir(fs.path.dirname(other.getOutputPath()).?);
+                    self.addPathForDynLibs(other);
+                }
+            },
+            else => {},
+        }
+    }
+}
lib/std/build/translate_c.zig
@@ -1,109 +0,0 @@
-// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2021 Zig Contributors
-// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
-// The MIT license requires this copyright notice to be included in all copies
-// and substantial portions of the software.
-const std = @import("../std.zig");
-const build = std.build;
-const Step = build.Step;
-const Builder = build.Builder;
-const LibExeObjStep = build.LibExeObjStep;
-const CheckFileStep = build.CheckFileStep;
-const fs = std.fs;
-const mem = std.mem;
-const CrossTarget = std.zig.CrossTarget;
-
-pub const TranslateCStep = struct {
-    step: Step,
-    builder: *Builder,
-    source: build.FileSource,
-    include_dirs: std.ArrayList([]const u8),
-    output_dir: ?[]const u8,
-    out_basename: []const u8,
-    target: CrossTarget = CrossTarget{},
-    output_file: build.GeneratedFile,
-
-    pub fn create(builder: *Builder, source: build.FileSource) *TranslateCStep {
-        const self = builder.allocator.create(TranslateCStep) catch unreachable;
-        self.* = TranslateCStep{
-            .step = Step.init(.TranslateC, "translate-c", builder.allocator, make),
-            .builder = builder,
-            .source = source,
-            .include_dirs = std.ArrayList([]const u8).init(builder.allocator),
-            .output_dir = null,
-            .out_basename = undefined,
-            .output_file = build.GeneratedFile{
-                .step = &self.step,
-                .getPathFn = getGeneratedFilePath,
-            },
-        };
-        source.addStepDependencies(&self.step);
-        return self;
-    }
-
-    fn getGeneratedFilePath(file: *const build.GeneratedFile) []const u8 {
-        const self = @fieldParentPtr(TranslateCStep, "step", file.step);
-        return self.getOutputPath();
-    }
-
-    /// Unless setOutputDir was called, this function must be called only in
-    /// the make step, from a step that has declared a dependency on this one.
-    /// To run an executable built with zig build, use `run`, or create an install step and invoke it.
-    pub fn getOutputPath(self: *TranslateCStep) []const u8 {
-        return fs.path.join(
-            self.builder.allocator,
-            &[_][]const u8{ self.output_dir.?, self.out_basename },
-        ) catch unreachable;
-    }
-
-    pub fn setTarget(self: *TranslateCStep, target: CrossTarget) void {
-        self.target = target;
-    }
-
-    /// Creates a step to build an executable from the translated source.
-    pub fn addExecutable(self: *TranslateCStep) *LibExeObjStep {
-        return self.builder.addExecutableSource("translated_c", build.FileSource{ .generated = &self.output_file }, false);
-    }
-
-    pub fn addIncludeDir(self: *TranslateCStep, include_dir: []const u8) void {
-        self.include_dirs.append(self.builder.dupePath(include_dir)) catch unreachable;
-    }
-
-    pub fn addCheckFile(self: *TranslateCStep, expected_matches: []const []const u8) *CheckFileStep {
-        return CheckFileStep.create(self.builder, .{ .generated = &self.output_file }, self.builder.dupeStrings(expected_matches));
-    }
-
-    fn make(step: *Step) !void {
-        const self = @fieldParentPtr(TranslateCStep, "step", step);
-
-        var argv_list = std.ArrayList([]const u8).init(self.builder.allocator);
-        try argv_list.append(self.builder.zig_exe);
-        try argv_list.append("translate-c");
-        try argv_list.append("-lc");
-
-        try argv_list.append("--enable-cache");
-
-        if (!self.target.isNative()) {
-            try argv_list.append("-target");
-            try argv_list.append(try self.target.zigTriple(self.builder.allocator));
-        }
-
-        for (self.include_dirs.items) |include_dir| {
-            try argv_list.append("-I");
-            try argv_list.append(include_dir);
-        }
-
-        try argv_list.append(self.source.getPath(self.builder));
-
-        const output_path_nl = try self.builder.execFromStep(argv_list.items, &self.step);
-        const output_path = mem.trimRight(u8, output_path_nl, "\r\n");
-
-        self.out_basename = fs.path.basename(output_path);
-        if (self.output_dir) |output_dir| {
-            const full_dest = try fs.path.join(self.builder.allocator, &[_][]const u8{ output_dir, self.out_basename });
-            try self.builder.updateFile(output_path, full_dest);
-        } else {
-            self.output_dir = fs.path.dirname(output_path).?;
-        }
-    }
-};
lib/std/build/TranslateCStep.zig
@@ -0,0 +1,109 @@
+// SPDX-License-Identifier: MIT
+// Copyright (c) 2015-2021 Zig Contributors
+// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
+// The MIT license requires this copyright notice to be included in all copies
+// and substantial portions of the software.
+const std = @import("../std.zig");
+const build = std.build;
+const Step = build.Step;
+const Builder = build.Builder;
+const LibExeObjStep = build.LibExeObjStep;
+const CheckFileStep = build.CheckFileStep;
+const fs = std.fs;
+const mem = std.mem;
+const CrossTarget = std.zig.CrossTarget;
+
+const TranslateCStep = @This();
+
+step: Step,
+builder: *Builder,
+source: build.FileSource,
+include_dirs: std.ArrayList([]const u8),
+output_dir: ?[]const u8,
+out_basename: []const u8,
+target: CrossTarget = CrossTarget{},
+output_file: build.GeneratedFile,
+
+pub fn create(builder: *Builder, source: build.FileSource) *TranslateCStep {
+    const self = builder.allocator.create(TranslateCStep) catch unreachable;
+    self.* = TranslateCStep{
+        .step = Step.init(.TranslateC, "translate-c", builder.allocator, make),
+        .builder = builder,
+        .source = source,
+        .include_dirs = std.ArrayList([]const u8).init(builder.allocator),
+        .output_dir = null,
+        .out_basename = undefined,
+        .output_file = build.GeneratedFile{
+            .step = &self.step,
+            .getPathFn = getGeneratedFilePath,
+        },
+    };
+    source.addStepDependencies(&self.step);
+    return self;
+}
+
+fn getGeneratedFilePath(file: *const build.GeneratedFile) []const u8 {
+    const self = @fieldParentPtr(TranslateCStep, "step", file.step);
+    return self.getOutputPath();
+}
+
+/// Unless setOutputDir was called, this function must be called only in
+/// the make step, from a step that has declared a dependency on this one.
+/// To run an executable built with zig build, use `run`, or create an install step and invoke it.
+pub fn getOutputPath(self: *TranslateCStep) []const u8 {
+    return fs.path.join(
+        self.builder.allocator,
+        &[_][]const u8{ self.output_dir.?, self.out_basename },
+    ) catch unreachable;
+}
+
+pub fn setTarget(self: *TranslateCStep, target: CrossTarget) void {
+    self.target = target;
+}
+
+/// Creates a step to build an executable from the translated source.
+pub fn addExecutable(self: *TranslateCStep) *LibExeObjStep {
+    return self.builder.addExecutableSource("translated_c", build.FileSource{ .generated = &self.output_file }, false);
+}
+
+pub fn addIncludeDir(self: *TranslateCStep, include_dir: []const u8) void {
+    self.include_dirs.append(self.builder.dupePath(include_dir)) catch unreachable;
+}
+
+pub fn addCheckFile(self: *TranslateCStep, expected_matches: []const []const u8) *CheckFileStep {
+    return CheckFileStep.create(self.builder, .{ .generated = &self.output_file }, self.builder.dupeStrings(expected_matches));
+}
+
+fn make(step: *Step) !void {
+    const self = @fieldParentPtr(TranslateCStep, "step", step);
+
+    var argv_list = std.ArrayList([]const u8).init(self.builder.allocator);
+    try argv_list.append(self.builder.zig_exe);
+    try argv_list.append("translate-c");
+    try argv_list.append("-lc");
+
+    try argv_list.append("--enable-cache");
+
+    if (!self.target.isNative()) {
+        try argv_list.append("-target");
+        try argv_list.append(try self.target.zigTriple(self.builder.allocator));
+    }
+
+    for (self.include_dirs.items) |include_dir| {
+        try argv_list.append("-I");
+        try argv_list.append(include_dir);
+    }
+
+    try argv_list.append(self.source.getPath(self.builder));
+
+    const output_path_nl = try self.builder.execFromStep(argv_list.items, &self.step);
+    const output_path = mem.trimRight(u8, output_path_nl, "\r\n");
+
+    self.out_basename = fs.path.basename(output_path);
+    if (self.output_dir) |output_dir| {
+        const full_dest = try fs.path.join(self.builder.allocator, &[_][]const u8{ output_dir, self.out_basename });
+        try self.builder.updateFile(output_path, full_dest);
+    } else {
+        self.output_dir = fs.path.dirname(output_path).?;
+    }
+}
lib/std/build/write_file.zig
@@ -1,133 +0,0 @@
-// SPDX-License-Identifier: MIT
-// Copyright (c) 2015-2021 Zig Contributors
-// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
-// The MIT license requires this copyright notice to be included in all copies
-// and substantial portions of the software.
-const std = @import("../std.zig");
-const build = @import("../build.zig");
-const Step = build.Step;
-const Builder = build.Builder;
-const fs = std.fs;
-const warn = std.debug.warn;
-const ArrayList = std.ArrayList;
-
-pub const WriteFileStep = struct {
-    step: Step,
-    builder: *Builder,
-    output_dir: []const u8,
-    files: std.TailQueue(File),
-
-    pub const File = struct {
-        source: build.GeneratedFile,
-        basename: []const u8,
-        bytes: []const u8,
-    };
-
-    pub fn init(builder: *Builder) WriteFileStep {
-        return WriteFileStep{
-            .builder = builder,
-            .step = Step.init(.WriteFile, "writefile", builder.allocator, make),
-            .files = .{},
-            .output_dir = undefined,
-        };
-    }
-
-    pub fn add(self: *WriteFileStep, basename: []const u8, bytes: []const u8) void {
-        const node = self.builder.allocator.create(std.TailQueue(File).Node) catch unreachable;
-        node.* = .{
-            .data = .{
-                .source = build.GeneratedFile{
-                    .step = &self.step,
-                    .getPathFn = getFilePath,
-                },
-                .basename = self.builder.dupePath(basename),
-                .bytes = self.builder.dupe(bytes),
-            },
-        };
-
-        self.files.append(node);
-    }
-
-    /// Unless setOutputDir was called, this function must be called only in
-    /// the make step, from a step that has declared a dependency on this one.
-    /// To run an executable built with zig build, use `run`, or create an install step and invoke it.
-    //pub const getOutputPath = @compileError("WriteFileStep.getOutputPath is deprecated! Use getFileSource to retrieve a ");
-    /// Gets a file source for the given basename. If the file does not exist, returns `null`.
-    pub fn getFileSource(step: *WriteFileStep, basename: []const u8) ?build.FileSource {
-        var it = step.files.first;
-        while (it) |node| : (it = node.next) {
-            if (std.mem.eql(u8, node.data.basename, basename))
-                return build.FileSource{ .generated = &node.data.source };
-        }
-        return null;
-    }
-
-    /// Returns the
-    fn getFilePath(source: *const build.GeneratedFile) []const u8 {
-        const file = @fieldParentPtr(File, "source", source);
-        const step = @fieldParentPtr(WriteFileStep, "step", source.step);
-
-        return fs.path.join(
-            step.builder.allocator,
-            &[_][]const u8{ step.output_dir, file.basename },
-        ) catch unreachable;
-    }
-
-    fn make(step: *Step) !void {
-        const self = @fieldParentPtr(WriteFileStep, "step", step);
-
-        // The cache is used here not really as a way to speed things up - because writing
-        // the data to a file would probably be very fast - but as a way to find a canonical
-        // location to put build artifacts.
-
-        // If, for example, a hard-coded path was used as the location to put WriteFileStep
-        // files, then two WriteFileSteps executing in parallel might clobber each other.
-
-        // TODO port the cache system from stage1 to zig std lib. Until then we use blake2b
-        // directly and construct the path, and no "cache hit" detection happens; the files
-        // are always written.
-        var hash = std.crypto.hash.blake2.Blake2b384.init(.{});
-
-        // Random bytes to make WriteFileStep unique. Refresh this with
-        // new random bytes when WriteFileStep implementation is modified
-        // in a non-backwards-compatible way.
-        hash.update("eagVR1dYXoE7ARDP");
-        {
-            var it = self.files.first;
-            while (it) |node| : (it = node.next) {
-                hash.update(node.data.basename);
-                hash.update(node.data.bytes);
-                hash.update("|");
-            }
-        }
-        var digest: [48]u8 = undefined;
-        hash.final(&digest);
-        var hash_basename: [64]u8 = undefined;
-        _ = fs.base64_encoder.encode(&hash_basename, &digest);
-        self.output_dir = try fs.path.join(self.builder.allocator, &[_][]const u8{
-            self.builder.cache_root,
-            "o",
-            &hash_basename,
-        });
-        // TODO replace with something like fs.makePathAndOpenDir
-        fs.cwd().makePath(self.output_dir) catch |err| {
-            warn("unable to make path {s}: {s}\n", .{ self.output_dir, @errorName(err) });
-            return err;
-        };
-        var dir = try fs.cwd().openDir(self.output_dir, .{});
-        defer dir.close();
-        {
-            var it = self.files.first;
-            while (it) |node| : (it = node.next) {
-                dir.writeFile(node.data.basename, node.data.bytes) catch |err| {
-                    warn("unable to write {s} into {s}: {s}\n", .{
-                        node.data.basename,
-                        self.output_dir,
-                        @errorName(err),
-                    });
-                    return err;
-                };
-            }
-        }
-    }
-};
lib/std/build/WriteFileStep.zig
@@ -0,0 +1,132 @@
+// SPDX-License-Identifier: MIT
+// Copyright (c) 2015-2021 Zig Contributors
+// This file is part of [zig](https://ziglang.org/), which is MIT licensed.
+// The MIT license requires this copyright notice to be included in all copies
+// and substantial portions of the software.
+const std = @import("../std.zig");
+const build = @import("../build.zig");
+const Step = build.Step;
+const Builder = build.Builder;
+const fs = std.fs;
+const warn = std.debug.warn;
+const ArrayList = std.ArrayList;
+
+const WriteFileStep = @This();
+
+step: Step,
+builder: *Builder,
+output_dir: []const u8,
+files: std.TailQueue(File),
+
+pub const File = struct {
+    source: build.GeneratedFile,
+    basename: []const u8,
+    bytes: []const u8,
+};
+
+pub fn init(builder: *Builder) WriteFileStep {
+    return WriteFileStep{
+        .builder = builder,
+        .step = Step.init(.WriteFile, "writefile", builder.allocator, make),
+        .files = .{},
+        .output_dir = undefined,
+    };
+}
+
+pub fn add(self: *WriteFileStep, basename: []const u8, bytes: []const u8) void {
+    const node = self.builder.allocator.create(std.TailQueue(File).Node) catch unreachable;
+    node.* = .{
+        .data = .{
+            .source = build.GeneratedFile{
+                .step = &self.step,
+                .getPathFn = getFilePath,
+            },
+            .basename = self.builder.dupePath(basename),
+            .bytes = self.builder.dupe(bytes),
+        },
+    };
+
+    self.files.append(node);
+}
+/// Unless setOutputDir was called, this function must be called only in
+/// the make step, from a step that has declared a dependency on this one.
+/// To run an executable built with zig build, use `run`, or create an install step and invoke it.
+//pub const getOutputPath = @compileError("WriteFileStep.getOutputPath is deprecated! Use getFileSource to retrieve a ");
+/// Gets a file source for the given basename. If the file does not exist, returns `null`.
+pub fn getFileSource(step: *WriteFileStep, basename: []const u8) ?build.FileSource {
+    var it = step.files.first;
+    while (it) |node| : (it = node.next) {
+        if (std.mem.eql(u8, node.data.basename, basename))
+            return build.FileSource{ .generated = &node.data.source };
+    }
+    return null;
+}
+
+/// Returns the
+fn getFilePath(source: *const build.GeneratedFile) []const u8 {
+    const file = @fieldParentPtr(File, "source", source);
+    const step = @fieldParentPtr(WriteFileStep, "step", source.step);
+
+    return fs.path.join(
+        step.builder.allocator,
+        &[_][]const u8{ step.output_dir, file.basename },
+    ) catch unreachable;
+}
+
+fn make(step: *Step) !void {
+    const self = @fieldParentPtr(WriteFileStep, "step", step);
+
+    // The cache is used here not really as a way to speed things up - because writing
+    // the data to a file would probably be very fast - but as a way to find a canonical
+    // location to put build artifacts.
+
+    // If, for example, a hard-coded path was used as the location to put WriteFileStep
+    // files, then two WriteFileSteps executing in parallel might clobber each other.
+
+    // TODO port the cache system from stage1 to zig std lib. Until then we use blake2b
+    // directly and construct the path, and no "cache hit" detection happens; the files
+    // are always written.
+    var hash = std.crypto.hash.blake2.Blake2b384.init(.{});
+
+    // Random bytes to make WriteFileStep unique. Refresh this with
+    // new random bytes when WriteFileStep implementation is modified
+    // in a non-backwards-compatible way.
+    hash.update("eagVR1dYXoE7ARDP");
+    {
+        var it = self.files.first;
+        while (it) |node| : (it = node.next) {
+            hash.update(node.data.basename);
+            hash.update(node.data.bytes);
+            hash.update("|");
+        }
+    }
+    var digest: [48]u8 = undefined;
+    hash.final(&digest);
+    var hash_basename: [64]u8 = undefined;
+    _ = fs.base64_encoder.encode(&hash_basename, &digest);
+    self.output_dir = try fs.path.join(self.builder.allocator, &[_][]const u8{
+        self.builder.cache_root,
+        "o",
+        &hash_basename,
+    });
+    // TODO replace with something like fs.makePathAndOpenDir
+    fs.cwd().makePath(self.output_dir) catch |err| {
+        warn("unable to make path {s}: {s}\n", .{ self.output_dir, @errorName(err) });
+        return err;
+    };
+    var dir = try fs.cwd().openDir(self.output_dir, .{});
+    defer dir.close();
+    {
+        var it = self.files.first;
+        while (it) |node| : (it = node.next) {
+            dir.writeFile(node.data.basename, node.data.bytes) catch |err| {
+                warn("unable to write {s} into {s}: {s}\n", .{
+                    node.data.basename,
+                    self.output_dir,
+                    @errorName(err),
+                });
+                return err;
+            };
+        }
+    }
+}
lib/std/build.zig
@@ -22,12 +22,12 @@ const fmt_lib = std.fmt;
 const File = std.fs.File;
 const CrossTarget = std.zig.CrossTarget;
 
-pub const FmtStep = @import("build/fmt.zig").FmtStep;
-pub const TranslateCStep = @import("build/translate_c.zig").TranslateCStep;
-pub const WriteFileStep = @import("build/write_file.zig").WriteFileStep;
-pub const RunStep = @import("build/run.zig").RunStep;
-pub const CheckFileStep = @import("build/check_file.zig").CheckFileStep;
-pub const InstallRawStep = @import("build/emit_raw.zig").InstallRawStep;
+pub const FmtStep = @import("build/FmtStep.zig");
+pub const TranslateCStep = @import("build/TranslateCStep.zig");
+pub const WriteFileStep = @import("build/WriteFileStep.zig");
+pub const RunStep = @import("build/RunStep.zig");
+pub const CheckFileStep = @import("build/CheckFileStep.zig");
+pub const InstallRawStep = @import("build/InstallRawStep.zig");
 
 pub const Builder = struct {
     install_tls: TopLevelStep,