From 56cb0b5ca0ffc4fc2c54b9dfb0f15fb7c50dc840 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20=28xq=29=20Quei=C3=9Fner?= Date: Mon, 22 Feb 2021 23:46:29 +0100 Subject: [PATCH] Moves files to file-global struct layout. --- lib/std/build.zig | 12 +- lib/std/build/CheckFileStep.zig | 57 ++++ lib/std/build/FmtStep.zig | 40 +++ .../{emit_raw.zig => InstallRawStep.zig} | 76 ++--- lib/std/build/RunStep.zig | 322 ++++++++++++++++++ lib/std/build/TranslateCStep.zig | 109 ++++++ lib/std/build/WriteFileStep.zig | 132 +++++++ lib/std/build/check_file.zig | 57 ---- lib/std/build/fmt.zig | 40 --- lib/std/build/translate_c.zig | 109 ------ lib/std/build/write_file.zig | 133 -------- 11 files changed, 703 insertions(+), 384 deletions(-) create mode 100644 lib/std/build/CheckFileStep.zig create mode 100644 lib/std/build/FmtStep.zig rename lib/std/build/{emit_raw.zig => InstallRawStep.zig} (79%) create mode 100644 lib/std/build/RunStep.zig create mode 100644 lib/std/build/TranslateCStep.zig create mode 100644 lib/std/build/WriteFileStep.zig delete mode 100644 lib/std/build/check_file.zig delete mode 100644 lib/std/build/fmt.zig delete mode 100644 lib/std/build/translate_c.zig delete mode 100644 lib/std/build/write_file.zig diff --git a/lib/std/build.zig b/lib/std/build.zig index eecf577525..80deb84f37 100644 --- a/lib/std/build.zig +++ b/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, diff --git a/lib/std/build/CheckFileStep.zig b/lib/std/build/CheckFileStep.zig new file mode 100644 index 0000000000..5422439472 --- /dev/null +++ b/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; + } + } +} diff --git a/lib/std/build/FmtStep.zig b/lib/std/build/FmtStep.zig new file mode 100644 index 0000000000..62cbd2b286 --- /dev/null +++ b/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); +} diff --git a/lib/std/build/emit_raw.zig b/lib/std/build/InstallRawStep.zig similarity index 79% rename from lib/std/build/emit_raw.zig rename to lib/std/build/InstallRawStep.zig index 0932e117fe..96fb374bc6 100644 --- a/lib/std/build/emit_raw.zig +++ b/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 InstallRawStep = @This(); - const Self = @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) *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); +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; + builder.pushInstalledFile(self.dest_dir, dest_filename); + return self; +} + +fn make(step: *Step) !void { + const self = @fieldParentPtr(InstallRawStep, "step", step); + const builder = self.builder; + + 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); diff --git a/lib/std/build/RunStep.zig b/lib/std/build/RunStep.zig new file mode 100644 index 0000000000..b950c1e40a --- /dev/null +++ b/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 => {}, + } + } +} diff --git a/lib/std/build/TranslateCStep.zig b/lib/std/build/TranslateCStep.zig new file mode 100644 index 0000000000..49a4070276 --- /dev/null +++ b/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).?; + } +} diff --git a/lib/std/build/WriteFileStep.zig b/lib/std/build/WriteFileStep.zig new file mode 100644 index 0000000000..a33386a4b9 --- /dev/null +++ b/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; + }; + } + } +} diff --git a/lib/std/build/check_file.zig b/lib/std/build/check_file.zig deleted file mode 100644 index 28c98547b7..0000000000 --- a/lib/std/build/check_file.zig +++ /dev/null @@ -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; - } - } - } -}; diff --git a/lib/std/build/fmt.zig b/lib/std/build/fmt.zig deleted file mode 100644 index 069cd348bc..0000000000 --- a/lib/std/build/fmt.zig +++ /dev/null @@ -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); - } -}; diff --git a/lib/std/build/translate_c.zig b/lib/std/build/translate_c.zig deleted file mode 100644 index 2ad6d9a9b9..0000000000 --- a/lib/std/build/translate_c.zig +++ /dev/null @@ -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).?; - } - } -}; diff --git a/lib/std/build/write_file.zig b/lib/std/build/write_file.zig deleted file mode 100644 index 438dffef25..0000000000 --- a/lib/std/build/write_file.zig +++ /dev/null @@ -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; - }; - } - } - } -};