zig

fork of https://codeberg.org/ziglang/zig
Log | Files | Refs | README | LICENSE

commit 56cb0b5ca0ffc4fc2c54b9dfb0f15fb7c50dc840 (tree)
parent 8501bb04ada0a29b66ba2d87ec956a4cdff46cee
Author: Felix (xq) Queißner <git@mq32.de>
Date:   Mon, 22 Feb 2021 23:46:29 +0100

Moves files to file-global struct layout.

Diffstat:
Mlib/std/build.zig | 12++++++------
Alib/std/build/CheckFileStep.zig | 57+++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Alib/std/build/FmtStep.zig | 40++++++++++++++++++++++++++++++++++++++++
Alib/std/build/InstallRawStep.zig | 226+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Alib/std/build/RunStep.zig | 322+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Alib/std/build/TranslateCStep.zig | 109+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Alib/std/build/WriteFileStep.zig | 132+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Dlib/std/build/check_file.zig | 57---------------------------------------------------------
Dlib/std/build/emit_raw.zig | 228-------------------------------------------------------------------------------
Dlib/std/build/fmt.zig | 40----------------------------------------
Dlib/std/build/translate_c.zig | 109-------------------------------------------------------------------------------
Dlib/std/build/write_file.zig | 133-------------------------------------------------------------------------------
12 files changed, 892 insertions(+), 573 deletions(-)

