| author | |
| committer | |
| log | 56cb0b5ca0ffc4fc2c54b9dfb0f15fb7c50dc840 |
| tree | a9594d810c4a5d8b5aa8a85d8898c6faa65a0a62 |
| parent | 8501bb04ada0a29b66ba2d87ec956a4cdff46cee |
12 files changed, 892 insertions(+), 573 deletions(-)
lib/std/build.zig+6-6| ... | @@ -22,12 +22,12 @@ const fmt_lib = std.fmt; | ... | @@ -22,12 +22,12 @@ const fmt_lib = std.fmt; |
| 22 | const File = std.fs.File; | 22 | const File = std.fs.File; |
| 23 | const CrossTarget = std.zig.CrossTarget; | 23 | const CrossTarget = std.zig.CrossTarget; |
| 24 | 24 | ||
| 25 | pub const FmtStep = @import("build/fmt.zig").FmtStep; | 25 | pub const FmtStep = @import("build/FmtStep.zig"); |
| 26 | pub const TranslateCStep = @import("build/translate_c.zig").TranslateCStep; | 26 | pub const TranslateCStep = @import("build/TranslateCStep.zig"); |
| 27 | pub const WriteFileStep = @import("build/write_file.zig").WriteFileStep; | 27 | pub const WriteFileStep = @import("build/WriteFileStep.zig"); |
| 28 | pub const RunStep = @import("build/run.zig").RunStep; | 28 | pub const RunStep = @import("build/RunStep.zig"); |
| 29 | pub const CheckFileStep = @import("build/check_file.zig").CheckFileStep; | 29 | pub const CheckFileStep = @import("build/CheckFileStep.zig"); |
| 30 | pub const InstallRawStep = @import("build/emit_raw.zig").InstallRawStep; | 30 | pub const InstallRawStep = @import("build/InstallRawStep.zig"); |
| 31 | 31 | ||
| 32 | pub const Builder = struct { | 32 | pub const Builder = struct { |
| 33 | install_tls: TopLevelStep, | 33 | install_tls: TopLevelStep, |
lib/std/build/CheckFileStep.zig created+57| ... | @@ -0,0 +1,57 @@ | ||
| 1 | // SPDX-License-Identifier: MIT | ||
| 2 | // Copyright (c) 2015-2021 Zig Contributors | ||
| 3 | // This file is part of [zig](https://ziglang.org/), which is MIT licensed. | ||
| 4 | // The MIT license requires this copyright notice to be included in all copies | ||
| 5 | // and substantial portions of the software. | ||
| 6 | const std = @import("../std.zig"); | ||
| 7 | const build = std.build; | ||
| 8 | const Step = build.Step; | ||
| 9 | const Builder = build.Builder; | ||
| 10 | const fs = std.fs; | ||
| 11 | const mem = std.mem; | ||
| 12 | const warn = std.debug.warn; | ||
| 13 | |||
| 14 | const CheckFileStep = @This(); | ||
| 15 | |||
| 16 | step: Step, | ||
| 17 | builder: *Builder, | ||
| 18 | expected_matches: []const []const u8, | ||
| 19 | source: build.FileSource, | ||
| 20 | max_bytes: usize = 20 * 1024 * 1024, | ||
| 21 | |||
| 22 | pub fn create( | ||
| 23 | builder: *Builder, | ||
| 24 | source: build.FileSource, | ||
| 25 | expected_matches: []const []const u8, | ||
| 26 | ) *CheckFileStep { | ||
| 27 | const self = builder.allocator.create(CheckFileStep) catch unreachable; | ||
| 28 | self.* = CheckFileStep{ | ||
| 29 | .builder = builder, | ||
| 30 | .step = Step.init(.CheckFile, "CheckFile", builder.allocator, make), | ||
| 31 | .source = source.dupe(builder), | ||
| 32 | .expected_matches = builder.dupeStrings(expected_matches), | ||
| 33 | }; | ||
| 34 | self.source.addStepDependencies(&self.step); | ||
| 35 | return self; | ||
| 36 | } | ||
| 37 | |||
| 38 | fn make(step: *Step) !void { | ||
| 39 | const self = @fieldParentPtr(CheckFileStep, "step", step); | ||
| 40 | |||
| 41 | const src_path = self.source.getPath(self.builder); | ||
| 42 | const contents = try fs.cwd().readFileAlloc(self.builder.allocator, src_path, self.max_bytes); | ||
| 43 | |||
| 44 | for (self.expected_matches) |expected_match| { | ||
| 45 | if (mem.indexOf(u8, contents, expected_match) == null) { | ||
| 46 | warn( | ||
| 47 | \\ | ||
| 48 | \\========= Expected to find: =================== | ||
| 49 | \\{s} | ||
| 50 | \\========= But file does not contain it: ======= | ||
| 51 | \\{s} | ||
| 52 | \\ | ||
| 53 | , .{ expected_match, contents }); | ||
| 54 | return error.TestFailed; | ||
| 55 | } | ||
| 56 | } | ||
| 57 | } | ||
lib/std/build/FmtStep.zig created+40| ... | @@ -0,0 +1,40 @@ | ||
| 1 | // SPDX-License-Identifier: MIT | ||
| 2 | // Copyright (c) 2015-2021 Zig Contributors | ||
| 3 | // This file is part of [zig](https://ziglang.org/), which is MIT licensed. | ||
| 4 | // The MIT license requires this copyright notice to be included in all copies | ||
| 5 | // and substantial portions of the software. | ||
| 6 | const std = @import("../std.zig"); | ||
| 7 | const build = @import("../build.zig"); | ||
| 8 | const Step = build.Step; | ||
| 9 | const Builder = build.Builder; | ||
| 10 | const BufMap = std.BufMap; | ||
| 11 | const mem = std.mem; | ||
| 12 | |||
| 13 | const FmtStep = @This(); | ||
| 14 | |||
| 15 | step: Step, | ||
| 16 | builder: *Builder, | ||
| 17 | argv: [][]const u8, | ||
| 18 | |||
| 19 | pub fn create(builder: *Builder, paths: []const []const u8) *FmtStep { | ||
| 20 | const self = builder.allocator.create(FmtStep) catch unreachable; | ||
| 21 | const name = "zig fmt"; | ||
| 22 | self.* = FmtStep{ | ||
| 23 | .step = Step.init(.Fmt, name, builder.allocator, make), | ||
| 24 | .builder = builder, | ||
| 25 | .argv = builder.allocator.alloc([]u8, paths.len + 2) catch unreachable, | ||
| 26 | }; | ||
| 27 | |||
| 28 | self.argv[0] = builder.zig_exe; | ||
| 29 | self.argv[1] = "fmt"; | ||
| 30 | for (paths) |path, i| { | ||
| 31 | self.argv[2 + i] = builder.pathFromRoot(path); | ||
| 32 | } | ||
| 33 | return self; | ||
| 34 | } | ||
| 35 | |||
| 36 | fn make(step: *Step) !void { | ||
| 37 | const self = @fieldParentPtr(FmtStep, "step", step); | ||
| 38 | |||
| 39 | return self.builder.spawnChild(self.argv); | ||
| 40 | } | ||
lib/std/build/InstallRawStep.zig created+226| ... | @@ -0,0 +1,226 @@ | ||
| 1 | // SPDX-License-Identifier: MIT | ||
| 2 | // Copyright (c) 2015-2021 Zig Contributors | ||
| 3 | // This file is part of [zig](https://ziglang.org/), which is MIT licensed. | ||
| 4 | // The MIT license requires this copyright notice to be included in all copies | ||
| 5 | // and substantial portions of the software. | ||
| 6 | const std = @import("std"); | ||
| 7 | |||
| 8 | const Allocator = std.mem.Allocator; | ||
| 9 | const ArenaAllocator = std.heap.ArenaAllocator; | ||
| 10 | const ArrayList = std.ArrayList; | ||
| 11 | const Builder = std.build.Builder; | ||
| 12 | const File = std.fs.File; | ||
| 13 | const InstallDir = std.build.InstallDir; | ||
| 14 | const LibExeObjStep = std.build.LibExeObjStep; | ||
| 15 | const Step = std.build.Step; | ||
| 16 | const elf = std.elf; | ||
| 17 | const fs = std.fs; | ||
| 18 | const io = std.io; | ||
| 19 | const sort = std.sort; | ||
| 20 | const warn = std.debug.warn; | ||
| 21 | |||
| 22 | const BinaryElfSection = struct { | ||
| 23 | elfOffset: u64, | ||
| 24 | binaryOffset: u64, | ||
| 25 | fileSize: usize, | ||
| 26 | segment: ?*BinaryElfSegment, | ||
| 27 | }; | ||
| 28 | |||
| 29 | const BinaryElfSegment = struct { | ||
| 30 | physicalAddress: u64, | ||
| 31 | virtualAddress: u64, | ||
| 32 | elfOffset: u64, | ||
| 33 | binaryOffset: u64, | ||
| 34 | fileSize: usize, | ||
| 35 | firstSection: ?*BinaryElfSection, | ||
| 36 | }; | ||
| 37 | |||
| 38 | const BinaryElfOutput = struct { | ||
| 39 | segments: ArrayList(*BinaryElfSegment), | ||
| 40 | sections: ArrayList(*BinaryElfSection), | ||
| 41 | |||
| 42 | const Self = @This(); | ||
| 43 | |||
| 44 | pub fn deinit(self: *Self) void { | ||
| 45 | self.sections.deinit(); | ||
| 46 | self.segments.deinit(); | ||
| 47 | } | ||
| 48 | |||
| 49 | pub fn parse(allocator: *Allocator, elf_file: File) !Self { | ||
| 50 | var self: Self = .{ | ||
| 51 | .segments = ArrayList(*BinaryElfSegment).init(allocator), | ||
| 52 | .sections = ArrayList(*BinaryElfSection).init(allocator), | ||
| 53 | }; | ||
| 54 | const elf_hdr = try std.elf.Header.read(&elf_file); | ||
| 55 | |||
| 56 | var section_headers = elf_hdr.section_header_iterator(&elf_file); | ||
| 57 | while (try section_headers.next()) |section| { | ||
| 58 | if (sectionValidForOutput(section)) { | ||
| 59 | const newSection = try allocator.create(BinaryElfSection); | ||
| 60 | |||
| 61 | newSection.binaryOffset = 0; | ||
| 62 | newSection.elfOffset = section.sh_offset; | ||
| 63 | newSection.fileSize = @intCast(usize, section.sh_size); | ||
| 64 | newSection.segment = null; | ||
| 65 | |||
| 66 | try self.sections.append(newSection); | ||
| 67 | } | ||
| 68 | } | ||
| 69 | |||
| 70 | var program_headers = elf_hdr.program_header_iterator(&elf_file); | ||
| 71 | while (try program_headers.next()) |phdr| { | ||
| 72 | if (phdr.p_type == elf.PT_LOAD) { | ||
| 73 | const newSegment = try allocator.create(BinaryElfSegment); | ||
| 74 | |||
| 75 | newSegment.physicalAddress = if (phdr.p_paddr != 0) phdr.p_paddr else phdr.p_vaddr; | ||
| 76 | newSegment.virtualAddress = phdr.p_vaddr; | ||
| 77 | newSegment.fileSize = @intCast(usize, phdr.p_filesz); | ||
| 78 | newSegment.elfOffset = phdr.p_offset; | ||
| 79 | newSegment.binaryOffset = 0; | ||
| 80 | newSegment.firstSection = null; | ||
| 81 | |||
| 82 | for (self.sections.items) |section| { | ||
| 83 | if (sectionWithinSegment(section, phdr)) { | ||
| 84 | if (section.segment) |sectionSegment| { | ||
| 85 | if (sectionSegment.elfOffset > newSegment.elfOffset) { | ||
| 86 | section.segment = newSegment; | ||
| 87 | } | ||
| 88 | } else { | ||
| 89 | section.segment = newSegment; | ||
| 90 | } | ||
| 91 | |||
| 92 | if (newSegment.firstSection == null) { | ||
| 93 | newSegment.firstSection = section; | ||
| 94 | } | ||
| 95 | } | ||
| 96 | } | ||
| 97 | |||
| 98 | try self.segments.append(newSegment); | ||
| 99 | } | ||
| 100 | } | ||
| 101 | |||
| 102 | sort.sort(*BinaryElfSegment, self.segments.items, {}, segmentSortCompare); | ||
| 103 | |||
| 104 | if (self.segments.items.len > 0) { | ||
| 105 | const firstSegment = self.segments.items[0]; | ||
| 106 | if (firstSegment.firstSection) |firstSection| { | ||
| 107 | const diff = firstSection.elfOffset - firstSegment.elfOffset; | ||
| 108 | |||
| 109 | firstSegment.elfOffset += diff; | ||
| 110 | firstSegment.fileSize += diff; | ||
| 111 | firstSegment.physicalAddress += diff; | ||
| 112 | |||
| 113 | const basePhysicalAddress = firstSegment.physicalAddress; | ||
| 114 | |||
| 115 | for (self.segments.items) |segment| { | ||
| 116 | segment.binaryOffset = segment.physicalAddress - basePhysicalAddress; | ||
| 117 | } | ||
| 118 | } | ||
| 119 | } | ||
| 120 | |||
| 121 | for (self.sections.items) |section| { | ||
| 122 | if (section.segment) |segment| { | ||
| 123 | section.binaryOffset = segment.binaryOffset + (section.elfOffset - segment.elfOffset); | ||
| 124 | } | ||
| 125 | } | ||
| 126 | |||
| 127 | sort.sort(*BinaryElfSection, self.sections.items, {}, sectionSortCompare); | ||
| 128 | |||
| 129 | return self; | ||
| 130 | } | ||
| 131 | |||
| 132 | fn sectionWithinSegment(section: *BinaryElfSection, segment: elf.Elf64_Phdr) bool { | ||
| 133 | return segment.p_offset <= section.elfOffset and (segment.p_offset + segment.p_filesz) >= (section.elfOffset + section.fileSize); | ||
| 134 | } | ||
| 135 | |||
| 136 | fn sectionValidForOutput(shdr: anytype) bool { | ||
| 137 | return shdr.sh_size > 0 and shdr.sh_type != elf.SHT_NOBITS and | ||
| 138 | ((shdr.sh_flags & elf.SHF_ALLOC) == elf.SHF_ALLOC); | ||
| 139 | } | ||
| 140 | |||
| 141 | fn segmentSortCompare(context: void, left: *BinaryElfSegment, right: *BinaryElfSegment) bool { | ||
| 142 | if (left.physicalAddress < right.physicalAddress) { | ||
| 143 | return true; | ||
| 144 | } | ||
| 145 | if (left.physicalAddress > right.physicalAddress) { | ||
| 146 | return false; | ||
| 147 | } | ||
| 148 | return false; | ||
| 149 | } | ||
| 150 | |||
| 151 | fn sectionSortCompare(context: void, left: *BinaryElfSection, right: *BinaryElfSection) bool { | ||
| 152 | return left.binaryOffset < right.binaryOffset; | ||
| 153 | } | ||
| 154 | }; | ||
| 155 | |||
| 156 | fn writeBinaryElfSection(elf_file: File, out_file: File, section: *BinaryElfSection) !void { | ||
| 157 | try out_file.seekTo(section.binaryOffset); | ||
| 158 | |||
| 159 | try out_file.writeFileAll(elf_file, .{ | ||
| 160 | .in_offset = section.elfOffset, | ||
| 161 | .in_len = section.fileSize, | ||
| 162 | }); | ||
| 163 | } | ||
| 164 | |||
| 165 | fn emitRaw(allocator: *Allocator, elf_path: []const u8, raw_path: []const u8) !void { | ||
| 166 | var elf_file = try fs.cwd().openFile(elf_path, .{}); | ||
| 167 | defer elf_file.close(); | ||
| 168 | |||
| 169 | var out_file = try fs.cwd().createFile(raw_path, .{}); | ||
| 170 | defer out_file.close(); | ||
| 171 | |||
| 172 | var binary_elf_output = try BinaryElfOutput.parse(allocator, elf_file); | ||
| 173 | defer binary_elf_output.deinit(); | ||
| 174 | |||
| 175 | for (binary_elf_output.sections.items) |section| { | ||
| 176 | try writeBinaryElfSection(elf_file, out_file, section); | ||
| 177 | } | ||
| 178 | } | ||
| 179 | |||
| 180 | const InstallRawStep = @This(); | ||
| 181 | |||
| 182 | step: Step, | ||
| 183 | builder: *Builder, | ||
| 184 | artifact: *LibExeObjStep, | ||
| 185 | dest_dir: InstallDir, | ||
| 186 | dest_filename: []const u8, | ||
| 187 | |||
| 188 | pub fn create(builder: *Builder, artifact: *LibExeObjStep, dest_filename: []const u8) *InstallRawStep { | ||
| 189 | const self = builder.allocator.create(InstallRawStep) catch unreachable; | ||
| 190 | self.* = InstallRawStep{ | ||
| 191 | .step = Step.init(.InstallRaw, builder.fmt("install raw binary {s}", .{artifact.step.name}), builder.allocator, make), | ||
| 192 | .builder = builder, | ||
| 193 | .artifact = artifact, | ||
| 194 | .dest_dir = switch (artifact.kind) { | ||
| 195 | .Obj => unreachable, | ||
| 196 | .Test => unreachable, | ||
| 197 | .Exe => .Bin, | ||
| 198 | .Lib => unreachable, | ||
| 199 | }, | ||
| 200 | .dest_filename = dest_filename, | ||
| 201 | }; | ||
| 202 | self.step.dependOn(&artifact.step); | ||
| 203 | |||
| 204 | builder.pushInstalledFile(self.dest_dir, dest_filename); | ||
| 205 | return self; | ||
| 206 | } | ||
| 207 | |||
| 208 | fn make(step: *Step) !void { | ||
| 209 | const self = @fieldParentPtr(InstallRawStep, "step", step); | ||
| 210 | const builder = self.builder; | ||
| 211 | |||
| 212 | if (self.artifact.target.getObjectFormat() != .elf) { | ||
| 213 | warn("InstallRawStep only works with ELF format.\n", .{}); | ||
| 214 | return error.InvalidObjectFormat; | ||
| 215 | } | ||
| 216 | |||
| 217 | const full_src_path = self.artifact.getOutputPath(); | ||
| 218 | const full_dest_path = builder.getInstallPath(self.dest_dir, self.dest_filename); | ||
| 219 | |||
| 220 | fs.cwd().makePath(builder.getInstallPath(self.dest_dir, "")) catch unreachable; | ||
| 221 | try emitRaw(builder.allocator, full_src_path, full_dest_path); | ||
| 222 | } | ||
| 223 | |||
| 224 | test { | ||
| 225 | std.testing.refAllDecls(InstallRawStep); | ||
| 226 | } | ||
lib/std/build/RunStep.zig created+322| ... | @@ -0,0 +1,322 @@ | ||
| 1 | // SPDX-License-Identifier: MIT | ||
| 2 | // Copyright (c) 2015-2021 Zig Contributors | ||
| 3 | // This file is part of [zig](https://ziglang.org/), which is MIT licensed. | ||
| 4 | // The MIT license requires this copyright notice to be included in all copies | ||
| 5 | // and substantial portions of the software. | ||
| 6 | const std = @import("../std.zig"); | ||
| 7 | const builtin = std.builtin; | ||
| 8 | const build = std.build; | ||
| 9 | const Step = build.Step; | ||
| 10 | const Builder = build.Builder; | ||
| 11 | const LibExeObjStep = build.LibExeObjStep; | ||
| 12 | const WriteFileStep = build.WriteFileStep; | ||
| 13 | const fs = std.fs; | ||
| 14 | const mem = std.mem; | ||
| 15 | const process = std.process; | ||
| 16 | const ArrayList = std.ArrayList; | ||
| 17 | const BufMap = std.BufMap; | ||
| 18 | const warn = std.debug.warn; | ||
| 19 | |||
| 20 | const max_stdout_size = 1 * 1024 * 1024; // 1 MiB | ||
| 21 | |||
| 22 | const RunStep = @This(); | ||
| 23 | |||
| 24 | step: Step, | ||
| 25 | builder: *Builder, | ||
| 26 | |||
| 27 | /// See also addArg and addArgs to modifying this directly | ||
| 28 | argv: ArrayList(Arg), | ||
| 29 | |||
| 30 | /// Set this to modify the current working directory | ||
| 31 | cwd: ?[]const u8, | ||
| 32 | |||
| 33 | /// Override this field to modify the environment, or use setEnvironmentVariable | ||
| 34 | env_map: ?*BufMap, | ||
| 35 | |||
| 36 | stdout_action: StdIoAction = .inherit, | ||
| 37 | stderr_action: StdIoAction = .inherit, | ||
| 38 | |||
| 39 | stdin_behavior: std.ChildProcess.StdIo = .Inherit, | ||
| 40 | |||
| 41 | expected_exit_code: u8 = 0, | ||
| 42 | |||
| 43 | pub const StdIoAction = union(enum) { | ||
| 44 | inherit, | ||
| 45 | ignore, | ||
| 46 | expect_exact: []const u8, | ||
| 47 | expect_matches: []const []const u8, | ||
| 48 | }; | ||
| 49 | |||
| 50 | pub const Arg = union(enum) { | ||
| 51 | artifact: *LibExeObjStep, | ||
| 52 | file_source: build.FileSource, | ||
| 53 | bytes: []u8, | ||
| 54 | }; | ||
| 55 | |||
| 56 | pub fn create(builder: *Builder, name: []const u8) *RunStep { | ||
| 57 | const self = builder.allocator.create(RunStep) catch unreachable; | ||
| 58 | self.* = RunStep{ | ||
| 59 | .builder = builder, | ||
| 60 | .step = Step.init(.Run, name, builder.allocator, make), | ||
| 61 | .argv = ArrayList(Arg).init(builder.allocator), | ||
| 62 | .cwd = null, | ||
| 63 | .env_map = null, | ||
| 64 | }; | ||
| 65 | return self; | ||
| 66 | } | ||
| 67 | |||
| 68 | pub fn addArtifactArg(self: *RunStep, artifact: *LibExeObjStep) void { | ||
| 69 | self.argv.append(Arg{ .artifact = artifact }) catch unreachable; | ||
| 70 | self.step.dependOn(&artifact.step); | ||
| 71 | } | ||
| 72 | |||
| 73 | pub fn addFileSourceArg(self: *RunStep, file_source: build.FileSource) void { | ||
| 74 | self.argv.append(Arg{ | ||
| 75 | .file_source = file_source.dupe(self.builder), | ||
| 76 | }) catch unreachable; | ||
| 77 | file_source.addStepDependencies(&self.step); | ||
| 78 | } | ||
| 79 | |||
| 80 | pub fn addArg(self: *RunStep, arg: []const u8) void { | ||
| 81 | self.argv.append(Arg{ .bytes = self.builder.dupe(arg) }) catch unreachable; | ||
| 82 | } | ||
| 83 | |||
| 84 | pub fn addArgs(self: *RunStep, args: []const []const u8) void { | ||
| 85 | for (args) |arg| { | ||
| 86 | self.addArg(arg); | ||
| 87 | } | ||
| 88 | } | ||
| 89 | |||
| 90 | pub fn clearEnvironment(self: *RunStep) void { | ||
| 91 | const new_env_map = self.builder.allocator.create(BufMap) catch unreachable; | ||
| 92 | new_env_map.* = BufMap.init(self.builder.allocator); | ||
| 93 | self.env_map = new_env_map; | ||
| 94 | } | ||
| 95 | |||
| 96 | pub fn addPathDir(self: *RunStep, search_path: []const u8) void { | ||
| 97 | const env_map = self.getEnvMap(); | ||
| 98 | |||
| 99 | var key: []const u8 = undefined; | ||
| 100 | var prev_path: ?[]const u8 = undefined; | ||
| 101 | if (builtin.os.tag == .windows) { | ||
| 102 | key = "Path"; | ||
| 103 | prev_path = env_map.get(key); | ||
| 104 | if (prev_path == null) { | ||
| 105 | key = "PATH"; | ||
| 106 | prev_path = env_map.get(key); | ||
| 107 | } | ||
| 108 | } else { | ||
| 109 | key = "PATH"; | ||
| 110 | prev_path = env_map.get(key); | ||
| 111 | } | ||
| 112 | |||
| 113 | if (prev_path) |pp| { | ||
| 114 | const new_path = self.builder.fmt("{s}" ++ [1]u8{fs.path.delimiter} ++ "{s}", .{ pp, search_path }); | ||
| 115 | env_map.set(key, new_path) catch unreachable; | ||
| 116 | } else { | ||
| 117 | env_map.set(key, self.builder.dupePath(search_path)) catch unreachable; | ||
| 118 | } | ||
| 119 | } | ||
| 120 | |||
| 121 | pub fn getEnvMap(self: *RunStep) *BufMap { | ||
| 122 | return self.env_map orelse { | ||
| 123 | const env_map = self.builder.allocator.create(BufMap) catch unreachable; | ||
| 124 | env_map.* = process.getEnvMap(self.builder.allocator) catch unreachable; | ||
| 125 | self.env_map = env_map; | ||
| 126 | return env_map; | ||
| 127 | }; | ||
| 128 | } | ||
| 129 | |||
| 130 | pub fn setEnvironmentVariable(self: *RunStep, key: []const u8, value: []const u8) void { | ||
| 131 | const env_map = self.getEnvMap(); | ||
| 132 | env_map.set( | ||
| 133 | self.builder.dupe(key), | ||
| 134 | self.builder.dupe(value), | ||
| 135 | ) catch unreachable; | ||
| 136 | } | ||
| 137 | |||
| 138 | pub fn expectStdErrEqual(self: *RunStep, bytes: []const u8) void { | ||
| 139 | self.stderr_action = .{ .expect_exact = self.builder.dupe(bytes) }; | ||
| 140 | } | ||
| 141 | |||
| 142 | pub fn expectStdOutEqual(self: *RunStep, bytes: []const u8) void { | ||
| 143 | self.stdout_action = .{ .expect_exact = self.builder.dupe(bytes) }; | ||
| 144 | } | ||
| 145 | |||
| 146 | fn stdIoActionToBehavior(action: StdIoAction) std.ChildProcess.StdIo { | ||
| 147 | return switch (action) { | ||
| 148 | .ignore => .Ignore, | ||
| 149 | .inherit => .Inherit, | ||
| 150 | .expect_exact, .expect_matches => .Pipe, | ||
| 151 | }; | ||
| 152 | } | ||
| 153 | |||
| 154 | fn make(step: *Step) !void { | ||
| 155 | const self = @fieldParentPtr(RunStep, "step", step); | ||
| 156 | |||
| 157 | const cwd = if (self.cwd) |cwd| self.builder.pathFromRoot(cwd) else self.builder.build_root; | ||
| 158 | |||
| 159 | var argv_list = ArrayList([]const u8).init(self.builder.allocator); | ||
| 160 | for (self.argv.items) |arg| { | ||
| 161 | switch (arg) { | ||
| 162 | .bytes => |bytes| try argv_list.append(bytes), | ||
| 163 | .file_source => |file| try argv_list.append(file.getPath(self.builder)), | ||
| 164 | .artifact => |artifact| { | ||
| 165 | if (artifact.target.isWindows()) { | ||
| 166 | // On Windows we don't have rpaths so we have to add .dll search paths to PATH | ||
| 167 | self.addPathForDynLibs(artifact); | ||
| 168 | } | ||
| 169 | const executable_path = artifact.installed_path orelse artifact.getOutputPath(); | ||
| 170 | try argv_list.append(executable_path); | ||
| 171 | }, | ||
| 172 | } | ||
| 173 | } | ||
| 174 | |||
| 175 | const argv = argv_list.items; | ||
| 176 | |||
| 177 | const child = std.ChildProcess.init(argv, self.builder.allocator) catch unreachable; | ||
| 178 | defer child.deinit(); | ||
| 179 | |||
| 180 | child.cwd = cwd; | ||
| 181 | child.env_map = self.env_map orelse self.builder.env_map; | ||
| 182 | |||
| 183 | child.stdin_behavior = self.stdin_behavior; | ||
| 184 | child.stdout_behavior = stdIoActionToBehavior(self.stdout_action); | ||
| 185 | child.stderr_behavior = stdIoActionToBehavior(self.stderr_action); | ||
| 186 | |||
| 187 | child.spawn() catch |err| { | ||
| 188 | warn("Unable to spawn {s}: {s}\n", .{ argv[0], @errorName(err) }); | ||
| 189 | return err; | ||
| 190 | }; | ||
| 191 | |||
| 192 | // TODO need to poll to read these streams to prevent a deadlock (or rely on evented I/O). | ||
| 193 | |||
| 194 | var stdout: ?[]const u8 = null; | ||
| 195 | defer if (stdout) |s| self.builder.allocator.free(s); | ||
| 196 | |||
| 197 | switch (self.stdout_action) { | ||
| 198 | .expect_exact, .expect_matches => { | ||
| 199 | stdout = child.stdout.?.reader().readAllAlloc(self.builder.allocator, max_stdout_size) catch unreachable; | ||
| 200 | }, | ||
| 201 | .inherit, .ignore => {}, | ||
| 202 | } | ||
| 203 | |||
| 204 | var stderr: ?[]const u8 = null; | ||
| 205 | defer if (stderr) |s| self.builder.allocator.free(s); | ||
| 206 | |||
| 207 | switch (self.stderr_action) { | ||
| 208 | .expect_exact, .expect_matches => { | ||
| 209 | stderr = child.stderr.?.reader().readAllAlloc(self.builder.allocator, max_stdout_size) catch unreachable; | ||
| 210 | }, | ||
| 211 | .inherit, .ignore => {}, | ||
| 212 | } | ||
| 213 | |||
| 214 | const term = child.wait() catch |err| { | ||
| 215 | warn("Unable to spawn {s}: {s}\n", .{ argv[0], @errorName(err) }); | ||
| 216 | return err; | ||
| 217 | }; | ||
| 218 | |||
| 219 | switch (term) { | ||
| 220 | .Exited => |code| { | ||
| 221 | if (code != self.expected_exit_code) { | ||
| 222 | warn("The following command exited with error code {} (expected {}):\n", .{ | ||
| 223 | code, | ||
| 224 | self.expected_exit_code, | ||
| 225 | }); | ||
| 226 | printCmd(cwd, argv); | ||
| 227 | return error.UncleanExit; | ||
| 228 | } | ||
| 229 | }, | ||
| 230 | else => { | ||
| 231 | warn("The following command terminated unexpectedly:\n", .{}); | ||
| 232 | printCmd(cwd, argv); | ||
| 233 | return error.UncleanExit; | ||
| 234 | }, | ||
| 235 | } | ||
| 236 | |||
| 237 | switch (self.stderr_action) { | ||
| 238 | .inherit, .ignore => {}, | ||
| 239 | .expect_exact => |expected_bytes| { | ||
| 240 | if (!mem.eql(u8, expected_bytes, stderr.?)) { | ||
| 241 | warn( | ||
| 242 | \\ | ||
| 243 | \\========= Expected this stderr: ========= | ||
| 244 | \\{s} | ||
| 245 | \\========= But found: ==================== | ||
| 246 | \\{s} | ||
| 247 | \\ | ||
| 248 | , .{ expected_bytes, stderr.? }); | ||
| 249 | printCmd(cwd, argv); | ||
| 250 | return error.TestFailed; | ||
| 251 | } | ||
| 252 | }, | ||
| 253 | .expect_matches => |matches| for (matches) |match| { | ||
| 254 | if (mem.indexOf(u8, stderr.?, match) == null) { | ||
| 255 | warn( | ||
| 256 | \\ | ||
| 257 | \\========= Expected to find in stderr: ========= | ||
| 258 | \\{s} | ||
| 259 | \\========= But stderr does not contain it: ===== | ||
| 260 | \\{s} | ||
| 261 | \\ | ||
| 262 | , .{ match, stderr.? }); | ||
| 263 | printCmd(cwd, argv); | ||
| 264 | return error.TestFailed; | ||
| 265 | } | ||
| 266 | }, | ||
| 267 | } | ||
| 268 | |||
| 269 | switch (self.stdout_action) { | ||
| 270 | .inherit, .ignore => {}, | ||
| 271 | .expect_exact => |expected_bytes| { | ||
| 272 | if (!mem.eql(u8, expected_bytes, stdout.?)) { | ||
| 273 | warn( | ||
| 274 | \\ | ||
| 275 | \\========= Expected this stdout: ========= | ||
| 276 | \\{s} | ||
| 277 | \\========= But found: ==================== | ||
| 278 | \\{s} | ||
| 279 | \\ | ||
| 280 | , .{ expected_bytes, stdout.? }); | ||
| 281 | printCmd(cwd, argv); | ||
| 282 | return error.TestFailed; | ||
| 283 | } | ||
| 284 | }, | ||
| 285 | .expect_matches => |matches| for (matches) |match| { | ||
| 286 | if (mem.indexOf(u8, stdout.?, match) == null) { | ||
| 287 | warn( | ||
| 288 | \\ | ||
| 289 | \\========= Expected to find in stdout: ========= | ||
| 290 | \\{s} | ||
| 291 | \\========= But stdout does not contain it: ===== | ||
| 292 | \\{s} | ||
| 293 | \\ | ||
| 294 | , .{ match, stdout.? }); | ||
| 295 | printCmd(cwd, argv); | ||
| 296 | return error.TestFailed; | ||
| 297 | } | ||
| 298 | }, | ||
| 299 | } | ||
| 300 | } | ||
| 301 | |||
| 302 | fn printCmd(cwd: ?[]const u8, argv: []const []const u8) void { | ||
| 303 | if (cwd) |yes_cwd| warn("cd {s} && ", .{yes_cwd}); | ||
| 304 | for (argv) |arg| { | ||
| 305 | warn("{s} ", .{arg}); | ||
| 306 | } | ||
| 307 | warn("\n", .{}); | ||
| 308 | } | ||
| 309 | |||
| 310 | fn addPathForDynLibs(self: *RunStep, artifact: *LibExeObjStep) void { | ||
| 311 | for (artifact.link_objects.items) |link_object| { | ||
| 312 | switch (link_object) { | ||
| 313 | .other_step => |other| { | ||
| 314 | if (other.target.isWindows() and other.isDynamicLibrary()) { | ||
| 315 | self.addPathDir(fs.path.dirname(other.getOutputPath()).?); | ||
| 316 | self.addPathForDynLibs(other); | ||
| 317 | } | ||
| 318 | }, | ||
| 319 | else => {}, | ||
| 320 | } | ||
| 321 | } | ||
| 322 | } | ||
lib/std/build/TranslateCStep.zig created+109| ... | @@ -0,0 +1,109 @@ | ||
| 1 | // SPDX-License-Identifier: MIT | ||
| 2 | // Copyright (c) 2015-2021 Zig Contributors | ||
| 3 | // This file is part of [zig](https://ziglang.org/), which is MIT licensed. | ||
| 4 | // The MIT license requires this copyright notice to be included in all copies | ||
| 5 | // and substantial portions of the software. | ||
| 6 | const std = @import("../std.zig"); | ||
| 7 | const build = std.build; | ||
| 8 | const Step = build.Step; | ||
| 9 | const Builder = build.Builder; | ||
| 10 | const LibExeObjStep = build.LibExeObjStep; | ||
| 11 | const CheckFileStep = build.CheckFileStep; | ||
| 12 | const fs = std.fs; | ||
| 13 | const mem = std.mem; | ||
| 14 | const CrossTarget = std.zig.CrossTarget; | ||
| 15 | |||
| 16 | const TranslateCStep = @This(); | ||
| 17 | |||
| 18 | step: Step, | ||
| 19 | builder: *Builder, | ||
| 20 | source: build.FileSource, | ||
| 21 | include_dirs: std.ArrayList([]const u8), | ||
| 22 | output_dir: ?[]const u8, | ||
| 23 | out_basename: []const u8, | ||
| 24 | target: CrossTarget = CrossTarget{}, | ||
| 25 | output_file: build.GeneratedFile, | ||
| 26 | |||
| 27 | pub fn create(builder: *Builder, source: build.FileSource) *TranslateCStep { | ||
| 28 | const self = builder.allocator.create(TranslateCStep) catch unreachable; | ||
| 29 | self.* = TranslateCStep{ | ||
| 30 | .step = Step.init(.TranslateC, "translate-c", builder.allocator, make), | ||
| 31 | .builder = builder, | ||
| 32 | .source = source, | ||
| 33 | .include_dirs = std.ArrayList([]const u8).init(builder.allocator), | ||
| 34 | .output_dir = null, | ||
| 35 | .out_basename = undefined, | ||
| 36 | .output_file = build.GeneratedFile{ | ||
| 37 | .step = &self.step, | ||
| 38 | .getPathFn = getGeneratedFilePath, | ||
| 39 | }, | ||
| 40 | }; | ||
| 41 | source.addStepDependencies(&self.step); | ||
| 42 | return self; | ||
| 43 | } | ||
| 44 | |||
| 45 | fn getGeneratedFilePath(file: *const build.GeneratedFile) []const u8 { | ||
| 46 | const self = @fieldParentPtr(TranslateCStep, "step", file.step); | ||
| 47 | return self.getOutputPath(); | ||
| 48 | } | ||
| 49 | |||
| 50 | /// Unless setOutputDir was called, this function must be called only in | ||
| 51 | /// the make step, from a step that has declared a dependency on this one. | ||
| 52 | /// To run an executable built with zig build, use `run`, or create an install step and invoke it. | ||
| 53 | pub fn getOutputPath(self: *TranslateCStep) []const u8 { | ||
| 54 | return fs.path.join( | ||
| 55 | self.builder.allocator, | ||
| 56 | &[_][]const u8{ self.output_dir.?, self.out_basename }, | ||
| 57 | ) catch unreachable; | ||
| 58 | } | ||
| 59 | |||
| 60 | pub fn setTarget(self: *TranslateCStep, target: CrossTarget) void { | ||
| 61 | self.target = target; | ||
| 62 | } | ||
| 63 | |||
| 64 | /// Creates a step to build an executable from the translated source. | ||
| 65 | pub fn addExecutable(self: *TranslateCStep) *LibExeObjStep { | ||
| 66 | return self.builder.addExecutableSource("translated_c", build.FileSource{ .generated = &self.output_file }, false); | ||
| 67 | } | ||
| 68 | |||
| 69 | pub fn addIncludeDir(self: *TranslateCStep, include_dir: []const u8) void { | ||
| 70 | self.include_dirs.append(self.builder.dupePath(include_dir)) catch unreachable; | ||
| 71 | } | ||
| 72 | |||
| 73 | pub fn addCheckFile(self: *TranslateCStep, expected_matches: []const []const u8) *CheckFileStep { | ||
| 74 | return CheckFileStep.create(self.builder, .{ .generated = &self.output_file }, self.builder.dupeStrings(expected_matches)); | ||
| 75 | } | ||
| 76 | |||
| 77 | fn make(step: *Step) !void { | ||
| 78 | const self = @fieldParentPtr(TranslateCStep, "step", step); | ||
| 79 | |||
| 80 | var argv_list = std.ArrayList([]const u8).init(self.builder.allocator); | ||
| 81 | try argv_list.append(self.builder.zig_exe); | ||
| 82 | try argv_list.append("translate-c"); | ||
| 83 | try argv_list.append("-lc"); | ||
| 84 | |||
| 85 | try argv_list.append("--enable-cache"); | ||
| 86 | |||
| 87 | if (!self.target.isNative()) { | ||
| 88 | try argv_list.append("-target"); | ||
| 89 | try argv_list.append(try self.target.zigTriple(self.builder.allocator)); | ||
| 90 | } | ||
| 91 | |||
| 92 | for (self.include_dirs.items) |include_dir| { | ||
| 93 | try argv_list.append("-I"); | ||
| 94 | try argv_list.append(include_dir); | ||
| 95 | } | ||
| 96 | |||
| 97 | try argv_list.append(self.source.getPath(self.builder)); | ||
| 98 | |||
| 99 | const output_path_nl = try self.builder.execFromStep(argv_list.items, &self.step); | ||
| 100 | const output_path = mem.trimRight(u8, output_path_nl, "\r\n"); | ||
| 101 | |||
| 102 | self.out_basename = fs.path.basename(output_path); | ||
| 103 | if (self.output_dir) |output_dir| { | ||
| 104 | const full_dest = try fs.path.join(self.builder.allocator, &[_][]const u8{ output_dir, self.out_basename }); | ||
| 105 | try self.builder.updateFile(output_path, full_dest); | ||
| 106 | } else { | ||
| 107 | self.output_dir = fs.path.dirname(output_path).?; | ||
| 108 | } | ||
| 109 | } | ||
lib/std/build/WriteFileStep.zig created+132| ... | @@ -0,0 +1,132 @@ | ||
| 1 | // SPDX-License-Identifier: MIT | ||
| 2 | // Copyright (c) 2015-2021 Zig Contributors | ||
| 3 | // This file is part of [zig](https://ziglang.org/), which is MIT licensed. | ||
| 4 | // The MIT license requires this copyright notice to be included in all copies | ||
| 5 | // and substantial portions of the software. | ||
| 6 | const std = @import("../std.zig"); | ||
| 7 | const build = @import("../build.zig"); | ||
| 8 | const Step = build.Step; | ||
| 9 | const Builder = build.Builder; | ||
| 10 | const fs = std.fs; | ||
| 11 | const warn = std.debug.warn; | ||
| 12 | const ArrayList = std.ArrayList; | ||
| 13 | |||
| 14 | const WriteFileStep = @This(); | ||
| 15 | |||
| 16 | step: Step, | ||
| 17 | builder: *Builder, | ||
| 18 | output_dir: []const u8, | ||
| 19 | files: std.TailQueue(File), | ||
| 20 | |||
| 21 | pub const File = struct { | ||
| 22 | source: build.GeneratedFile, | ||
| 23 | basename: []const u8, | ||
| 24 | bytes: []const u8, | ||
| 25 | }; | ||
| 26 | |||
| 27 | pub fn init(builder: *Builder) WriteFileStep { | ||
| 28 | return WriteFileStep{ | ||
| 29 | .builder = builder, | ||
| 30 | .step = Step.init(.WriteFile, "writefile", builder.allocator, make), | ||
| 31 | .files = .{}, | ||
| 32 | .output_dir = undefined, | ||
| 33 | }; | ||
| 34 | } | ||
| 35 | |||
| 36 | pub fn add(self: *WriteFileStep, basename: []const u8, bytes: []const u8) void { | ||
| 37 | const node = self.builder.allocator.create(std.TailQueue(File).Node) catch unreachable; | ||
| 38 | node.* = .{ | ||
| 39 | .data = .{ | ||
| 40 | .source = build.GeneratedFile{ | ||
| 41 | .step = &self.step, | ||
| 42 | .getPathFn = getFilePath, | ||
| 43 | }, | ||
| 44 | .basename = self.builder.dupePath(basename), | ||
| 45 | .bytes = self.builder.dupe(bytes), | ||
| 46 | }, | ||
| 47 | }; | ||
| 48 | |||
| 49 | self.files.append(node); | ||
| 50 | } | ||
| 51 | /// Unless setOutputDir was called, this function must be called only in | ||
| 52 | /// the make step, from a step that has declared a dependency on this one. | ||
| 53 | /// To run an executable built with zig build, use `run`, or create an install step and invoke it. | ||
| 54 | //pub const getOutputPath = @compileError("WriteFileStep.getOutputPath is deprecated! Use getFileSource to retrieve a "); | ||
| 55 | /// Gets a file source for the given basename. If the file does not exist, returns `null`. | ||
| 56 | pub fn getFileSource(step: *WriteFileStep, basename: []const u8) ?build.FileSource { | ||
| 57 | var it = step.files.first; | ||
| 58 | while (it) |node| : (it = node.next) { | ||
| 59 | if (std.mem.eql(u8, node.data.basename, basename)) | ||
| 60 | return build.FileSource{ .generated = &node.data.source }; | ||
| 61 | } | ||
| 62 | return null; | ||
| 63 | } | ||
| 64 | |||
| 65 | /// Returns the | ||
| 66 | fn getFilePath(source: *const build.GeneratedFile) []const u8 { | ||
| 67 | const file = @fieldParentPtr(File, "source", source); | ||
| 68 | const step = @fieldParentPtr(WriteFileStep, "step", source.step); | ||
| 69 | |||
| 70 | return fs.path.join( | ||
| 71 | step.builder.allocator, | ||
| 72 | &[_][]const u8{ step.output_dir, file.basename }, | ||
| 73 | ) catch unreachable; | ||
| 74 | } | ||
| 75 | |||
| 76 | fn make(step: *Step) !void { | ||
| 77 | const self = @fieldParentPtr(WriteFileStep, "step", step); | ||
| 78 | |||
| 79 | // The cache is used here not really as a way to speed things up - because writing | ||
| 80 | // the data to a file would probably be very fast - but as a way to find a canonical | ||
| 81 | // location to put build artifacts. | ||
| 82 | |||
| 83 | // If, for example, a hard-coded path was used as the location to put WriteFileStep | ||
| 84 | // files, then two WriteFileSteps executing in parallel might clobber each other. | ||
| 85 | |||
| 86 | // TODO port the cache system from stage1 to zig std lib. Until then we use blake2b | ||
| 87 | // directly and construct the path, and no "cache hit" detection happens; the files | ||
| 88 | // are always written. | ||
| 89 | var hash = std.crypto.hash.blake2.Blake2b384.init(.{}); | ||
| 90 | |||
| 91 | // Random bytes to make WriteFileStep unique. Refresh this with | ||
| 92 | // new random bytes when WriteFileStep implementation is modified | ||
| 93 | // in a non-backwards-compatible way. | ||
| 94 | hash.update("eagVR1dYXoE7ARDP"); | ||
| 95 | { | ||
| 96 | var it = self.files.first; | ||
| 97 | while (it) |node| : (it = node.next) { | ||
| 98 | hash.update(node.data.basename); | ||
| 99 | hash.update(node.data.bytes); | ||
| 100 | hash.update("|"); | ||
| 101 | } | ||
| 102 | } | ||
| 103 | var digest: [48]u8 = undefined; | ||
| 104 | hash.final(&digest); | ||
| 105 | var hash_basename: [64]u8 = undefined; | ||
| 106 | _ = fs.base64_encoder.encode(&hash_basename, &digest); | ||
| 107 | self.output_dir = try fs.path.join(self.builder.allocator, &[_][]const u8{ | ||
| 108 | self.builder.cache_root, | ||
| 109 | "o", | ||
| 110 | &hash_basename, | ||
| 111 | }); | ||
| 112 | // TODO replace with something like fs.makePathAndOpenDir | ||
| 113 | fs.cwd().makePath(self.output_dir) catch |err| { | ||
| 114 | warn("unable to make path {s}: {s}\n", .{ self.output_dir, @errorName(err) }); | ||
| 115 | return err; | ||
| 116 | }; | ||
| 117 | var dir = try fs.cwd().openDir(self.output_dir, .{}); | ||
| 118 | defer dir.close(); | ||
| 119 | { | ||
| 120 | var it = self.files.first; | ||
| 121 | while (it) |node| : (it = node.next) { | ||
| 122 | dir.writeFile(node.data.basename, node.data.bytes) catch |err| { | ||
| 123 | warn("unable to write {s} into {s}: {s}\n", .{ | ||
| 124 | node.data.basename, | ||
| 125 | self.output_dir, | ||
| 126 | @errorName(err), | ||
| 127 | }); | ||
| 128 | return err; | ||
| 129 | }; | ||
| 130 | } | ||
| 131 | } | ||
| 132 | } | ||
lib/std/build/check_file.zig deleted-57| ... | @@ -1,57 +0,0 @@ | ||
| 1 | // SPDX-License-Identifier: MIT | ||
| 2 | // Copyright (c) 2015-2021 Zig Contributors | ||
| 3 | // This file is part of [zig](https://ziglang.org/), which is MIT licensed. | ||
| 4 | // The MIT license requires this copyright notice to be included in all copies | ||
| 5 | // and substantial portions of the software. | ||
| 6 | const std = @import("../std.zig"); | ||
| 7 | const build = std.build; | ||
| 8 | const Step = build.Step; | ||
| 9 | const Builder = build.Builder; | ||
| 10 | const fs = std.fs; | ||
| 11 | const mem = std.mem; | ||
| 12 | const warn = std.debug.warn; | ||
| 13 | |||
| 14 | pub const CheckFileStep = struct { | ||
| 15 | step: Step, | ||
| 16 | builder: *Builder, | ||
| 17 | expected_matches: []const []const u8, | ||
| 18 | source: build.FileSource, | ||
| 19 | max_bytes: usize = 20 * 1024 * 1024, | ||
| 20 | |||
| 21 | pub fn create( | ||
| 22 | builder: *Builder, | ||
| 23 | source: build.FileSource, | ||
| 24 | expected_matches: []const []const u8, | ||
| 25 | ) *CheckFileStep { | ||
| 26 | const self = builder.allocator.create(CheckFileStep) catch unreachable; | ||
| 27 | self.* = CheckFileStep{ | ||
| 28 | .builder = builder, | ||
| 29 | .step = Step.init(.CheckFile, "CheckFile", builder.allocator, make), | ||
| 30 | .source = source.dupe(builder), | ||
| 31 | .expected_matches = builder.dupeStrings(expected_matches), | ||
| 32 | }; | ||
| 33 | self.source.addStepDependencies(&self.step); | ||
| 34 | return self; | ||
| 35 | } | ||
| 36 | |||
| 37 | fn make(step: *Step) !void { | ||
| 38 | const self = @fieldParentPtr(CheckFileStep, "step", step); | ||
| 39 | |||
| 40 | const src_path = self.source.getPath(self.builder); | ||
| 41 | const contents = try fs.cwd().readFileAlloc(self.builder.allocator, src_path, self.max_bytes); | ||
| 42 | |||
| 43 | for (self.expected_matches) |expected_match| { | ||
| 44 | if (mem.indexOf(u8, contents, expected_match) == null) { | ||
| 45 | warn( | ||
| 46 | \\ | ||
| 47 | \\========= Expected to find: =================== | ||
| 48 | \\{s} | ||
| 49 | \\========= But file does not contain it: ======= | ||
| 50 | \\{s} | ||
| 51 | \\ | ||
| 52 | , .{ expected_match, contents }); | ||
| 53 | return error.TestFailed; | ||
| 54 | } | ||
| 55 | } | ||
| 56 | } | ||
| 57 | }; | ||
lib/std/build/emit_raw.zig deleted-228| ... | @@ -1,228 +0,0 @@ | ||
| 1 | // SPDX-License-Identifier: MIT | ||
| 2 | // Copyright (c) 2015-2021 Zig Contributors | ||
| 3 | // This file is part of [zig](https://ziglang.org/), which is MIT licensed. | ||
| 4 | // The MIT license requires this copyright notice to be included in all copies | ||
| 5 | // and substantial portions of the software. | ||
| 6 | const std = @import("std"); | ||
| 7 | |||
| 8 | const Allocator = std.mem.Allocator; | ||
| 9 | const ArenaAllocator = std.heap.ArenaAllocator; | ||
| 10 | const ArrayList = std.ArrayList; | ||
| 11 | const Builder = std.build.Builder; | ||
| 12 | const File = std.fs.File; | ||
| 13 | const InstallDir = std.build.InstallDir; | ||
| 14 | const LibExeObjStep = std.build.LibExeObjStep; | ||
| 15 | const Step = std.build.Step; | ||
| 16 | const elf = std.elf; | ||
| 17 | const fs = std.fs; | ||
| 18 | const io = std.io; | ||
| 19 | const sort = std.sort; | ||
| 20 | const warn = std.debug.warn; | ||
| 21 | |||
| 22 | const BinaryElfSection = struct { | ||
| 23 | elfOffset: u64, | ||
| 24 | binaryOffset: u64, | ||
| 25 | fileSize: usize, | ||
| 26 | segment: ?*BinaryElfSegment, | ||
| 27 | }; | ||
| 28 | |||
| 29 | const BinaryElfSegment = struct { | ||
| 30 | physicalAddress: u64, | ||
| 31 | virtualAddress: u64, | ||
| 32 | elfOffset: u64, | ||
| 33 | binaryOffset: u64, | ||
| 34 | fileSize: usize, | ||
| 35 | firstSection: ?*BinaryElfSection, | ||
| 36 | }; | ||
| 37 | |||
| 38 | const BinaryElfOutput = struct { | ||
| 39 | segments: ArrayList(*BinaryElfSegment), | ||
| 40 | sections: ArrayList(*BinaryElfSection), | ||
| 41 | |||
| 42 | const Self = @This(); | ||
| 43 | |||
| 44 | pub fn deinit(self: *Self) void { | ||
| 45 | self.sections.deinit(); | ||
| 46 | self.segments.deinit(); | ||
| 47 | } | ||
| 48 | |||
| 49 | pub fn parse(allocator: *Allocator, elf_file: File) !Self { | ||
| 50 | var self: Self = .{ | ||
| 51 | .segments = ArrayList(*BinaryElfSegment).init(allocator), | ||
| 52 | .sections = ArrayList(*BinaryElfSection).init(allocator), | ||
| 53 | }; | ||
| 54 | const elf_hdr = try std.elf.Header.read(&elf_file); | ||
| 55 | |||
| 56 | var section_headers = elf_hdr.section_header_iterator(&elf_file); | ||
| 57 | while (try section_headers.next()) |section| { | ||
| 58 | if (sectionValidForOutput(section)) { | ||
| 59 | const newSection = try allocator.create(BinaryElfSection); | ||
| 60 | |||
| 61 | newSection.binaryOffset = 0; | ||
| 62 | newSection.elfOffset = section.sh_offset; | ||
| 63 | newSection.fileSize = @intCast(usize, section.sh_size); | ||
| 64 | newSection.segment = null; | ||
| 65 | |||
| 66 | try self.sections.append(newSection); | ||
| 67 | } | ||
| 68 | } | ||
| 69 | |||
| 70 | var program_headers = elf_hdr.program_header_iterator(&elf_file); | ||
| 71 | while (try program_headers.next()) |phdr| { | ||
| 72 | if (phdr.p_type == elf.PT_LOAD) { | ||
| 73 | const newSegment = try allocator.create(BinaryElfSegment); | ||
| 74 | |||
| 75 | newSegment.physicalAddress = if (phdr.p_paddr != 0) phdr.p_paddr else phdr.p_vaddr; | ||
| 76 | newSegment.virtualAddress = phdr.p_vaddr; | ||
| 77 | newSegment.fileSize = @intCast(usize, phdr.p_filesz); | ||
| 78 | newSegment.elfOffset = phdr.p_offset; | ||
| 79 | newSegment.binaryOffset = 0; | ||
| 80 | newSegment.firstSection = null; | ||
| 81 | |||
| 82 | for (self.sections.items) |section| { | ||
| 83 | if (sectionWithinSegment(section, phdr)) { | ||
| 84 | if (section.segment) |sectionSegment| { | ||
| 85 | if (sectionSegment.elfOffset > newSegment.elfOffset) { | ||
| 86 | section.segment = newSegment; | ||
| 87 | } | ||
| 88 | } else { | ||
| 89 | section.segment = newSegment; | ||
| 90 | } | ||
| 91 | |||
| 92 | if (newSegment.firstSection == null) { | ||
| 93 | newSegment.firstSection = section; | ||
| 94 | } | ||
| 95 | } | ||
| 96 | } | ||
| 97 | |||
| 98 | try self.segments.append(newSegment); | ||
| 99 | } | ||
| 100 | } | ||
| 101 | |||
| 102 | sort.sort(*BinaryElfSegment, self.segments.items, {}, segmentSortCompare); | ||
| 103 | |||
| 104 | if (self.segments.items.len > 0) { | ||
| 105 | const firstSegment = self.segments.items[0]; | ||
| 106 | if (firstSegment.firstSection) |firstSection| { | ||
| 107 | const diff = firstSection.elfOffset - firstSegment.elfOffset; | ||
| 108 | |||
| 109 | firstSegment.elfOffset += diff; | ||
| 110 | firstSegment.fileSize += diff; | ||
| 111 | firstSegment.physicalAddress += diff; | ||
| 112 | |||
| 113 | const basePhysicalAddress = firstSegment.physicalAddress; | ||
| 114 | |||
| 115 | for (self.segments.items) |segment| { | ||
| 116 | segment.binaryOffset = segment.physicalAddress - basePhysicalAddress; | ||
| 117 | } | ||
| 118 | } | ||
| 119 | } | ||
| 120 | |||
| 121 | for (self.sections.items) |section| { | ||
| 122 | if (section.segment) |segment| { | ||
| 123 | section.binaryOffset = segment.binaryOffset + (section.elfOffset - segment.elfOffset); | ||
| 124 | } | ||
| 125 | } | ||
| 126 | |||
| 127 | sort.sort(*BinaryElfSection, self.sections.items, {}, sectionSortCompare); | ||
| 128 | |||
| 129 | return self; | ||
| 130 | } | ||
| 131 | |||
| 132 | fn sectionWithinSegment(section: *BinaryElfSection, segment: elf.Elf64_Phdr) bool { | ||
| 133 | return segment.p_offset <= section.elfOffset and (segment.p_offset + segment.p_filesz) >= (section.elfOffset + section.fileSize); | ||
| 134 | } | ||
| 135 | |||
| 136 | fn sectionValidForOutput(shdr: anytype) bool { | ||
| 137 | return shdr.sh_size > 0 and shdr.sh_type != elf.SHT_NOBITS and | ||
| 138 | ((shdr.sh_flags & elf.SHF_ALLOC) == elf.SHF_ALLOC); | ||
| 139 | } | ||
| 140 | |||
| 141 | fn segmentSortCompare(context: void, left: *BinaryElfSegment, right: *BinaryElfSegment) bool { | ||
| 142 | if (left.physicalAddress < right.physicalAddress) { | ||
| 143 | return true; | ||
| 144 | } | ||
| 145 | if (left.physicalAddress > right.physicalAddress) { | ||
| 146 | return false; | ||
| 147 | } | ||
| 148 | return false; | ||
| 149 | } | ||
| 150 | |||
| 151 | fn sectionSortCompare(context: void, left: *BinaryElfSection, right: *BinaryElfSection) bool { | ||
| 152 | return left.binaryOffset < right.binaryOffset; | ||
| 153 | } | ||
| 154 | }; | ||
| 155 | |||
| 156 | fn writeBinaryElfSection(elf_file: File, out_file: File, section: *BinaryElfSection) !void { | ||
| 157 | try out_file.seekTo(section.binaryOffset); | ||
| 158 | |||
| 159 | try out_file.writeFileAll(elf_file, .{ | ||
| 160 | .in_offset = section.elfOffset, | ||
| 161 | .in_len = section.fileSize, | ||
| 162 | }); | ||
| 163 | } | ||
| 164 | |||
| 165 | fn emitRaw(allocator: *Allocator, elf_path: []const u8, raw_path: []const u8) !void { | ||
| 166 | var elf_file = try fs.cwd().openFile(elf_path, .{}); | ||
| 167 | defer elf_file.close(); | ||
| 168 | |||
| 169 | var out_file = try fs.cwd().createFile(raw_path, .{}); | ||
| 170 | defer out_file.close(); | ||
| 171 | |||
| 172 | var binary_elf_output = try BinaryElfOutput.parse(allocator, elf_file); | ||
| 173 | defer binary_elf_output.deinit(); | ||
| 174 | |||
| 175 | for (binary_elf_output.sections.items) |section| { | ||
| 176 | try writeBinaryElfSection(elf_file, out_file, section); | ||
| 177 | } | ||
| 178 | } | ||
| 179 | |||
| 180 | pub const InstallRawStep = struct { | ||
| 181 | step: Step, | ||
| 182 | builder: *Builder, | ||
| 183 | artifact: *LibExeObjStep, | ||
| 184 | dest_dir: InstallDir, | ||
| 185 | dest_filename: []const u8, | ||
| 186 | |||
| 187 | const Self = @This(); | ||
| 188 | |||
| 189 | pub fn create(builder: *Builder, artifact: *LibExeObjStep, dest_filename: []const u8) *Self { | ||
| 190 | const self = builder.allocator.create(Self) catch unreachable; | ||
| 191 | self.* = Self{ | ||
| 192 | .step = Step.init(.InstallRaw, builder.fmt("install raw binary {s}", .{artifact.step.name}), builder.allocator, make), | ||
| 193 | .builder = builder, | ||
| 194 | .artifact = artifact, | ||
| 195 | .dest_dir = switch (artifact.kind) { | ||
| 196 | .Obj => unreachable, | ||
| 197 | .Test => unreachable, | ||
| 198 | .Exe => .Bin, | ||
| 199 | .Lib => unreachable, | ||
| 200 | }, | ||
| 201 | .dest_filename = dest_filename, | ||
| 202 | }; | ||
| 203 | self.step.dependOn(&artifact.step); | ||
| 204 | |||
| 205 | builder.pushInstalledFile(self.dest_dir, dest_filename); | ||
| 206 | return self; | ||
| 207 | } | ||
| 208 | |||
| 209 | fn make(step: *Step) !void { | ||
| 210 | const self = @fieldParentPtr(Self, "step", step); | ||
| 211 | const builder = self.builder; | ||
| 212 | |||
| 213 | if (self.artifact.target.getObjectFormat() != .elf) { | ||
| 214 | warn("InstallRawStep only works with ELF format.\n", .{}); | ||
| 215 | return error.InvalidObjectFormat; | ||
| 216 | } | ||
| 217 | |||
| 218 | const full_src_path = self.artifact.getOutputPath(); | ||
| 219 | const full_dest_path = builder.getInstallPath(self.dest_dir, self.dest_filename); | ||
| 220 | |||
| 221 | fs.cwd().makePath(builder.getInstallPath(self.dest_dir, "")) catch unreachable; | ||
| 222 | try emitRaw(builder.allocator, full_src_path, full_dest_path); | ||
| 223 | } | ||
| 224 | }; | ||
| 225 | |||
| 226 | test { | ||
| 227 | std.testing.refAllDecls(InstallRawStep); | ||
| 228 | } | ||
lib/std/build/fmt.zig deleted-40| ... | @@ -1,40 +0,0 @@ | ||
| 1 | // SPDX-License-Identifier: MIT | ||
| 2 | // Copyright (c) 2015-2021 Zig Contributors | ||
| 3 | // This file is part of [zig](https://ziglang.org/), which is MIT licensed. | ||
| 4 | // The MIT license requires this copyright notice to be included in all copies | ||
| 5 | // and substantial portions of the software. | ||
| 6 | const std = @import("../std.zig"); | ||
| 7 | const build = @import("../build.zig"); | ||
| 8 | const Step = build.Step; | ||
| 9 | const Builder = build.Builder; | ||
| 10 | const BufMap = std.BufMap; | ||
| 11 | const mem = std.mem; | ||
| 12 | |||
| 13 | pub const FmtStep = struct { | ||
| 14 | step: Step, | ||
| 15 | builder: *Builder, | ||
| 16 | argv: [][]const u8, | ||
| 17 | |||
| 18 | pub fn create(builder: *Builder, paths: []const []const u8) *FmtStep { | ||
| 19 | const self = builder.allocator.create(FmtStep) catch unreachable; | ||
| 20 | const name = "zig fmt"; | ||
| 21 | self.* = FmtStep{ | ||
| 22 | .step = Step.init(.Fmt, name, builder.allocator, make), | ||
| 23 | .builder = builder, | ||
| 24 | .argv = builder.allocator.alloc([]u8, paths.len + 2) catch unreachable, | ||
| 25 | }; | ||
| 26 | |||
| 27 | self.argv[0] = builder.zig_exe; | ||
| 28 | self.argv[1] = "fmt"; | ||
| 29 | for (paths) |path, i| { | ||
| 30 | self.argv[2 + i] = builder.pathFromRoot(path); | ||
| 31 | } | ||
| 32 | return self; | ||
| 33 | } | ||
| 34 | |||
| 35 | fn make(step: *Step) !void { | ||
| 36 | const self = @fieldParentPtr(FmtStep, "step", step); | ||
| 37 | |||
| 38 | return self.builder.spawnChild(self.argv); | ||
| 39 | } | ||
| 40 | }; | ||
lib/std/build/translate_c.zig deleted-109| ... | @@ -1,109 +0,0 @@ | ||
| 1 | // SPDX-License-Identifier: MIT | ||
| 2 | // Copyright (c) 2015-2021 Zig Contributors | ||
| 3 | // This file is part of [zig](https://ziglang.org/), which is MIT licensed. | ||
| 4 | // The MIT license requires this copyright notice to be included in all copies | ||
| 5 | // and substantial portions of the software. | ||
| 6 | const std = @import("../std.zig"); | ||
| 7 | const build = std.build; | ||
| 8 | const Step = build.Step; | ||
| 9 | const Builder = build.Builder; | ||
| 10 | const LibExeObjStep = build.LibExeObjStep; | ||
| 11 | const CheckFileStep = build.CheckFileStep; | ||
| 12 | const fs = std.fs; | ||
| 13 | const mem = std.mem; | ||
| 14 | const CrossTarget = std.zig.CrossTarget; | ||
| 15 | |||
| 16 | pub const TranslateCStep = struct { | ||
| 17 | step: Step, | ||
| 18 | builder: *Builder, | ||
| 19 | source: build.FileSource, | ||
| 20 | include_dirs: std.ArrayList([]const u8), | ||
| 21 | output_dir: ?[]const u8, | ||
| 22 | out_basename: []const u8, | ||
| 23 | target: CrossTarget = CrossTarget{}, | ||
| 24 | output_file: build.GeneratedFile, | ||
| 25 | |||
| 26 | pub fn create(builder: *Builder, source: build.FileSource) *TranslateCStep { | ||
| 27 | const self = builder.allocator.create(TranslateCStep) catch unreachable; | ||
| 28 | self.* = TranslateCStep{ | ||
| 29 | .step = Step.init(.TranslateC, "translate-c", builder.allocator, make), | ||
| 30 | .builder = builder, | ||
| 31 | .source = source, | ||
| 32 | .include_dirs = std.ArrayList([]const u8).init(builder.allocator), | ||
| 33 | .output_dir = null, | ||
| 34 | .out_basename = undefined, | ||
| 35 | .output_file = build.GeneratedFile{ | ||
| 36 | .step = &self.step, | ||
| 37 | .getPathFn = getGeneratedFilePath, | ||
| 38 | }, | ||
| 39 | }; | ||
| 40 | source.addStepDependencies(&self.step); | ||
| 41 | return self; | ||
| 42 | } | ||
| 43 | |||
| 44 | fn getGeneratedFilePath(file: *const build.GeneratedFile) []const u8 { | ||
| 45 | const self = @fieldParentPtr(TranslateCStep, "step", file.step); | ||
| 46 | return self.getOutputPath(); | ||
| 47 | } | ||
| 48 | |||
| 49 | /// Unless setOutputDir was called, this function must be called only in | ||
| 50 | /// the make step, from a step that has declared a dependency on this one. | ||
| 51 | /// To run an executable built with zig build, use `run`, or create an install step and invoke it. | ||
| 52 | pub fn getOutputPath(self: *TranslateCStep) []const u8 { | ||
| 53 | return fs.path.join( | ||
| 54 | self.builder.allocator, | ||
| 55 | &[_][]const u8{ self.output_dir.?, self.out_basename }, | ||
| 56 | ) catch unreachable; | ||
| 57 | } | ||
| 58 | |||
| 59 | pub fn setTarget(self: *TranslateCStep, target: CrossTarget) void { | ||
| 60 | self.target = target; | ||
| 61 | } | ||
| 62 | |||
| 63 | /// Creates a step to build an executable from the translated source. | ||
| 64 | pub fn addExecutable(self: *TranslateCStep) *LibExeObjStep { | ||
| 65 | return self.builder.addExecutableSource("translated_c", build.FileSource{ .generated = &self.output_file }, false); | ||
| 66 | } | ||
| 67 | |||
| 68 | pub fn addIncludeDir(self: *TranslateCStep, include_dir: []const u8) void { | ||
| 69 | self.include_dirs.append(self.builder.dupePath(include_dir)) catch unreachable; | ||
| 70 | } | ||
| 71 | |||
| 72 | pub fn addCheckFile(self: *TranslateCStep, expected_matches: []const []const u8) *CheckFileStep { | ||
| 73 | return CheckFileStep.create(self.builder, .{ .generated = &self.output_file }, self.builder.dupeStrings(expected_matches)); | ||
| 74 | } | ||
| 75 | |||
| 76 | fn make(step: *Step) !void { | ||
| 77 | const self = @fieldParentPtr(TranslateCStep, "step", step); | ||
| 78 | |||
| 79 | var argv_list = std.ArrayList([]const u8).init(self.builder.allocator); | ||
| 80 | try argv_list.append(self.builder.zig_exe); | ||
| 81 | try argv_list.append("translate-c"); | ||
| 82 | try argv_list.append("-lc"); | ||
| 83 | |||
| 84 | try argv_list.append("--enable-cache"); | ||
| 85 | |||
| 86 | if (!self.target.isNative()) { | ||
| 87 | try argv_list.append("-target"); | ||
| 88 | try argv_list.append(try self.target.zigTriple(self.builder.allocator)); | ||
| 89 | } | ||
| 90 | |||
| 91 | for (self.include_dirs.items) |include_dir| { | ||
| 92 | try argv_list.append("-I"); | ||
| 93 | try argv_list.append(include_dir); | ||
| 94 | } | ||
| 95 | |||
| 96 | try argv_list.append(self.source.getPath(self.builder)); | ||
| 97 | |||
| 98 | const output_path_nl = try self.builder.execFromStep(argv_list.items, &self.step); | ||
| 99 | const output_path = mem.trimRight(u8, output_path_nl, "\r\n"); | ||
| 100 | |||
| 101 | self.out_basename = fs.path.basename(output_path); | ||
| 102 | if (self.output_dir) |output_dir| { | ||
| 103 | const full_dest = try fs.path.join(self.builder.allocator, &[_][]const u8{ output_dir, self.out_basename }); | ||
| 104 | try self.builder.updateFile(output_path, full_dest); | ||
| 105 | } else { | ||
| 106 | self.output_dir = fs.path.dirname(output_path).?; | ||
| 107 | } | ||
| 108 | } | ||
| 109 | }; | ||
lib/std/build/write_file.zig deleted-133| ... | @@ -1,133 +0,0 @@ | ||
| 1 | // SPDX-License-Identifier: MIT | ||
| 2 | // Copyright (c) 2015-2021 Zig Contributors | ||
| 3 | // This file is part of [zig](https://ziglang.org/), which is MIT licensed. | ||
| 4 | // The MIT license requires this copyright notice to be included in all copies | ||
| 5 | // and substantial portions of the software. | ||
| 6 | const std = @import("../std.zig"); | ||
| 7 | const build = @import("../build.zig"); | ||
| 8 | const Step = build.Step; | ||
| 9 | const Builder = build.Builder; | ||
| 10 | const fs = std.fs; | ||
| 11 | const warn = std.debug.warn; | ||
| 12 | const ArrayList = std.ArrayList; | ||
| 13 | |||
| 14 | pub const WriteFileStep = struct { | ||
| 15 | step: Step, | ||
| 16 | builder: *Builder, | ||
| 17 | output_dir: []const u8, | ||
| 18 | files: std.TailQueue(File), | ||
| 19 | |||
| 20 | pub const File = struct { | ||
| 21 | source: build.GeneratedFile, | ||
| 22 | basename: []const u8, | ||
| 23 | bytes: []const u8, | ||
| 24 | }; | ||
| 25 | |||
| 26 | pub fn init(builder: *Builder) WriteFileStep { | ||
| 27 | return WriteFileStep{ | ||
| 28 | .builder = builder, | ||
| 29 | .step = Step.init(.WriteFile, "writefile", builder.allocator, make), | ||
| 30 | .files = .{}, | ||
| 31 | .output_dir = undefined, | ||
| 32 | }; | ||
| 33 | } | ||
| 34 | |||
| 35 | pub fn add(self: *WriteFileStep, basename: []const u8, bytes: []const u8) void { | ||
| 36 | const node = self.builder.allocator.create(std.TailQueue(File).Node) catch unreachable; | ||
| 37 | node.* = .{ | ||
| 38 | .data = .{ | ||
| 39 | .source = build.GeneratedFile{ | ||
| 40 | .step = &self.step, | ||
| 41 | .getPathFn = getFilePath, | ||
| 42 | }, | ||
| 43 | .basename = self.builder.dupePath(basename), | ||
| 44 | .bytes = self.builder.dupe(bytes), | ||
| 45 | }, | ||
| 46 | }; | ||
| 47 | |||
| 48 | self.files.append(node); | ||
| 49 | } | ||
| 50 | |||
| 51 | /// Unless setOutputDir was called, this function must be called only in | ||
| 52 | /// the make step, from a step that has declared a dependency on this one. | ||
| 53 | /// To run an executable built with zig build, use `run`, or create an install step and invoke it. | ||
| 54 | //pub const getOutputPath = @compileError("WriteFileStep.getOutputPath is deprecated! Use getFileSource to retrieve a "); | ||
| 55 | /// Gets a file source for the given basename. If the file does not exist, returns `null`. | ||
| 56 | pub fn getFileSource(step: *WriteFileStep, basename: []const u8) ?build.FileSource { | ||
| 57 | var it = step.files.first; | ||
| 58 | while (it) |node| : (it = node.next) { | ||
| 59 | if (std.mem.eql(u8, node.data.basename, basename)) | ||
| 60 | return build.FileSource{ .generated = &node.data.source }; | ||
| 61 | } | ||
| 62 | return null; | ||
| 63 | } | ||
| 64 | |||
| 65 | /// Returns the | ||
| 66 | fn getFilePath(source: *const build.GeneratedFile) []const u8 { | ||
| 67 | const file = @fieldParentPtr(File, "source", source); | ||
| 68 | const step = @fieldParentPtr(WriteFileStep, "step", source.step); | ||
| 69 | |||
| 70 | return fs.path.join( | ||
| 71 | step.builder.allocator, | ||
| 72 | &[_][]const u8{ step.output_dir, file.basename }, | ||
| 73 | ) catch unreachable; | ||
| 74 | } | ||
| 75 | |||
| 76 | fn make(step: *Step) !void { | ||
| 77 | const self = @fieldParentPtr(WriteFileStep, "step", step); | ||
| 78 | |||
| 79 | // The cache is used here not really as a way to speed things up - because writing | ||
| 80 | // the data to a file would probably be very fast - but as a way to find a canonical | ||
| 81 | // location to put build artifacts. | ||
| 82 | |||
| 83 | // If, for example, a hard-coded path was used as the location to put WriteFileStep | ||
| 84 | // files, then two WriteFileSteps executing in parallel might clobber each other. | ||
| 85 | |||
| 86 | // TODO port the cache system from stage1 to zig std lib. Until then we use blake2b | ||
| 87 | // directly and construct the path, and no "cache hit" detection happens; the files | ||
| 88 | // are always written. | ||
| 89 | var hash = std.crypto.hash.blake2.Blake2b384.init(.{}); | ||
| 90 | |||
| 91 | // Random bytes to make WriteFileStep unique. Refresh this with | ||
| 92 | // new random bytes when WriteFileStep implementation is modified | ||
| 93 | // in a non-backwards-compatible way. | ||
| 94 | hash.update("eagVR1dYXoE7ARDP"); | ||
| 95 | { | ||
| 96 | var it = self.files.first; | ||
| 97 | while (it) |node| : (it = node.next) { | ||
| 98 | hash.update(node.data.basename); | ||
| 99 | hash.update(node.data.bytes); | ||
| 100 | hash.update("|"); | ||
| 101 | } | ||
| 102 | } | ||
| 103 | var digest: [48]u8 = undefined; | ||
| 104 | hash.final(&digest); | ||
| 105 | var hash_basename: [64]u8 = undefined; | ||
| 106 | _ = fs.base64_encoder.encode(&hash_basename, &digest); | ||
| 107 | self.output_dir = try fs.path.join(self.builder.allocator, &[_][]const u8{ | ||
| 108 | self.builder.cache_root, | ||
| 109 | "o", | ||
| 110 | &hash_basename, | ||
| 111 | }); | ||
| 112 | // TODO replace with something like fs.makePathAndOpenDir | ||
| 113 | fs.cwd().makePath(self.output_dir) catch |err| { | ||
| 114 | warn("unable to make path {s}: {s}\n", .{ self.output_dir, @errorName(err) }); | ||
| 115 | return err; | ||
| 116 | }; | ||
| 117 | var dir = try fs.cwd().openDir(self.output_dir, .{}); | ||
| 118 | defer dir.close(); | ||
| 119 | { | ||
| 120 | var it = self.files.first; | ||
| 121 | while (it) |node| : (it = node.next) { | ||
| 122 | dir.writeFile(node.data.basename, node.data.bytes) catch |err| { | ||
| 123 | warn("unable to write {s} into {s}: {s}\n", .{ | ||
| 124 | node.data.basename, | ||
| 125 | self.output_dir, | ||
| 126 | @errorName(err), | ||
| 127 | }); | ||
| 128 | return err; | ||
| 129 | }; | ||
| 130 | } | ||
| 131 | } | ||
| 132 | } | ||
| 133 | }; | ||