diff --git 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 @@ -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 @@ -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/InstallRawStep.zig b/lib/std/build/InstallRawStep.zig @@ -0,0 +1,226 @@ +// 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"); + +const Allocator = std.mem.Allocator; +const ArenaAllocator = std.heap.ArenaAllocator; +const ArrayList = std.ArrayList; +const Builder = std.build.Builder; +const File = std.fs.File; +const InstallDir = std.build.InstallDir; +const LibExeObjStep = std.build.LibExeObjStep; +const Step = std.build.Step; +const elf = std.elf; +const fs = std.fs; +const io = std.io; +const sort = std.sort; +const warn = std.debug.warn; + +const BinaryElfSection = struct { + elfOffset: u64, + binaryOffset: u64, + fileSize: usize, + segment: ?*BinaryElfSegment, +}; + +const BinaryElfSegment = struct { + physicalAddress: u64, + virtualAddress: u64, + elfOffset: u64, + binaryOffset: u64, + fileSize: usize, + firstSection: ?*BinaryElfSection, +}; + +const BinaryElfOutput = struct { + segments: ArrayList(*BinaryElfSegment), + sections: ArrayList(*BinaryElfSection), + + const Self = @This(); + + pub fn deinit(self: *Self) void { + self.sections.deinit(); + self.segments.deinit(); + } + + pub fn parse(allocator: *Allocator, elf_file: File) !Self { + var self: Self = .{ + .segments = ArrayList(*BinaryElfSegment).init(allocator), + .sections = ArrayList(*BinaryElfSection).init(allocator), + }; + const elf_hdr = try std.elf.Header.read(&elf_file); + + var section_headers = elf_hdr.section_header_iterator(&elf_file); + while (try section_headers.next()) |section| { + if (sectionValidForOutput(section)) { + const newSection = try allocator.create(BinaryElfSection); + + newSection.binaryOffset = 0; + newSection.elfOffset = section.sh_offset; + newSection.fileSize = @intCast(usize, section.sh_size); + newSection.segment = null; + + try self.sections.append(newSection); + } + } + + var program_headers = elf_hdr.program_header_iterator(&elf_file); + while (try program_headers.next()) |phdr| { + if (phdr.p_type == elf.PT_LOAD) { + const newSegment = try allocator.create(BinaryElfSegment); + + newSegment.physicalAddress = if (phdr.p_paddr != 0) phdr.p_paddr else phdr.p_vaddr; + newSegment.virtualAddress = phdr.p_vaddr; + newSegment.fileSize = @intCast(usize, phdr.p_filesz); + newSegment.elfOffset = phdr.p_offset; + newSegment.binaryOffset = 0; + newSegment.firstSection = null; + + for (self.sections.items) |section| { + if (sectionWithinSegment(section, phdr)) { + if (section.segment) |sectionSegment| { + if (sectionSegment.elfOffset > newSegment.elfOffset) { + section.segment = newSegment; + } + } else { + section.segment = newSegment; + } + + if (newSegment.firstSection == null) { + newSegment.firstSection = section; + } + } + } + + try self.segments.append(newSegment); + } + } + + sort.sort(*BinaryElfSegment, self.segments.items, {}, segmentSortCompare); + + if (self.segments.items.len > 0) { + const firstSegment = self.segments.items[0]; + if (firstSegment.firstSection) |firstSection| { + const diff = firstSection.elfOffset - firstSegment.elfOffset; + + firstSegment.elfOffset += diff; + firstSegment.fileSize += diff; + firstSegment.physicalAddress += diff; + + const basePhysicalAddress = firstSegment.physicalAddress; + + for (self.segments.items) |segment| { + segment.binaryOffset = segment.physicalAddress - basePhysicalAddress; + } + } + } + + for (self.sections.items) |section| { + if (section.segment) |segment| { + section.binaryOffset = segment.binaryOffset + (section.elfOffset - segment.elfOffset); + } + } + + sort.sort(*BinaryElfSection, self.sections.items, {}, sectionSortCompare); + + return self; + } + + fn sectionWithinSegment(section: *BinaryElfSection, segment: elf.Elf64_Phdr) bool { + return segment.p_offset <= section.elfOffset and (segment.p_offset + segment.p_filesz) >= (section.elfOffset + section.fileSize); + } + + fn sectionValidForOutput(shdr: anytype) bool { + return shdr.sh_size > 0 and shdr.sh_type != elf.SHT_NOBITS and + ((shdr.sh_flags & elf.SHF_ALLOC) == elf.SHF_ALLOC); + } + + fn segmentSortCompare(context: void, left: *BinaryElfSegment, right: *BinaryElfSegment) bool { + if (left.physicalAddress < right.physicalAddress) { + return true; + } + if (left.physicalAddress > right.physicalAddress) { + return false; + } + return false; + } + + fn sectionSortCompare(context: void, left: *BinaryElfSection, right: *BinaryElfSection) bool { + return left.binaryOffset < right.binaryOffset; + } +}; + +fn writeBinaryElfSection(elf_file: File, out_file: File, section: *BinaryElfSection) !void { + try out_file.seekTo(section.binaryOffset); + + try out_file.writeFileAll(elf_file, .{ + .in_offset = section.elfOffset, + .in_len = section.fileSize, + }); +} + +fn emitRaw(allocator: *Allocator, elf_path: []const u8, raw_path: []const u8) !void { + var elf_file = try fs.cwd().openFile(elf_path, .{}); + defer elf_file.close(); + + var out_file = try fs.cwd().createFile(raw_path, .{}); + defer out_file.close(); + + var binary_elf_output = try BinaryElfOutput.parse(allocator, elf_file); + defer binary_elf_output.deinit(); + + for (binary_elf_output.sections.items) |section| { + try writeBinaryElfSection(elf_file, out_file, section); + } +} + +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; +} + +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; + } + + 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); +} + +test { + std.testing.refAllDecls(InstallRawStep); +} diff --git a/lib/std/build/RunStep.zig 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 @@ -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 @@ -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 @@ -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/emit_raw.zig b/lib/std/build/emit_raw.zig @@ -1,228 +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"); - -const Allocator = std.mem.Allocator; -const ArenaAllocator = std.heap.ArenaAllocator; -const ArrayList = std.ArrayList; -const Builder = std.build.Builder; -const File = std.fs.File; -const InstallDir = std.build.InstallDir; -const LibExeObjStep = std.build.LibExeObjStep; -const Step = std.build.Step; -const elf = std.elf; -const fs = std.fs; -const io = std.io; -const sort = std.sort; -const warn = std.debug.warn; - -const BinaryElfSection = struct { - elfOffset: u64, - binaryOffset: u64, - fileSize: usize, - segment: ?*BinaryElfSegment, -}; - -const BinaryElfSegment = struct { - physicalAddress: u64, - virtualAddress: u64, - elfOffset: u64, - binaryOffset: u64, - fileSize: usize, - firstSection: ?*BinaryElfSection, -}; - -const BinaryElfOutput = struct { - segments: ArrayList(*BinaryElfSegment), - sections: ArrayList(*BinaryElfSection), - - const Self = @This(); - - pub fn deinit(self: *Self) void { - self.sections.deinit(); - self.segments.deinit(); - } - - pub fn parse(allocator: *Allocator, elf_file: File) !Self { - var self: Self = .{ - .segments = ArrayList(*BinaryElfSegment).init(allocator), - .sections = ArrayList(*BinaryElfSection).init(allocator), - }; - const elf_hdr = try std.elf.Header.read(&elf_file); - - var section_headers = elf_hdr.section_header_iterator(&elf_file); - while (try section_headers.next()) |section| { - if (sectionValidForOutput(section)) { - const newSection = try allocator.create(BinaryElfSection); - - newSection.binaryOffset = 0; - newSection.elfOffset = section.sh_offset; - newSection.fileSize = @intCast(usize, section.sh_size); - newSection.segment = null; - - try self.sections.append(newSection); - } - } - - var program_headers = elf_hdr.program_header_iterator(&elf_file); - while (try program_headers.next()) |phdr| { - if (phdr.p_type == elf.PT_LOAD) { - const newSegment = try allocator.create(BinaryElfSegment); - - newSegment.physicalAddress = if (phdr.p_paddr != 0) phdr.p_paddr else phdr.p_vaddr; - newSegment.virtualAddress = phdr.p_vaddr; - newSegment.fileSize = @intCast(usize, phdr.p_filesz); - newSegment.elfOffset = phdr.p_offset; - newSegment.binaryOffset = 0; - newSegment.firstSection = null; - - for (self.sections.items) |section| { - if (sectionWithinSegment(section, phdr)) { - if (section.segment) |sectionSegment| { - if (sectionSegment.elfOffset > newSegment.elfOffset) { - section.segment = newSegment; - } - } else { - section.segment = newSegment; - } - - if (newSegment.firstSection == null) { - newSegment.firstSection = section; - } - } - } - - try self.segments.append(newSegment); - } - } - - sort.sort(*BinaryElfSegment, self.segments.items, {}, segmentSortCompare); - - if (self.segments.items.len > 0) { - const firstSegment = self.segments.items[0]; - if (firstSegment.firstSection) |firstSection| { - const diff = firstSection.elfOffset - firstSegment.elfOffset; - - firstSegment.elfOffset += diff; - firstSegment.fileSize += diff; - firstSegment.physicalAddress += diff; - - const basePhysicalAddress = firstSegment.physicalAddress; - - for (self.segments.items) |segment| { - segment.binaryOffset = segment.physicalAddress - basePhysicalAddress; - } - } - } - - for (self.sections.items) |section| { - if (section.segment) |segment| { - section.binaryOffset = segment.binaryOffset + (section.elfOffset - segment.elfOffset); - } - } - - sort.sort(*BinaryElfSection, self.sections.items, {}, sectionSortCompare); - - return self; - } - - fn sectionWithinSegment(section: *BinaryElfSection, segment: elf.Elf64_Phdr) bool { - return segment.p_offset <= section.elfOffset and (segment.p_offset + segment.p_filesz) >= (section.elfOffset + section.fileSize); - } - - fn sectionValidForOutput(shdr: anytype) bool { - return shdr.sh_size > 0 and shdr.sh_type != elf.SHT_NOBITS and - ((shdr.sh_flags & elf.SHF_ALLOC) == elf.SHF_ALLOC); - } - - fn segmentSortCompare(context: void, left: *BinaryElfSegment, right: *BinaryElfSegment) bool { - if (left.physicalAddress < right.physicalAddress) { - return true; - } - if (left.physicalAddress > right.physicalAddress) { - return false; - } - return false; - } - - fn sectionSortCompare(context: void, left: *BinaryElfSection, right: *BinaryElfSection) bool { - return left.binaryOffset < right.binaryOffset; - } -}; - -fn writeBinaryElfSection(elf_file: File, out_file: File, section: *BinaryElfSection) !void { - try out_file.seekTo(section.binaryOffset); - - try out_file.writeFileAll(elf_file, .{ - .in_offset = section.elfOffset, - .in_len = section.fileSize, - }); -} - -fn emitRaw(allocator: *Allocator, elf_path: []const u8, raw_path: []const u8) !void { - var elf_file = try fs.cwd().openFile(elf_path, .{}); - defer elf_file.close(); - - var out_file = try fs.cwd().createFile(raw_path, .{}); - defer out_file.close(); - - var binary_elf_output = try BinaryElfOutput.parse(allocator, elf_file); - defer binary_elf_output.deinit(); - - for (binary_elf_output.sections.items) |section| { - try writeBinaryElfSection(elf_file, out_file, section); - } -} - -pub const InstallRawStep = struct { - step: Step, - builder: *Builder, - artifact: *LibExeObjStep, - dest_dir: InstallDir, - dest_filename: []const u8, - - const Self = @This(); - - 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); - - builder.pushInstalledFile(self.dest_dir, dest_filename); - return self; - } - - fn make(step: *Step) !void { - const self = @fieldParentPtr(Self, "step", step); - const builder = self.builder; - - 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); - } -}; - -test { - std.testing.refAllDecls(InstallRawStep); -} diff --git a/lib/std/build/fmt.zig b/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); - } -}; diff --git a/lib/std/build/translate_c.zig b/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).?; - } - } -}; diff --git a/lib/std/build/write_file.zig b/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; - }; - } - } - } -};