| author | |
| committer | |
| log | a0ca34979ea5ff9be0d353f539e7c86dedbf8693 |
| tree | e81c0dbbc2b7b81ce1d6f46b79ee781e31326ebc |
| parent | 2e5342512f32384777ebcb0bf3dcb177b9380080 |
| parent | 242f5d10d57eb9239e7a89d4e705fc05785abe7c |
| signature |
add test harness for "run translated C" tests15 files changed, 1226 insertions(+), 815 deletions(-)
build.zig+1| ... | ... | @@ -137,6 +137,7 @@ pub fn build(b: *Builder) !void { |
| 137 | 137 | test_step.dependOn(tests.addAssembleAndLinkTests(b, test_filter, modes)); |
| 138 | 138 | test_step.dependOn(tests.addRuntimeSafetyTests(b, test_filter, modes)); |
| 139 | 139 | test_step.dependOn(tests.addTranslateCTests(b, test_filter)); |
| 140 | test_step.dependOn(tests.addRunTranslatedCTests(b, test_filter)); | |
| 140 | 141 | test_step.dependOn(tests.addGenHTests(b, test_filter)); |
| 141 | 142 | test_step.dependOn(tests.addCompileErrorTests(b, test_filter, modes)); |
| 142 | 143 | test_step.dependOn(docs_step); |
lib/std/build.zig+146-192| ... | ... | @@ -17,6 +17,10 @@ const fmt_lib = std.fmt; |
| 17 | 17 | const File = std.fs.File; |
| 18 | 18 | |
| 19 | 19 | pub const FmtStep = @import("build/fmt.zig").FmtStep; |
| 20 | pub const TranslateCStep = @import("build/translate_c.zig").TranslateCStep; | |
| 21 | pub const WriteFileStep = @import("build/write_file.zig").WriteFileStep; | |
| 22 | pub const RunStep = @import("build/run.zig").RunStep; | |
| 23 | pub const CheckFileStep = @import("build/check_file.zig").CheckFileStep; | |
| 20 | 24 | |
| 21 | 25 | pub const Builder = struct { |
| 22 | 26 | install_tls: TopLevelStep, |
| ... | ... | @@ -203,23 +207,67 @@ pub const Builder = struct { |
| 203 | 207 | } |
| 204 | 208 | |
| 205 | 209 | pub fn addExecutable(self: *Builder, name: []const u8, root_src: ?[]const u8) *LibExeObjStep { |
| 210 | return LibExeObjStep.createExecutable( | |
| 211 | self, | |
| 212 | name, | |
| 213 | if (root_src) |p| FileSource{ .path = p } else null, | |
| 214 | false, | |
| 215 | ); | |
| 216 | } | |
| 217 | ||
| 218 | pub fn addExecutableFromWriteFileStep( | |
| 219 | self: *Builder, | |
| 220 | name: []const u8, | |
| 221 | wfs: *WriteFileStep, | |
| 222 | basename: []const u8, | |
| 223 | ) *LibExeObjStep { | |
| 224 | return LibExeObjStep.createExecutable(self, name, @as(FileSource, .{ | |
| 225 | .write_file = .{ | |
| 226 | .step = wfs, | |
| 227 | .basename = basename, | |
| 228 | }, | |
| 229 | }), false); | |
| 230 | } | |
| 231 | ||
| 232 | pub fn addExecutableSource( | |
| 233 | self: *Builder, | |
| 234 | name: []const u8, | |
| 235 | root_src: ?FileSource, | |
| 236 | ) *LibExeObjStep { | |
| 206 | 237 | return LibExeObjStep.createExecutable(self, name, root_src, false); |
| 207 | 238 | } |
| 208 | 239 | |
| 209 | 240 | pub fn addObject(self: *Builder, name: []const u8, root_src: ?[]const u8) *LibExeObjStep { |
| 210 | return LibExeObjStep.createObject(self, name, root_src); | |
| 241 | const root_src_param = if (root_src) |p| @as(FileSource, .{ .path = p }) else null; | |
| 242 | return LibExeObjStep.createObject(self, name, root_src_param); | |
| 243 | } | |
| 244 | ||
| 245 | pub fn addObjectFromWriteFileStep( | |
| 246 | self: *Builder, | |
| 247 | name: []const u8, | |
| 248 | wfs: *WriteFileStep, | |
| 249 | basename: []const u8, | |
| 250 | ) *LibExeObjStep { | |
| 251 | return LibExeObjStep.createObject(self, name, @as(FileSource, .{ | |
| 252 | .write_file = .{ | |
| 253 | .step = wfs, | |
| 254 | .basename = basename, | |
| 255 | }, | |
| 256 | })); | |
| 211 | 257 | } |
| 212 | 258 | |
| 213 | 259 | pub fn addSharedLibrary(self: *Builder, name: []const u8, root_src: ?[]const u8, ver: Version) *LibExeObjStep { |
| 214 | return LibExeObjStep.createSharedLibrary(self, name, root_src, ver); | |
| 260 | const root_src_param = if (root_src) |p| @as(FileSource, .{ .path = p }) else null; | |
| 261 | return LibExeObjStep.createSharedLibrary(self, name, root_src_param, ver); | |
| 215 | 262 | } |
| 216 | 263 | |
| 217 | 264 | pub fn addStaticLibrary(self: *Builder, name: []const u8, root_src: ?[]const u8) *LibExeObjStep { |
| 218 | return LibExeObjStep.createStaticLibrary(self, name, root_src); | |
| 265 | const root_src_param = if (root_src) |p| @as(FileSource, .{ .path = p }) else null; | |
| 266 | return LibExeObjStep.createStaticLibrary(self, name, root_src_param); | |
| 219 | 267 | } |
| 220 | 268 | |
| 221 | 269 | pub fn addTest(self: *Builder, root_src: []const u8) *LibExeObjStep { |
| 222 | return LibExeObjStep.createTest(self, "test", root_src); | |
| 270 | return LibExeObjStep.createTest(self, "test", .{ .path = root_src }); | |
| 223 | 271 | } |
| 224 | 272 | |
| 225 | 273 | pub fn addAssemble(self: *Builder, name: []const u8, src: []const u8) *LibExeObjStep { |
| ... | ... | @@ -256,8 +304,14 @@ pub const Builder = struct { |
| 256 | 304 | } |
| 257 | 305 | |
| 258 | 306 | pub fn addWriteFile(self: *Builder, file_path: []const u8, data: []const u8) *WriteFileStep { |
| 307 | const write_file_step = self.addWriteFiles(); | |
| 308 | write_file_step.add(file_path, data); | |
| 309 | return write_file_step; | |
| 310 | } | |
| 311 | ||
| 312 | pub fn addWriteFiles(self: *Builder) *WriteFileStep { | |
| 259 | 313 | const write_file_step = self.allocator.create(WriteFileStep) catch unreachable; |
| 260 | write_file_step.* = WriteFileStep.init(self, file_path, data); | |
| 314 | write_file_step.* = WriteFileStep.init(self); | |
| 261 | 315 | return write_file_step; |
| 262 | 316 | } |
| 263 | 317 | |
| ... | ... | @@ -278,6 +332,10 @@ pub const Builder = struct { |
| 278 | 332 | return FmtStep.create(self, paths); |
| 279 | 333 | } |
| 280 | 334 | |
| 335 | pub fn addTranslateC(self: *Builder, source: FileSource) *TranslateCStep { | |
| 336 | return TranslateCStep.create(self, source); | |
| 337 | } | |
| 338 | ||
| 281 | 339 | pub fn version(self: *const Builder, major: u32, minor: u32, patch: u32) Version { |
| 282 | 340 | return Version{ |
| 283 | 341 | .major = major, |
| ... | ... | @@ -1002,7 +1060,7 @@ const Pkg = struct { |
| 1002 | 1060 | }; |
| 1003 | 1061 | |
| 1004 | 1062 | const CSourceFile = struct { |
| 1005 | source_path: []const u8, | |
| 1063 | source: FileSource, | |
| 1006 | 1064 | args: []const []const u8, |
| 1007 | 1065 | }; |
| 1008 | 1066 | |
| ... | ... | @@ -1015,6 +1073,33 @@ fn isLibCLibrary(name: []const u8) bool { |
| 1015 | 1073 | return false; |
| 1016 | 1074 | } |
| 1017 | 1075 | |
| 1076 | pub const FileSource = union(enum) { | |
| 1077 | /// Relative to build root | |
| 1078 | path: []const u8, | |
| 1079 | write_file: struct { | |
| 1080 | step: *WriteFileStep, | |
| 1081 | basename: []const u8, | |
| 1082 | }, | |
| 1083 | translate_c: *TranslateCStep, | |
| 1084 | ||
| 1085 | pub fn addStepDependencies(self: FileSource, step: *Step) void { | |
| 1086 | switch (self) { | |
| 1087 | .path => {}, | |
| 1088 | .write_file => |wf| step.dependOn(&wf.step.step), | |
| 1089 | .translate_c => |tc| step.dependOn(&tc.step), | |
| 1090 | } | |
| 1091 | } | |
| 1092 | ||
| 1093 | /// Should only be called during make() | |
| 1094 | pub fn getPath(self: FileSource, builder: *Builder) []const u8 { | |
| 1095 | return switch (self) { | |
| 1096 | .path => |p| builder.pathFromRoot(p), | |
| 1097 | .write_file => |wf| wf.step.getOutputPath(wf.basename), | |
| 1098 | .translate_c => |tc| tc.getOutputPath(), | |
| 1099 | }; | |
| 1100 | } | |
| 1101 | }; | |
| 1102 | ||
| 1018 | 1103 | pub const LibExeObjStep = struct { |
| 1019 | 1104 | step: Step, |
| 1020 | 1105 | builder: *Builder, |
| ... | ... | @@ -1047,7 +1132,7 @@ pub const LibExeObjStep = struct { |
| 1047 | 1132 | filter: ?[]const u8, |
| 1048 | 1133 | single_threaded: bool, |
| 1049 | 1134 | |
| 1050 | root_src: ?[]const u8, | |
| 1135 | root_src: ?FileSource, | |
| 1051 | 1136 | out_h_filename: []const u8, |
| 1052 | 1137 | out_lib_filename: []const u8, |
| 1053 | 1138 | out_pdb_filename: []const u8, |
| ... | ... | @@ -1099,7 +1184,7 @@ pub const LibExeObjStep = struct { |
| 1099 | 1184 | StaticPath: []const u8, |
| 1100 | 1185 | OtherStep: *LibExeObjStep, |
| 1101 | 1186 | SystemLib: []const u8, |
| 1102 | AssemblyFile: []const u8, | |
| 1187 | AssemblyFile: FileSource, | |
| 1103 | 1188 | CSourceFile: *CSourceFile, |
| 1104 | 1189 | }; |
| 1105 | 1190 | |
| ... | ... | @@ -1116,37 +1201,44 @@ pub const LibExeObjStep = struct { |
| 1116 | 1201 | Test, |
| 1117 | 1202 | }; |
| 1118 | 1203 | |
| 1119 | pub fn createSharedLibrary(builder: *Builder, name: []const u8, root_src: ?[]const u8, ver: Version) *LibExeObjStep { | |
| 1204 | pub fn createSharedLibrary(builder: *Builder, name: []const u8, root_src: ?FileSource, ver: Version) *LibExeObjStep { | |
| 1120 | 1205 | const self = builder.allocator.create(LibExeObjStep) catch unreachable; |
| 1121 | 1206 | self.* = initExtraArgs(builder, name, root_src, Kind.Lib, true, ver); |
| 1122 | 1207 | return self; |
| 1123 | 1208 | } |
| 1124 | 1209 | |
| 1125 | pub fn createStaticLibrary(builder: *Builder, name: []const u8, root_src: ?[]const u8) *LibExeObjStep { | |
| 1210 | pub fn createStaticLibrary(builder: *Builder, name: []const u8, root_src: ?FileSource) *LibExeObjStep { | |
| 1126 | 1211 | const self = builder.allocator.create(LibExeObjStep) catch unreachable; |
| 1127 | 1212 | self.* = initExtraArgs(builder, name, root_src, Kind.Lib, false, builder.version(0, 0, 0)); |
| 1128 | 1213 | return self; |
| 1129 | 1214 | } |
| 1130 | 1215 | |
| 1131 | pub fn createObject(builder: *Builder, name: []const u8, root_src: ?[]const u8) *LibExeObjStep { | |
| 1216 | pub fn createObject(builder: *Builder, name: []const u8, root_src: ?FileSource) *LibExeObjStep { | |
| 1132 | 1217 | const self = builder.allocator.create(LibExeObjStep) catch unreachable; |
| 1133 | 1218 | self.* = initExtraArgs(builder, name, root_src, Kind.Obj, false, builder.version(0, 0, 0)); |
| 1134 | 1219 | return self; |
| 1135 | 1220 | } |
| 1136 | 1221 | |
| 1137 | pub fn createExecutable(builder: *Builder, name: []const u8, root_src: ?[]const u8, is_dynamic: bool) *LibExeObjStep { | |
| 1222 | pub fn createExecutable(builder: *Builder, name: []const u8, root_src: ?FileSource, is_dynamic: bool) *LibExeObjStep { | |
| 1138 | 1223 | const self = builder.allocator.create(LibExeObjStep) catch unreachable; |
| 1139 | 1224 | self.* = initExtraArgs(builder, name, root_src, Kind.Exe, is_dynamic, builder.version(0, 0, 0)); |
| 1140 | 1225 | return self; |
| 1141 | 1226 | } |
| 1142 | 1227 | |
| 1143 | pub fn createTest(builder: *Builder, name: []const u8, root_src: []const u8) *LibExeObjStep { | |
| 1228 | pub fn createTest(builder: *Builder, name: []const u8, root_src: FileSource) *LibExeObjStep { | |
| 1144 | 1229 | const self = builder.allocator.create(LibExeObjStep) catch unreachable; |
| 1145 | 1230 | self.* = initExtraArgs(builder, name, root_src, Kind.Test, false, builder.version(0, 0, 0)); |
| 1146 | 1231 | return self; |
| 1147 | 1232 | } |
| 1148 | 1233 | |
| 1149 | fn initExtraArgs(builder: *Builder, name: []const u8, root_src: ?[]const u8, kind: Kind, is_dynamic: bool, ver: Version) LibExeObjStep { | |
| 1234 | fn initExtraArgs( | |
| 1235 | builder: *Builder, | |
| 1236 | name: []const u8, | |
| 1237 | root_src: ?FileSource, | |
| 1238 | kind: Kind, | |
| 1239 | is_dynamic: bool, | |
| 1240 | ver: Version, | |
| 1241 | ) LibExeObjStep { | |
| 1150 | 1242 | if (mem.indexOf(u8, name, "/") != null or mem.indexOf(u8, name, "\\") != null) { |
| 1151 | 1243 | panic("invalid name: '{}'. It looks like a file path, but it is supposed to be the library or application name.", .{name}); |
| 1152 | 1244 | } |
| ... | ... | @@ -1196,6 +1288,7 @@ pub const LibExeObjStep = struct { |
| 1196 | 1288 | .install_step = null, |
| 1197 | 1289 | }; |
| 1198 | 1290 | self.computeOutFileNames(); |
| 1291 | if (root_src) |rs| rs.addStepDependencies(&self.step); | |
| 1199 | 1292 | return self; |
| 1200 | 1293 | } |
| 1201 | 1294 | |
| ... | ... | @@ -1486,15 +1579,22 @@ pub const LibExeObjStep = struct { |
| 1486 | 1579 | } |
| 1487 | 1580 | |
| 1488 | 1581 | pub fn addCSourceFile(self: *LibExeObjStep, file: []const u8, args: []const []const u8) void { |
| 1582 | self.addCSourceFileSource(.{ | |
| 1583 | .args = args, | |
| 1584 | .source = .{ .path = file }, | |
| 1585 | }); | |
| 1586 | } | |
| 1587 | ||
| 1588 | pub fn addCSourceFileSource(self: *LibExeObjStep, source: CSourceFile) void { | |
| 1489 | 1589 | const c_source_file = self.builder.allocator.create(CSourceFile) catch unreachable; |
| 1490 | const args_copy = self.builder.allocator.alloc([]u8, args.len) catch unreachable; | |
| 1491 | for (args) |arg, i| { | |
| 1590 | ||
| 1591 | const args_copy = self.builder.allocator.alloc([]u8, source.args.len) catch unreachable; | |
| 1592 | for (source.args) |arg, i| { | |
| 1492 | 1593 | args_copy[i] = self.builder.dupe(arg); |
| 1493 | 1594 | } |
| 1494 | c_source_file.* = CSourceFile{ | |
| 1495 | .source_path = self.builder.dupe(file), | |
| 1496 | .args = args_copy, | |
| 1497 | }; | |
| 1595 | ||
| 1596 | c_source_file.* = source; | |
| 1597 | c_source_file.args = args_copy; | |
| 1498 | 1598 | self.link_objects.append(LinkObject{ .CSourceFile = c_source_file }) catch unreachable; |
| 1499 | 1599 | } |
| 1500 | 1600 | |
| ... | ... | @@ -1571,6 +1671,20 @@ pub const LibExeObjStep = struct { |
| 1571 | 1671 | self.link_objects.append(LinkObject{ .AssemblyFile = self.builder.dupe(path) }) catch unreachable; |
| 1572 | 1672 | } |
| 1573 | 1673 | |
| 1674 | pub fn addAssemblyFileFromWriteFileStep(self: *LibExeObjStep, wfs: *WriteFileStep, basename: []const u8) void { | |
| 1675 | self.addAssemblyFileSource(.{ | |
| 1676 | .write_file = .{ | |
| 1677 | .step = wfs, | |
| 1678 | .basename = self.builder.dupe(basename), | |
| 1679 | }, | |
| 1680 | }); | |
| 1681 | } | |
| 1682 | ||
| 1683 | pub fn addAssemblyFileSource(self: *LibExeObjStep, source: FileSource) void { | |
| 1684 | self.link_objects.append(LinkObject{ .AssemblyFile = source }) catch unreachable; | |
| 1685 | source.addStepDependencies(&self.step); | |
| 1686 | } | |
| 1687 | ||
| 1574 | 1688 | pub fn addObjectFile(self: *LibExeObjStep, path: []const u8) void { |
| 1575 | 1689 | self.link_objects.append(LinkObject{ .StaticPath = self.builder.dupe(path) }) catch unreachable; |
| 1576 | 1690 | } |
| ... | ... | @@ -1698,25 +1812,23 @@ pub const LibExeObjStep = struct { |
| 1698 | 1812 | }; |
| 1699 | 1813 | zig_args.append(cmd) catch unreachable; |
| 1700 | 1814 | |
| 1701 | if (self.root_src) |root_src| { | |
| 1702 | zig_args.append(builder.pathFromRoot(root_src)) catch unreachable; | |
| 1703 | } | |
| 1815 | if (self.root_src) |root_src| try zig_args.append(root_src.getPath(builder)); | |
| 1704 | 1816 | |
| 1705 | 1817 | for (self.link_objects.toSlice()) |link_object| { |
| 1706 | 1818 | switch (link_object) { |
| 1707 | LinkObject.StaticPath => |static_path| { | |
| 1819 | .StaticPath => |static_path| { | |
| 1708 | 1820 | try zig_args.append("--object"); |
| 1709 | 1821 | try zig_args.append(builder.pathFromRoot(static_path)); |
| 1710 | 1822 | }, |
| 1711 | 1823 | |
| 1712 | LinkObject.OtherStep => |other| switch (other.kind) { | |
| 1713 | LibExeObjStep.Kind.Exe => unreachable, | |
| 1714 | LibExeObjStep.Kind.Test => unreachable, | |
| 1715 | LibExeObjStep.Kind.Obj => { | |
| 1824 | .OtherStep => |other| switch (other.kind) { | |
| 1825 | .Exe => unreachable, | |
| 1826 | .Test => unreachable, | |
| 1827 | .Obj => { | |
| 1716 | 1828 | try zig_args.append("--object"); |
| 1717 | 1829 | try zig_args.append(other.getOutputPath()); |
| 1718 | 1830 | }, |
| 1719 | LibExeObjStep.Kind.Lib => { | |
| 1831 | .Lib => { | |
| 1720 | 1832 | if (!other.is_dynamic or self.target.isWindows()) { |
| 1721 | 1833 | try zig_args.append("--object"); |
| 1722 | 1834 | try zig_args.append(other.getOutputLibPath()); |
| ... | ... | @@ -1732,20 +1844,20 @@ pub const LibExeObjStep = struct { |
| 1732 | 1844 | } |
| 1733 | 1845 | }, |
| 1734 | 1846 | }, |
| 1735 | LinkObject.SystemLib => |name| { | |
| 1847 | .SystemLib => |name| { | |
| 1736 | 1848 | try zig_args.append("--library"); |
| 1737 | 1849 | try zig_args.append(name); |
| 1738 | 1850 | }, |
| 1739 | LinkObject.AssemblyFile => |asm_file| { | |
| 1851 | .AssemblyFile => |asm_file| { | |
| 1740 | 1852 | try zig_args.append("--c-source"); |
| 1741 | try zig_args.append(builder.pathFromRoot(asm_file)); | |
| 1853 | try zig_args.append(asm_file.getPath(builder)); | |
| 1742 | 1854 | }, |
| 1743 | LinkObject.CSourceFile => |c_source_file| { | |
| 1855 | .CSourceFile => |c_source_file| { | |
| 1744 | 1856 | try zig_args.append("--c-source"); |
| 1745 | 1857 | for (c_source_file.args) |arg| { |
| 1746 | 1858 | try zig_args.append(arg); |
| 1747 | 1859 | } |
| 1748 | try zig_args.append(self.builder.pathFromRoot(c_source_file.source_path)); | |
| 1860 | try zig_args.append(c_source_file.source.getPath(builder)); | |
| 1749 | 1861 | }, |
| 1750 | 1862 | } |
| 1751 | 1863 | } |
| ... | ... | @@ -2041,134 +2153,6 @@ pub const LibExeObjStep = struct { |
| 2041 | 2153 | } |
| 2042 | 2154 | }; |
| 2043 | 2155 | |
| 2044 | pub const RunStep = struct { | |
| 2045 | step: Step, | |
| 2046 | builder: *Builder, | |
| 2047 | ||
| 2048 | /// See also addArg and addArgs to modifying this directly | |
| 2049 | argv: ArrayList(Arg), | |
| 2050 | ||
| 2051 | /// Set this to modify the current working directory | |
| 2052 | cwd: ?[]const u8, | |
| 2053 | ||
| 2054 | /// Override this field to modify the environment, or use setEnvironmentVariable | |
| 2055 | env_map: ?*BufMap, | |
| 2056 | ||
| 2057 | pub const Arg = union(enum) { | |
| 2058 | Artifact: *LibExeObjStep, | |
| 2059 | Bytes: []u8, | |
| 2060 | }; | |
| 2061 | ||
| 2062 | pub fn create(builder: *Builder, name: []const u8) *RunStep { | |
| 2063 | const self = builder.allocator.create(RunStep) catch unreachable; | |
| 2064 | self.* = RunStep{ | |
| 2065 | .builder = builder, | |
| 2066 | .step = Step.init(name, builder.allocator, make), | |
| 2067 | .argv = ArrayList(Arg).init(builder.allocator), | |
| 2068 | .cwd = null, | |
| 2069 | .env_map = null, | |
| 2070 | }; | |
| 2071 | return self; | |
| 2072 | } | |
| 2073 | ||
| 2074 | pub fn addArtifactArg(self: *RunStep, artifact: *LibExeObjStep) void { | |
| 2075 | self.argv.append(Arg{ .Artifact = artifact }) catch unreachable; | |
| 2076 | self.step.dependOn(&artifact.step); | |
| 2077 | } | |
| 2078 | ||
| 2079 | pub fn addArg(self: *RunStep, arg: []const u8) void { | |
| 2080 | self.argv.append(Arg{ .Bytes = self.builder.dupe(arg) }) catch unreachable; | |
| 2081 | } | |
| 2082 | ||
| 2083 | pub fn addArgs(self: *RunStep, args: []const []const u8) void { | |
| 2084 | for (args) |arg| { | |
| 2085 | self.addArg(arg); | |
| 2086 | } | |
| 2087 | } | |
| 2088 | ||
| 2089 | pub fn clearEnvironment(self: *RunStep) void { | |
| 2090 | const new_env_map = self.builder.allocator.create(BufMap) catch unreachable; | |
| 2091 | new_env_map.* = BufMap.init(self.builder.allocator); | |
| 2092 | self.env_map = new_env_map; | |
| 2093 | } | |
| 2094 | ||
| 2095 | pub fn addPathDir(self: *RunStep, search_path: []const u8) void { | |
| 2096 | const env_map = self.getEnvMap(); | |
| 2097 | ||
| 2098 | var key: []const u8 = undefined; | |
| 2099 | var prev_path: ?[]const u8 = undefined; | |
| 2100 | if (builtin.os == .windows) { | |
| 2101 | key = "Path"; | |
| 2102 | prev_path = env_map.get(key); | |
| 2103 | if (prev_path == null) { | |
| 2104 | key = "PATH"; | |
| 2105 | prev_path = env_map.get(key); | |
| 2106 | } | |
| 2107 | } else { | |
| 2108 | key = "PATH"; | |
| 2109 | prev_path = env_map.get(key); | |
| 2110 | } | |
| 2111 | ||
| 2112 | if (prev_path) |pp| { | |
| 2113 | const new_path = self.builder.fmt("{}" ++ [1]u8{fs.path.delimiter} ++ "{}", .{ pp, search_path }); | |
| 2114 | env_map.set(key, new_path) catch unreachable; | |
| 2115 | } else { | |
| 2116 | env_map.set(key, search_path) catch unreachable; | |
| 2117 | } | |
| 2118 | } | |
| 2119 | ||
| 2120 | pub fn getEnvMap(self: *RunStep) *BufMap { | |
| 2121 | return self.env_map orelse { | |
| 2122 | const env_map = self.builder.allocator.create(BufMap) catch unreachable; | |
| 2123 | env_map.* = process.getEnvMap(self.builder.allocator) catch unreachable; | |
| 2124 | self.env_map = env_map; | |
| 2125 | return env_map; | |
| 2126 | }; | |
| 2127 | } | |
| 2128 | ||
| 2129 | pub fn setEnvironmentVariable(self: *RunStep, key: []const u8, value: []const u8) void { | |
| 2130 | const env_map = self.getEnvMap(); | |
| 2131 | env_map.set(key, value) catch unreachable; | |
| 2132 | } | |
| 2133 | ||
| 2134 | fn make(step: *Step) !void { | |
| 2135 | const self = @fieldParentPtr(RunStep, "step", step); | |
| 2136 | ||
| 2137 | const cwd = if (self.cwd) |cwd| self.builder.pathFromRoot(cwd) else self.builder.build_root; | |
| 2138 | ||
| 2139 | var argv = ArrayList([]const u8).init(self.builder.allocator); | |
| 2140 | for (self.argv.toSlice()) |arg| { | |
| 2141 | switch (arg) { | |
| 2142 | Arg.Bytes => |bytes| try argv.append(bytes), | |
| 2143 | Arg.Artifact => |artifact| { | |
| 2144 | if (artifact.target.isWindows()) { | |
| 2145 | // On Windows we don't have rpaths so we have to add .dll search paths to PATH | |
| 2146 | self.addPathForDynLibs(artifact); | |
| 2147 | } | |
| 2148 | const executable_path = artifact.installed_path orelse artifact.getOutputPath(); | |
| 2149 | try argv.append(executable_path); | |
| 2150 | }, | |
| 2151 | } | |
| 2152 | } | |
| 2153 | ||
| 2154 | return self.builder.spawnChildEnvMap(cwd, self.env_map orelse self.builder.env_map, argv.toSliceConst()); | |
| 2155 | } | |
| 2156 | ||
| 2157 | fn addPathForDynLibs(self: *RunStep, artifact: *LibExeObjStep) void { | |
| 2158 | for (artifact.link_objects.toSliceConst()) |link_object| { | |
| 2159 | switch (link_object) { | |
| 2160 | LibExeObjStep.LinkObject.OtherStep => |other| { | |
| 2161 | if (other.target.isWindows() and other.isDynamicLibrary()) { | |
| 2162 | self.addPathDir(fs.path.dirname(other.getOutputPath()).?); | |
| 2163 | self.addPathForDynLibs(other); | |
| 2164 | } | |
| 2165 | }, | |
| 2166 | else => {}, | |
| 2167 | } | |
| 2168 | } | |
| 2169 | } | |
| 2170 | }; | |
| 2171 | ||
| 2172 | 2156 | const InstallArtifactStep = struct { |
| 2173 | 2157 | step: Step, |
| 2174 | 2158 | builder: *Builder, |
| ... | ... | @@ -2321,36 +2305,6 @@ pub const InstallDirStep = struct { |
| 2321 | 2305 | } |
| 2322 | 2306 | }; |
| 2323 | 2307 | |
| 2324 | pub const WriteFileStep = struct { | |
| 2325 | step: Step, | |
| 2326 | builder: *Builder, | |
| 2327 | file_path: []const u8, | |
| 2328 | data: []const u8, | |
| 2329 | ||
| 2330 | pub fn init(builder: *Builder, file_path: []const u8, data: []const u8) WriteFileStep { | |
| 2331 | return WriteFileStep{ | |
| 2332 | .builder = builder, | |
| 2333 | .step = Step.init(builder.fmt("writefile {}", .{file_path}), builder.allocator, make), | |
| 2334 | .file_path = file_path, | |
| 2335 | .data = data, | |
| 2336 | }; | |
| 2337 | } | |
| 2338 | ||
| 2339 | fn make(step: *Step) !void { | |
| 2340 | const self = @fieldParentPtr(WriteFileStep, "step", step); | |
| 2341 | const full_path = self.builder.pathFromRoot(self.file_path); | |
| 2342 | const full_path_dir = fs.path.dirname(full_path) orelse "."; | |
| 2343 | fs.makePath(self.builder.allocator, full_path_dir) catch |err| { | |
| 2344 | warn("unable to make path {}: {}\n", .{ full_path_dir, @errorName(err) }); | |
| 2345 | return err; | |
| 2346 | }; | |
| 2347 | io.writeFile(full_path, self.data) catch |err| { | |
| 2348 | warn("unable to write {}: {}\n", .{ full_path, @errorName(err) }); | |
| 2349 | return err; | |
| 2350 | }; | |
| 2351 | } | |
| 2352 | }; | |
| 2353 | ||
| 2354 | 2308 | pub const LogStep = struct { |
| 2355 | 2309 | step: Step, |
| 2356 | 2310 | builder: *Builder, |
lib/std/build/check_file.zig created+52| ... | ... | @@ -0,0 +1,52 @@ |
| 1 | const std = @import("../std.zig"); | |
| 2 | const build = std.build; | |
| 3 | const Step = build.Step; | |
| 4 | const Builder = build.Builder; | |
| 5 | const fs = std.fs; | |
| 6 | const mem = std.mem; | |
| 7 | const warn = std.debug.warn; | |
| 8 | ||
| 9 | pub const CheckFileStep = struct { | |
| 10 | step: Step, | |
| 11 | builder: *Builder, | |
| 12 | expected_matches: []const []const u8, | |
| 13 | source: build.FileSource, | |
| 14 | max_bytes: usize = 20 * 1024 * 1024, | |
| 15 | ||
| 16 | pub fn create( | |
| 17 | builder: *Builder, | |
| 18 | source: build.FileSource, | |
| 19 | expected_matches: []const []const u8, | |
| 20 | ) *CheckFileStep { | |
| 21 | const self = builder.allocator.create(CheckFileStep) catch unreachable; | |
| 22 | self.* = CheckFileStep{ | |
| 23 | .builder = builder, | |
| 24 | .step = Step.init("CheckFile", builder.allocator, make), | |
| 25 | .source = source, | |
| 26 | .expected_matches = expected_matches, | |
| 27 | }; | |
| 28 | self.source.addStepDependencies(&self.step); | |
| 29 | return self; | |
| 30 | } | |
| 31 | ||
| 32 | fn make(step: *Step) !void { | |
| 33 | const self = @fieldParentPtr(CheckFileStep, "step", step); | |
| 34 | ||
| 35 | const src_path = self.source.getPath(self.builder); | |
| 36 | const contents = try fs.cwd().readFileAlloc(self.builder.allocator, src_path, self.max_bytes); | |
| 37 | ||
| 38 | for (self.expected_matches) |expected_match| { | |
| 39 | if (mem.indexOf(u8, contents, expected_match) == null) { | |
| 40 | warn( | |
| 41 | \\ | |
| 42 | \\========= Expected to find: =================== | |
| 43 | \\{} | |
| 44 | \\========= But file does not contain it: ======= | |
| 45 | \\{} | |
| 46 | \\ | |
| 47 | , .{ expected_match, contents }); | |
| 48 | return error.TestFailed; | |
| 49 | } | |
| 50 | } | |
| 51 | } | |
| 52 | }; |
lib/std/build/run.zig created+302| ... | ... | @@ -0,0 +1,302 @@ |
| 1 | const std = @import("../std.zig"); | |
| 2 | const builtin = std.builtin; | |
| 3 | const build = std.build; | |
| 4 | const Step = build.Step; | |
| 5 | const Builder = build.Builder; | |
| 6 | const LibExeObjStep = build.LibExeObjStep; | |
| 7 | const fs = std.fs; | |
| 8 | const mem = std.mem; | |
| 9 | const process = std.process; | |
| 10 | const ArrayList = std.ArrayList; | |
| 11 | const BufMap = std.BufMap; | |
| 12 | const Buffer = std.Buffer; | |
| 13 | const warn = std.debug.warn; | |
| 14 | ||
| 15 | const max_stdout_size = 1 * 1024 * 1024; // 1 MiB | |
| 16 | ||
| 17 | pub const RunStep = struct { | |
| 18 | step: Step, | |
| 19 | builder: *Builder, | |
| 20 | ||
| 21 | /// See also addArg and addArgs to modifying this directly | |
| 22 | argv: ArrayList(Arg), | |
| 23 | ||
| 24 | /// Set this to modify the current working directory | |
| 25 | cwd: ?[]const u8, | |
| 26 | ||
| 27 | /// Override this field to modify the environment, or use setEnvironmentVariable | |
| 28 | env_map: ?*BufMap, | |
| 29 | ||
| 30 | stdout_action: StdIoAction = .inherit, | |
| 31 | stderr_action: StdIoAction = .inherit, | |
| 32 | ||
| 33 | expected_exit_code: u8 = 0, | |
| 34 | ||
| 35 | pub const StdIoAction = union(enum) { | |
| 36 | inherit, | |
| 37 | ignore, | |
| 38 | expect_exact: []const u8, | |
| 39 | expect_matches: []const []const u8, | |
| 40 | }; | |
| 41 | ||
| 42 | pub const Arg = union(enum) { | |
| 43 | Artifact: *LibExeObjStep, | |
| 44 | Bytes: []u8, | |
| 45 | }; | |
| 46 | ||
| 47 | pub fn create(builder: *Builder, name: []const u8) *RunStep { | |
| 48 | const self = builder.allocator.create(RunStep) catch unreachable; | |
| 49 | self.* = RunStep{ | |
| 50 | .builder = builder, | |
| 51 | .step = Step.init(name, builder.allocator, make), | |
| 52 | .argv = ArrayList(Arg).init(builder.allocator), | |
| 53 | .cwd = null, | |
| 54 | .env_map = null, | |
| 55 | }; | |
| 56 | return self; | |
| 57 | } | |
| 58 | ||
| 59 | pub fn addArtifactArg(self: *RunStep, artifact: *LibExeObjStep) void { | |
| 60 | self.argv.append(Arg{ .Artifact = artifact }) catch unreachable; | |
| 61 | self.step.dependOn(&artifact.step); | |
| 62 | } | |
| 63 | ||
| 64 | pub fn addArg(self: *RunStep, arg: []const u8) void { | |
| 65 | self.argv.append(Arg{ .Bytes = self.builder.dupe(arg) }) catch unreachable; | |
| 66 | } | |
| 67 | ||
| 68 | pub fn addArgs(self: *RunStep, args: []const []const u8) void { | |
| 69 | for (args) |arg| { | |
| 70 | self.addArg(arg); | |
| 71 | } | |
| 72 | } | |
| 73 | ||
| 74 | pub fn clearEnvironment(self: *RunStep) void { | |
| 75 | const new_env_map = self.builder.allocator.create(BufMap) catch unreachable; | |
| 76 | new_env_map.* = BufMap.init(self.builder.allocator); | |
| 77 | self.env_map = new_env_map; | |
| 78 | } | |
| 79 | ||
| 80 | pub fn addPathDir(self: *RunStep, search_path: []const u8) void { | |
| 81 | const env_map = self.getEnvMap(); | |
| 82 | ||
| 83 | var key: []const u8 = undefined; | |
| 84 | var prev_path: ?[]const u8 = undefined; | |
| 85 | if (builtin.os == .windows) { | |
| 86 | key = "Path"; | |
| 87 | prev_path = env_map.get(key); | |
| 88 | if (prev_path == null) { | |
| 89 | key = "PATH"; | |
| 90 | prev_path = env_map.get(key); | |
| 91 | } | |
| 92 | } else { | |
| 93 | key = "PATH"; | |
| 94 | prev_path = env_map.get(key); | |
| 95 | } | |
| 96 | ||
| 97 | if (prev_path) |pp| { | |
| 98 | const new_path = self.builder.fmt("{}" ++ [1]u8{fs.path.delimiter} ++ "{}", .{ pp, search_path }); | |
| 99 | env_map.set(key, new_path) catch unreachable; | |
| 100 | } else { | |
| 101 | env_map.set(key, search_path) catch unreachable; | |
| 102 | } | |
| 103 | } | |
| 104 | ||
| 105 | pub fn getEnvMap(self: *RunStep) *BufMap { | |
| 106 | return self.env_map orelse { | |
| 107 | const env_map = self.builder.allocator.create(BufMap) catch unreachable; | |
| 108 | env_map.* = process.getEnvMap(self.builder.allocator) catch unreachable; | |
| 109 | self.env_map = env_map; | |
| 110 | return env_map; | |
| 111 | }; | |
| 112 | } | |
| 113 | ||
| 114 | pub fn setEnvironmentVariable(self: *RunStep, key: []const u8, value: []const u8) void { | |
| 115 | const env_map = self.getEnvMap(); | |
| 116 | env_map.set(key, value) catch unreachable; | |
| 117 | } | |
| 118 | ||
| 119 | pub fn expectStdErrEqual(self: *RunStep, bytes: []const u8) void { | |
| 120 | self.stderr_action = .{ .expect_exact = bytes }; | |
| 121 | } | |
| 122 | ||
| 123 | pub fn expectStdOutEqual(self: *RunStep, bytes: []const u8) void { | |
| 124 | self.stdout_action = .{ .expect_exact = bytes }; | |
| 125 | } | |
| 126 | ||
| 127 | fn stdIoActionToBehavior(action: StdIoAction) std.ChildProcess.StdIo { | |
| 128 | return switch (action) { | |
| 129 | .ignore => .Ignore, | |
| 130 | .inherit => .Inherit, | |
| 131 | .expect_exact, .expect_matches => .Pipe, | |
| 132 | }; | |
| 133 | } | |
| 134 | ||
| 135 | fn make(step: *Step) !void { | |
| 136 | const self = @fieldParentPtr(RunStep, "step", step); | |
| 137 | ||
| 138 | const cwd = if (self.cwd) |cwd| self.builder.pathFromRoot(cwd) else self.builder.build_root; | |
| 139 | ||
| 140 | var argv_list = ArrayList([]const u8).init(self.builder.allocator); | |
| 141 | for (self.argv.toSlice()) |arg| { | |
| 142 | switch (arg) { | |
| 143 | Arg.Bytes => |bytes| try argv_list.append(bytes), | |
| 144 | Arg.Artifact => |artifact| { | |
| 145 | if (artifact.target.isWindows()) { | |
| 146 | // On Windows we don't have rpaths so we have to add .dll search paths to PATH | |
| 147 | self.addPathForDynLibs(artifact); | |
| 148 | } | |
| 149 | const executable_path = artifact.installed_path orelse artifact.getOutputPath(); | |
| 150 | try argv_list.append(executable_path); | |
| 151 | }, | |
| 152 | } | |
| 153 | } | |
| 154 | ||
| 155 | const argv = argv_list.toSliceConst(); | |
| 156 | ||
| 157 | const child = std.ChildProcess.init(argv, self.builder.allocator) catch unreachable; | |
| 158 | defer child.deinit(); | |
| 159 | ||
| 160 | child.cwd = cwd; | |
| 161 | child.env_map = self.env_map orelse self.builder.env_map; | |
| 162 | ||
| 163 | child.stdin_behavior = .Ignore; | |
| 164 | child.stdout_behavior = stdIoActionToBehavior(self.stdout_action); | |
| 165 | child.stderr_behavior = stdIoActionToBehavior(self.stderr_action); | |
| 166 | ||
| 167 | child.spawn() catch |err| { | |
| 168 | warn("Unable to spawn {}: {}\n", .{ argv[0], @errorName(err) }); | |
| 169 | return err; | |
| 170 | }; | |
| 171 | ||
| 172 | var stdout = Buffer.initNull(self.builder.allocator); | |
| 173 | var stderr = Buffer.initNull(self.builder.allocator); | |
| 174 | ||
| 175 | // TODO need to poll to read these streams to prevent a deadlock (or rely on evented I/O). | |
| 176 | ||
| 177 | switch (self.stdout_action) { | |
| 178 | .expect_exact, .expect_matches => { | |
| 179 | var stdout_file_in_stream = child.stdout.?.inStream(); | |
| 180 | stdout_file_in_stream.stream.readAllBuffer(&stdout, max_stdout_size) catch unreachable; | |
| 181 | }, | |
| 182 | .inherit, .ignore => {}, | |
| 183 | } | |
| 184 | ||
| 185 | switch (self.stdout_action) { | |
| 186 | .expect_exact, .expect_matches => { | |
| 187 | var stderr_file_in_stream = child.stderr.?.inStream(); | |
| 188 | stderr_file_in_stream.stream.readAllBuffer(&stderr, max_stdout_size) catch unreachable; | |
| 189 | }, | |
| 190 | .inherit, .ignore => {}, | |
| 191 | } | |
| 192 | ||
| 193 | const term = child.wait() catch |err| { | |
| 194 | warn("Unable to spawn {}: {}\n", .{ argv[0], @errorName(err) }); | |
| 195 | return err; | |
| 196 | }; | |
| 197 | ||
| 198 | switch (term) { | |
| 199 | .Exited => |code| { | |
| 200 | if (code != self.expected_exit_code) { | |
| 201 | warn("The following command exited with error code {} (expected {}):\n", .{ | |
| 202 | code, | |
| 203 | self.expected_exit_code, | |
| 204 | }); | |
| 205 | printCmd(cwd, argv); | |
| 206 | return error.UncleanExit; | |
| 207 | } | |
| 208 | }, | |
| 209 | else => { | |
| 210 | warn("The following command terminated unexpectedly:\n", .{}); | |
| 211 | printCmd(cwd, argv); | |
| 212 | return error.UncleanExit; | |
| 213 | }, | |
| 214 | } | |
| 215 | ||
| 216 | switch (self.stderr_action) { | |
| 217 | .inherit, .ignore => {}, | |
| 218 | .expect_exact => |expected_bytes| { | |
| 219 | if (!mem.eql(u8, expected_bytes, stderr.toSliceConst())) { | |
| 220 | warn( | |
| 221 | \\ | |
| 222 | \\========= Expected this stderr: ========= | |
| 223 | \\{} | |
| 224 | \\========= But found: ==================== | |
| 225 | \\{} | |
| 226 | \\ | |
| 227 | , .{ expected_bytes, stderr.toSliceConst() }); | |
| 228 | printCmd(cwd, argv); | |
| 229 | return error.TestFailed; | |
| 230 | } | |
| 231 | }, | |
| 232 | .expect_matches => |matches| for (matches) |match| { | |
| 233 | if (mem.indexOf(u8, stderr.toSliceConst(), match) == null) { | |
| 234 | warn( | |
| 235 | \\ | |
| 236 | \\========= Expected to find in stderr: ========= | |
| 237 | \\{} | |
| 238 | \\========= But stderr does not contain it: ===== | |
| 239 | \\{} | |
| 240 | \\ | |
| 241 | , .{ match, stderr.toSliceConst() }); | |
| 242 | printCmd(cwd, argv); | |
| 243 | return error.TestFailed; | |
| 244 | } | |
| 245 | }, | |
| 246 | } | |
| 247 | ||
| 248 | switch (self.stdout_action) { | |
| 249 | .inherit, .ignore => {}, | |
| 250 | .expect_exact => |expected_bytes| { | |
| 251 | if (!mem.eql(u8, expected_bytes, stdout.toSliceConst())) { | |
| 252 | warn( | |
| 253 | \\ | |
| 254 | \\========= Expected this stdout: ========= | |
| 255 | \\{} | |
| 256 | \\========= But found: ==================== | |
| 257 | \\{} | |
| 258 | \\ | |
| 259 | , .{ expected_bytes, stdout.toSliceConst() }); | |
| 260 | printCmd(cwd, argv); | |
| 261 | return error.TestFailed; | |
| 262 | } | |
| 263 | }, | |
| 264 | .expect_matches => |matches| for (matches) |match| { | |
| 265 | if (mem.indexOf(u8, stdout.toSliceConst(), match) == null) { | |
| 266 | warn( | |
| 267 | \\ | |
| 268 | \\========= Expected to find in stdout: ========= | |
| 269 | \\{} | |
| 270 | \\========= But stdout does not contain it: ===== | |
| 271 | \\{} | |
| 272 | \\ | |
| 273 | , .{ match, stdout.toSliceConst() }); | |
| 274 | printCmd(cwd, argv); | |
| 275 | return error.TestFailed; | |
| 276 | } | |
| 277 | }, | |
| 278 | } | |
| 279 | } | |
| 280 | ||
| 281 | fn printCmd(cwd: ?[]const u8, argv: []const []const u8) void { | |
| 282 | if (cwd) |yes_cwd| warn("cd {} && ", .{yes_cwd}); | |
| 283 | for (argv) |arg| { | |
| 284 | warn("{} ", .{arg}); | |
| 285 | } | |
| 286 | warn("\n", .{}); | |
| 287 | } | |
| 288 | ||
| 289 | fn addPathForDynLibs(self: *RunStep, artifact: *LibExeObjStep) void { | |
| 290 | for (artifact.link_objects.toSliceConst()) |link_object| { | |
| 291 | switch (link_object) { | |
| 292 | .OtherStep => |other| { | |
| 293 | if (other.target.isWindows() and other.isDynamicLibrary()) { | |
| 294 | self.addPathDir(fs.path.dirname(other.getOutputPath()).?); | |
| 295 | self.addPathForDynLibs(other); | |
| 296 | } | |
| 297 | }, | |
| 298 | else => {}, | |
| 299 | } | |
| 300 | } | |
| 301 | } | |
| 302 | }; |
lib/std/build/translate_c.zig created+73| ... | ... | @@ -0,0 +1,73 @@ |
| 1 | const std = @import("../std.zig"); | |
| 2 | const build = std.build; | |
| 3 | const Step = build.Step; | |
| 4 | const Builder = build.Builder; | |
| 5 | const WriteFileStep = build.WriteFileStep; | |
| 6 | const LibExeObjStep = build.LibExeObjStep; | |
| 7 | const CheckFileStep = build.CheckFileStep; | |
| 8 | const fs = std.fs; | |
| 9 | const mem = std.mem; | |
| 10 | ||
| 11 | pub const TranslateCStep = struct { | |
| 12 | step: Step, | |
| 13 | builder: *Builder, | |
| 14 | source: build.FileSource, | |
| 15 | output_dir: ?[]const u8, | |
| 16 | out_basename: []const u8, | |
| 17 | ||
| 18 | pub fn create(builder: *Builder, source: build.FileSource) *TranslateCStep { | |
| 19 | const self = builder.allocator.create(TranslateCStep) catch unreachable; | |
| 20 | self.* = TranslateCStep{ | |
| 21 | .step = Step.init("zig translate-c", builder.allocator, make), | |
| 22 | .builder = builder, | |
| 23 | .source = source, | |
| 24 | .output_dir = null, | |
| 25 | .out_basename = undefined, | |
| 26 | }; | |
| 27 | source.addStepDependencies(&self.step); | |
| 28 | return self; | |
| 29 | } | |
| 30 | ||
| 31 | /// Unless setOutputDir was called, this function must be called only in | |
| 32 | /// the make step, from a step that has declared a dependency on this one. | |
| 33 | /// To run an executable built with zig build, use `run`, or create an install step and invoke it. | |
| 34 | pub fn getOutputPath(self: *TranslateCStep) []const u8 { | |
| 35 | return fs.path.join( | |
| 36 | self.builder.allocator, | |
| 37 | &[_][]const u8{ self.output_dir.?, self.out_basename }, | |
| 38 | ) catch unreachable; | |
| 39 | } | |
| 40 | ||
| 41 | /// Creates a step to build an executable from the translated source. | |
| 42 | pub fn addExecutable(self: *TranslateCStep) *LibExeObjStep { | |
| 43 | return self.builder.addExecutableSource("translated_c", @as(build.FileSource, .{ .translate_c = self })); | |
| 44 | } | |
| 45 | ||
| 46 | pub fn addCheckFile(self: *TranslateCStep, expected_matches: []const []const u8) *CheckFileStep { | |
| 47 | return CheckFileStep.create(self.builder, .{ .translate_c = self }, expected_matches); | |
| 48 | } | |
| 49 | ||
| 50 | fn make(step: *Step) !void { | |
| 51 | const self = @fieldParentPtr(TranslateCStep, "step", step); | |
| 52 | ||
| 53 | const argv = [_][]const u8{ | |
| 54 | self.builder.zig_exe, | |
| 55 | "translate-c", | |
| 56 | "-lc", | |
| 57 | "--cache", | |
| 58 | "on", | |
| 59 | self.source.getPath(self.builder), | |
| 60 | }; | |
| 61 | ||
| 62 | const output_path_nl = try self.builder.exec(&argv); | |
| 63 | const output_path = mem.trimRight(u8, output_path_nl, "\r\n"); | |
| 64 | ||
| 65 | self.out_basename = fs.path.basename(output_path); | |
| 66 | if (self.output_dir) |output_dir| { | |
| 67 | const full_dest = try fs.path.join(self.builder.allocator, &[_][]const u8{ output_dir, self.out_basename }); | |
| 68 | try self.builder.updateFile(output_path, full_dest); | |
| 69 | } else { | |
| 70 | self.output_dir = fs.path.dirname(output_path).?; | |
| 71 | } | |
| 72 | } | |
| 73 | }; |
lib/std/build/write_file.zig created+94| ... | ... | @@ -0,0 +1,94 @@ |
| 1 | const std = @import("../std.zig"); | |
| 2 | const build = @import("../build.zig"); | |
| 3 | const Step = build.Step; | |
| 4 | const Builder = build.Builder; | |
| 5 | const fs = std.fs; | |
| 6 | const warn = std.debug.warn; | |
| 7 | const ArrayList = std.ArrayList; | |
| 8 | ||
| 9 | pub const WriteFileStep = struct { | |
| 10 | step: Step, | |
| 11 | builder: *Builder, | |
| 12 | output_dir: []const u8, | |
| 13 | files: ArrayList(File), | |
| 14 | ||
| 15 | pub const File = struct { | |
| 16 | basename: []const u8, | |
| 17 | bytes: []const u8, | |
| 18 | }; | |
| 19 | ||
| 20 | pub fn init(builder: *Builder) WriteFileStep { | |
| 21 | return WriteFileStep{ | |
| 22 | .builder = builder, | |
| 23 | .step = Step.init("writefile", builder.allocator, make), | |
| 24 | .files = ArrayList(File).init(builder.allocator), | |
| 25 | .output_dir = undefined, | |
| 26 | }; | |
| 27 | } | |
| 28 | ||
| 29 | pub fn add(self: *WriteFileStep, basename: []const u8, bytes: []const u8) void { | |
| 30 | self.files.append(.{ .basename = basename, .bytes = bytes }) catch unreachable; | |
| 31 | } | |
| 32 | ||
| 33 | /// Unless setOutputDir was called, this function must be called only in | |
| 34 | /// the make step, from a step that has declared a dependency on this one. | |
| 35 | /// To run an executable built with zig build, use `run`, or create an install step and invoke it. | |
| 36 | pub fn getOutputPath(self: *WriteFileStep, basename: []const u8) []const u8 { | |
| 37 | return fs.path.join( | |
| 38 | self.builder.allocator, | |
| 39 | &[_][]const u8{ self.output_dir, basename }, | |
| 40 | ) catch unreachable; | |
| 41 | } | |
| 42 | ||
| 43 | fn make(step: *Step) !void { | |
| 44 | const self = @fieldParentPtr(WriteFileStep, "step", step); | |
| 45 | ||
| 46 | // The cache is used here not really as a way to speed things up - because writing | |
| 47 | // the data to a file would probably be very fast - but as a way to find a canonical | |
| 48 | // location to put build artifacts. | |
| 49 | ||
| 50 | // If, for example, a hard-coded path was used as the location to put WriteFileStep | |
| 51 | // files, then two WriteFileSteps executing in parallel might clobber each other. | |
| 52 | ||
| 53 | // TODO port the cache system from stage1 to zig std lib. Until then we use blake2b | |
| 54 | // directly and construct the path, and no "cache hit" detection happens; the files | |
| 55 | // are always written. | |
| 56 | var hash = std.crypto.Blake2b384.init(); | |
| 57 | ||
| 58 | // Random bytes to make WriteFileStep unique. Refresh this with | |
| 59 | // new random bytes when WriteFileStep implementation is modified | |
| 60 | // in a non-backwards-compatible way. | |
| 61 | hash.update("eagVR1dYXoE7ARDP"); | |
| 62 | for (self.files.toSliceConst()) |file| { | |
| 63 | hash.update(file.basename); | |
| 64 | hash.update(file.bytes); | |
| 65 | hash.update("|"); | |
| 66 | } | |
| 67 | var digest: [48]u8 = undefined; | |
| 68 | hash.final(&digest); | |
| 69 | var hash_basename: [64]u8 = undefined; | |
| 70 | fs.base64_encoder.encode(&hash_basename, &digest); | |
| 71 | self.output_dir = try fs.path.join(self.builder.allocator, &[_][]const u8{ | |
| 72 | self.builder.cache_root, | |
| 73 | "o", | |
| 74 | &hash_basename, | |
| 75 | }); | |
| 76 | // TODO replace with something like fs.makePathAndOpenDir | |
| 77 | fs.makePath(self.builder.allocator, self.output_dir) catch |err| { | |
| 78 | warn("unable to make path {}: {}\n", .{ self.output_dir, @errorName(err) }); | |
| 79 | return err; | |
| 80 | }; | |
| 81 | var dir = try fs.cwd().openDirTraverse(self.output_dir); | |
| 82 | defer dir.close(); | |
| 83 | for (self.files.toSliceConst()) |file| { | |
| 84 | dir.writeFile(file.basename, file.bytes) catch |err| { | |
| 85 | warn("unable to write {} into {}: {}\n", .{ | |
| 86 | file.basename, | |
| 87 | self.output_dir, | |
| 88 | @errorName(err), | |
| 89 | }); | |
| 90 | return err; | |
| 91 | }; | |
| 92 | } | |
| 93 | } | |
| 94 | }; |
lib/std/fs.zig+8-5| ... | ... | @@ -37,8 +37,11 @@ pub const MAX_PATH_BYTES = switch (builtin.os) { |
| 37 | 37 | else => @compileError("Unsupported OS"), |
| 38 | 38 | }; |
| 39 | 39 | |
| 40 | // here we replace the standard +/ with -_ so that it can be used in a file name | |
| 41 | const b64_fs_encoder = base64.Base64Encoder.init("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_", base64.standard_pad_char); | |
| 40 | /// Base64, replacing the standard `+/` with `-_` so that it can be used in a file name on any filesystem. | |
| 41 | pub const base64_encoder = base64.Base64Encoder.init( | |
| 42 | "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_", | |
| 43 | base64.standard_pad_char, | |
| 44 | ); | |
| 42 | 45 | |
| 43 | 46 | /// TODO remove the allocator requirement from this API |
| 44 | 47 | pub fn atomicSymLink(allocator: *Allocator, existing_path: []const u8, new_path: []const u8) !void { |
| ... | ... | @@ -58,7 +61,7 @@ pub fn atomicSymLink(allocator: *Allocator, existing_path: []const u8, new_path: |
| 58 | 61 | tmp_path[dirname.len] = path.sep; |
| 59 | 62 | while (true) { |
| 60 | 63 | try crypto.randomBytes(rand_buf[0..]); |
| 61 | b64_fs_encoder.encode(tmp_path[dirname.len + 1 ..], &rand_buf); | |
| 64 | base64_encoder.encode(tmp_path[dirname.len + 1 ..], &rand_buf); | |
| 62 | 65 | |
| 63 | 66 | if (symLink(existing_path, tmp_path)) { |
| 64 | 67 | return rename(tmp_path, new_path); |
| ... | ... | @@ -227,10 +230,10 @@ pub const AtomicFile = struct { |
| 227 | 230 | |
| 228 | 231 | while (true) { |
| 229 | 232 | try crypto.randomBytes(rand_buf[0..]); |
| 230 | b64_fs_encoder.encode(tmp_path_slice[dirname_component_len..tmp_path_len], &rand_buf); | |
| 233 | base64_encoder.encode(tmp_path_slice[dirname_component_len..tmp_path_len], &rand_buf); | |
| 231 | 234 | |
| 232 | 235 | const file = my_cwd.createFileC( |
| 233 | tmp_path_slice, | |
| 236 | tmp_path_slice, | |
| 234 | 237 | .{ .mode = mode, .exclusive = true }, |
| 235 | 238 | ) catch |err| switch (err) { |
| 236 | 239 | error.PathAlreadyExists => continue, |
src/codegen.cpp+96-2| ... | ... | @@ -194,6 +194,7 @@ static LLVMValueRef build_alloca(CodeGen *g, ZigType *type_entry, const char *na |
| 194 | 194 | static LLVMValueRef gen_await_early_return(CodeGen *g, IrInstruction *source_instr, |
| 195 | 195 | LLVMValueRef target_frame_ptr, ZigType *result_type, ZigType *ptr_result_type, |
| 196 | 196 | LLVMValueRef result_loc, bool non_async); |
| 197 | static Error get_tmp_filename(CodeGen *g, Buf *out, Buf *suffix); | |
| 197 | 198 | |
| 198 | 199 | static void addLLVMAttr(LLVMValueRef val, LLVMAttributeIndex attr_index, const char *attr_name) { |
| 199 | 200 | unsigned kind_id = LLVMGetEnumAttributeKindForName(attr_name, strlen(attr_name)); |
| ... | ... | @@ -9102,8 +9103,9 @@ void add_cc_args(CodeGen *g, ZigList<const char *> &args, const char *out_dep_pa |
| 9102 | 9103 | |
| 9103 | 9104 | } |
| 9104 | 9105 | |
| 9105 | void codegen_translate_c(CodeGen *g, Buf *full_path, FILE *out_file) { | |
| 9106 | void codegen_translate_c(CodeGen *g, Buf *full_path) { | |
| 9106 | 9107 | Error err; |
| 9108 | ||
| 9107 | 9109 | Buf *src_basename = buf_alloc(); |
| 9108 | 9110 | Buf *src_dirname = buf_alloc(); |
| 9109 | 9111 | os_path_split(full_path, src_dirname, src_basename); |
| ... | ... | @@ -9111,12 +9113,61 @@ void codegen_translate_c(CodeGen *g, Buf *full_path, FILE *out_file) { |
| 9111 | 9113 | Buf noextname = BUF_INIT; |
| 9112 | 9114 | os_path_extname(src_basename, &noextname, nullptr); |
| 9113 | 9115 | |
| 9116 | Buf *zig_basename = buf_sprintf("%s.zig", buf_ptr(&noextname)); | |
| 9117 | ||
| 9114 | 9118 | detect_libc(g); |
| 9115 | 9119 | |
| 9120 | Buf cache_digest = BUF_INIT; | |
| 9121 | buf_resize(&cache_digest, 0); | |
| 9122 | ||
| 9123 | CacheHash *cache_hash = nullptr; | |
| 9124 | if (g->enable_cache) { | |
| 9125 | if ((err = create_c_object_cache(g, &cache_hash, true))) { | |
| 9126 | // Already printed error; verbose = true | |
| 9127 | exit(1); | |
| 9128 | } | |
| 9129 | cache_file(cache_hash, full_path); | |
| 9130 | // to distinguish from generating a C object | |
| 9131 | cache_buf(cache_hash, buf_create_from_str("translate-c")); | |
| 9132 | ||
| 9133 | if ((err = cache_hit(cache_hash, &cache_digest))) { | |
| 9134 | if (err != ErrorInvalidFormat) { | |
| 9135 | fprintf(stderr, "unable to check cache: %s\n", err_str(err)); | |
| 9136 | exit(1); | |
| 9137 | } | |
| 9138 | } | |
| 9139 | if (cache_hash->manifest_file_path != nullptr) { | |
| 9140 | g->caches_to_release.append(cache_hash); | |
| 9141 | } | |
| 9142 | } | |
| 9143 | ||
| 9144 | if (g->enable_cache && buf_len(&cache_digest) != 0) { | |
| 9145 | // cache hit | |
| 9146 | Buf *cached_path = buf_sprintf("%s" OS_SEP CACHE_OUT_SUBDIR OS_SEP "%s" OS_SEP "%s", | |
| 9147 | buf_ptr(g->cache_dir), buf_ptr(&cache_digest), buf_ptr(zig_basename)); | |
| 9148 | fprintf(stdout, "%s\n", buf_ptr(cached_path)); | |
| 9149 | return; | |
| 9150 | } | |
| 9151 | ||
| 9152 | // cache miss or cache disabled | |
| 9116 | 9153 | init(g); |
| 9117 | 9154 | |
| 9155 | Buf *out_dep_path = nullptr; | |
| 9156 | const char *out_dep_path_cstr = nullptr; | |
| 9157 | ||
| 9158 | if (g->enable_cache) { | |
| 9159 | buf_alloc();// we can't know the digest until we do the C compiler invocation, so we | |
| 9160 | // need a tmp filename. | |
| 9161 | out_dep_path = buf_alloc(); | |
| 9162 | if ((err = get_tmp_filename(g, out_dep_path, buf_sprintf("%s.d", buf_ptr(zig_basename))))) { | |
| 9163 | fprintf(stderr, "unable to create tmp dir: %s\n", err_str(err)); | |
| 9164 | exit(1); | |
| 9165 | } | |
| 9166 | out_dep_path_cstr = buf_ptr(out_dep_path); | |
| 9167 | } | |
| 9168 | ||
| 9118 | 9169 | ZigList<const char *> clang_argv = {0}; |
| 9119 | add_cc_args(g, clang_argv, nullptr, true); | |
| 9170 | add_cc_args(g, clang_argv, out_dep_path_cstr, true); | |
| 9120 | 9171 | |
| 9121 | 9172 | clang_argv.append(buf_ptr(full_path)); |
| 9122 | 9173 | |
| ... | ... | @@ -9160,7 +9211,50 @@ void codegen_translate_c(CodeGen *g, Buf *full_path, FILE *out_file) { |
| 9160 | 9211 | exit(1); |
| 9161 | 9212 | } |
| 9162 | 9213 | |
| 9214 | if (!g->enable_cache) { | |
| 9215 | stage2_render_ast(ast, stdout); | |
| 9216 | return; | |
| 9217 | } | |
| 9218 | ||
| 9219 | // add the files depended on to the cache system | |
| 9220 | if ((err = cache_add_dep_file(cache_hash, out_dep_path, true))) { | |
| 9221 | // Don't treat the absence of the .d file as a fatal error, the | |
| 9222 | // compiler may not produce one eg. when compiling .s files | |
| 9223 | if (err != ErrorFileNotFound) { | |
| 9224 | fprintf(stderr, "Failed to add C source dependencies to cache: %s\n", err_str(err)); | |
| 9225 | exit(1); | |
| 9226 | } | |
| 9227 | } | |
| 9228 | if (err != ErrorFileNotFound) { | |
| 9229 | os_delete_file(out_dep_path); | |
| 9230 | } | |
| 9231 | ||
| 9232 | if ((err = cache_final(cache_hash, &cache_digest))) { | |
| 9233 | fprintf(stderr, "Unable to finalize cache hash: %s\n", err_str(err)); | |
| 9234 | exit(1); | |
| 9235 | } | |
| 9236 | ||
| 9237 | Buf *artifact_dir = buf_sprintf("%s" OS_SEP CACHE_OUT_SUBDIR OS_SEP "%s", | |
| 9238 | buf_ptr(g->cache_dir), buf_ptr(&cache_digest)); | |
| 9239 | ||
| 9240 | if ((err = os_make_path(artifact_dir))) { | |
| 9241 | fprintf(stderr, "Unable to make dir: %s\n", err_str(err)); | |
| 9242 | exit(1); | |
| 9243 | } | |
| 9244 | ||
| 9245 | Buf *cached_path = buf_sprintf("%s" OS_SEP "%s", buf_ptr(artifact_dir), buf_ptr(zig_basename)); | |
| 9246 | ||
| 9247 | FILE *out_file = fopen(buf_ptr(cached_path), "wb"); | |
| 9248 | if (out_file == nullptr) { | |
| 9249 | fprintf(stderr, "Unable to open output file: %s\n", strerror(errno)); | |
| 9250 | exit(1); | |
| 9251 | } | |
| 9163 | 9252 | stage2_render_ast(ast, out_file); |
| 9253 | if (fclose(out_file) != 0) { | |
| 9254 | fprintf(stderr, "Unable to write to output file: %s\n", strerror(errno)); | |
| 9255 | exit(1); | |
| 9256 | } | |
| 9257 | fprintf(stdout, "%s\n", buf_ptr(cached_path)); | |
| 9164 | 9258 | } |
| 9165 | 9259 | |
| 9166 | 9260 | static void update_test_functions_builtin_decl(CodeGen *g) { |
src/codegen.hpp+1-1| ... | ... | @@ -54,7 +54,7 @@ ZigPackage *codegen_create_package(CodeGen *g, const char *root_src_dir, const c |
| 54 | 54 | void codegen_add_assembly(CodeGen *g, Buf *path); |
| 55 | 55 | void codegen_add_object(CodeGen *g, Buf *object_path); |
| 56 | 56 | |
| 57 | void codegen_translate_c(CodeGen *g, Buf *full_path, FILE *out_file); | |
| 57 | void codegen_translate_c(CodeGen *g, Buf *full_path); | |
| 58 | 58 | |
| 59 | 59 | Buf *codegen_generate_builtin_source(CodeGen *g); |
| 60 | 60 |
src/main.cpp+3-2| ... | ... | @@ -1164,7 +1164,7 @@ int main(int argc, char **argv) { |
| 1164 | 1164 | return print_error_usage(arg0); |
| 1165 | 1165 | } |
| 1166 | 1166 | |
| 1167 | Buf *zig_root_source_file = cmd == CmdTranslateC ? nullptr : in_file_buf; | |
| 1167 | Buf *zig_root_source_file = (cmd == CmdTranslateC) ? nullptr : in_file_buf; | |
| 1168 | 1168 | |
| 1169 | 1169 | if (cmd == CmdRun && buf_out_name == nullptr) { |
| 1170 | 1170 | buf_out_name = buf_create_from_str("run"); |
| ... | ... | @@ -1330,7 +1330,8 @@ int main(int argc, char **argv) { |
| 1330 | 1330 | zig_unreachable(); |
| 1331 | 1331 | } |
| 1332 | 1332 | } else if (cmd == CmdTranslateC) { |
| 1333 | codegen_translate_c(g, in_file_buf, stdout); | |
| 1333 | g->enable_cache = get_cache_opt(enable_cache, false); | |
| 1334 | codegen_translate_c(g, in_file_buf); | |
| 1334 | 1335 | if (timing_info) |
| 1335 | 1336 | codegen_print_timing_report(g, stderr); |
| 1336 | 1337 | return main_exit(root_progress_node, EXIT_SUCCESS); |
test/run_translated_c.zig created+26| ... | ... | @@ -0,0 +1,26 @@ |
| 1 | const std = @import("std"); | |
| 2 | const tests = @import("tests.zig"); | |
| 3 | const nl = std.cstr.line_sep; | |
| 4 | ||
| 5 | pub fn addCases(cases: *tests.RunTranslatedCContext) void { | |
| 6 | cases.add("hello world", | |
| 7 | \\#define _NO_CRT_STDIO_INLINE 1 | |
| 8 | \\#include <stdio.h> | |
| 9 | \\int main(int argc, char **argv) { | |
| 10 | \\ printf("hello, world!\n"); | |
| 11 | \\ return 0; | |
| 12 | \\} | |
| 13 | , "hello, world!" ++ nl); | |
| 14 | ||
| 15 | cases.add("anon struct init", | |
| 16 | \\#include <stdlib.h> | |
| 17 | \\struct {int a; int b;} x = {1, 2}; | |
| 18 | \\int main(int argc, char **argv) { | |
| 19 | \\ x.a += 2; | |
| 20 | \\ x.b += 1; | |
| 21 | \\ if (x.a != 3) abort(); | |
| 22 | \\ if (x.b != 3) abort(); | |
| 23 | \\ return 0; | |
| 24 | \\} | |
| 25 | , ""); | |
| 26 | } |
test/src/compare_output.zig created+165| ... | ... | @@ -0,0 +1,165 @@ |
| 1 | // This is the implementation of the test harness. | |
| 2 | // For the actual test cases, see test/compare_output.zig. | |
| 3 | const std = @import("std"); | |
| 4 | const builtin = std.builtin; | |
| 5 | const build = std.build; | |
| 6 | const ArrayList = std.ArrayList; | |
| 7 | const fmt = std.fmt; | |
| 8 | const mem = std.mem; | |
| 9 | const fs = std.fs; | |
| 10 | const warn = std.debug.warn; | |
| 11 | const Mode = builtin.Mode; | |
| 12 | ||
| 13 | pub const CompareOutputContext = struct { | |
| 14 | b: *build.Builder, | |
| 15 | step: *build.Step, | |
| 16 | test_index: usize, | |
| 17 | test_filter: ?[]const u8, | |
| 18 | modes: []const Mode, | |
| 19 | ||
| 20 | const Special = enum { | |
| 21 | None, | |
| 22 | Asm, | |
| 23 | RuntimeSafety, | |
| 24 | }; | |
| 25 | ||
| 26 | const TestCase = struct { | |
| 27 | name: []const u8, | |
| 28 | sources: ArrayList(SourceFile), | |
| 29 | expected_output: []const u8, | |
| 30 | link_libc: bool, | |
| 31 | special: Special, | |
| 32 | cli_args: []const []const u8, | |
| 33 | ||
| 34 | const SourceFile = struct { | |
| 35 | filename: []const u8, | |
| 36 | source: []const u8, | |
| 37 | }; | |
| 38 | ||
| 39 | pub fn addSourceFile(self: *TestCase, filename: []const u8, source: []const u8) void { | |
| 40 | self.sources.append(SourceFile{ | |
| 41 | .filename = filename, | |
| 42 | .source = source, | |
| 43 | }) catch unreachable; | |
| 44 | } | |
| 45 | ||
| 46 | pub fn setCommandLineArgs(self: *TestCase, args: []const []const u8) void { | |
| 47 | self.cli_args = args; | |
| 48 | } | |
| 49 | }; | |
| 50 | ||
| 51 | pub fn createExtra(self: *CompareOutputContext, name: []const u8, source: []const u8, expected_output: []const u8, special: Special) TestCase { | |
| 52 | var tc = TestCase{ | |
| 53 | .name = name, | |
| 54 | .sources = ArrayList(TestCase.SourceFile).init(self.b.allocator), | |
| 55 | .expected_output = expected_output, | |
| 56 | .link_libc = false, | |
| 57 | .special = special, | |
| 58 | .cli_args = &[_][]const u8{}, | |
| 59 | }; | |
| 60 | const root_src_name = if (special == Special.Asm) "source.s" else "source.zig"; | |
| 61 | tc.addSourceFile(root_src_name, source); | |
| 62 | return tc; | |
| 63 | } | |
| 64 | ||
| 65 | pub fn create(self: *CompareOutputContext, name: []const u8, source: []const u8, expected_output: []const u8) TestCase { | |
| 66 | return createExtra(self, name, source, expected_output, Special.None); | |
| 67 | } | |
| 68 | ||
| 69 | pub fn addC(self: *CompareOutputContext, name: []const u8, source: []const u8, expected_output: []const u8) void { | |
| 70 | var tc = self.create(name, source, expected_output); | |
| 71 | tc.link_libc = true; | |
| 72 | self.addCase(tc); | |
| 73 | } | |
| 74 | ||
| 75 | pub fn add(self: *CompareOutputContext, name: []const u8, source: []const u8, expected_output: []const u8) void { | |
| 76 | const tc = self.create(name, source, expected_output); | |
| 77 | self.addCase(tc); | |
| 78 | } | |
| 79 | ||
| 80 | pub fn addAsm(self: *CompareOutputContext, name: []const u8, source: []const u8, expected_output: []const u8) void { | |
| 81 | const tc = self.createExtra(name, source, expected_output, Special.Asm); | |
| 82 | self.addCase(tc); | |
| 83 | } | |
| 84 | ||
| 85 | pub fn addRuntimeSafety(self: *CompareOutputContext, name: []const u8, source: []const u8) void { | |
| 86 | const tc = self.createExtra(name, source, undefined, Special.RuntimeSafety); | |
| 87 | self.addCase(tc); | |
| 88 | } | |
| 89 | ||
| 90 | pub fn addCase(self: *CompareOutputContext, case: TestCase) void { | |
| 91 | const b = self.b; | |
| 92 | ||
| 93 | const write_src = b.addWriteFiles(); | |
| 94 | for (case.sources.toSliceConst()) |src_file| { | |
| 95 | write_src.add(src_file.filename, src_file.source); | |
| 96 | } | |
| 97 | ||
| 98 | switch (case.special) { | |
| 99 | Special.Asm => { | |
| 100 | const annotated_case_name = fmt.allocPrint(self.b.allocator, "assemble-and-link {}", .{ | |
| 101 | case.name, | |
| 102 | }) catch unreachable; | |
| 103 | if (self.test_filter) |filter| { | |
| 104 | if (mem.indexOf(u8, annotated_case_name, filter) == null) return; | |
| 105 | } | |
| 106 | ||
| 107 | const exe = b.addExecutable("test", null); | |
| 108 | exe.addAssemblyFileFromWriteFileStep(write_src, case.sources.toSliceConst()[0].filename); | |
| 109 | ||
| 110 | const run = exe.run(); | |
| 111 | run.addArgs(case.cli_args); | |
| 112 | run.expectStdErrEqual(""); | |
| 113 | run.expectStdOutEqual(case.expected_output); | |
| 114 | ||
| 115 | self.step.dependOn(&run.step); | |
| 116 | }, | |
| 117 | Special.None => { | |
| 118 | for (self.modes) |mode| { | |
| 119 | const annotated_case_name = fmt.allocPrint(self.b.allocator, "{} {} ({})", .{ | |
| 120 | "compare-output", | |
| 121 | case.name, | |
| 122 | @tagName(mode), | |
| 123 | }) catch unreachable; | |
| 124 | if (self.test_filter) |filter| { | |
| 125 | if (mem.indexOf(u8, annotated_case_name, filter) == null) continue; | |
| 126 | } | |
| 127 | ||
| 128 | const basename = case.sources.toSliceConst()[0].filename; | |
| 129 | const exe = b.addExecutableFromWriteFileStep("test", write_src, basename); | |
| 130 | exe.setBuildMode(mode); | |
| 131 | if (case.link_libc) { | |
| 132 | exe.linkSystemLibrary("c"); | |
| 133 | } | |
| 134 | ||
| 135 | const run = exe.run(); | |
| 136 | run.addArgs(case.cli_args); | |
| 137 | run.expectStdErrEqual(""); | |
| 138 | run.expectStdOutEqual(case.expected_output); | |
| 139 | ||
| 140 | self.step.dependOn(&run.step); | |
| 141 | } | |
| 142 | }, | |
| 143 | Special.RuntimeSafety => { | |
| 144 | const annotated_case_name = fmt.allocPrint(self.b.allocator, "safety {}", .{case.name}) catch unreachable; | |
| 145 | if (self.test_filter) |filter| { | |
| 146 | if (mem.indexOf(u8, annotated_case_name, filter) == null) return; | |
| 147 | } | |
| 148 | ||
| 149 | const basename = case.sources.toSliceConst()[0].filename; | |
| 150 | const exe = b.addExecutableFromWriteFileStep("test", write_src, basename); | |
| 151 | if (case.link_libc) { | |
| 152 | exe.linkSystemLibrary("c"); | |
| 153 | } | |
| 154 | ||
| 155 | const run = exe.run(); | |
| 156 | run.addArgs(case.cli_args); | |
| 157 | run.stderr_action = .ignore; | |
| 158 | run.stdout_action = .ignore; | |
| 159 | run.expected_exit_code = 126; | |
| 160 | ||
| 161 | self.step.dependOn(&run.step); | |
| 162 | }, | |
| 163 | } | |
| 164 | } | |
| 165 | }; |
test/src/run_translated_c.zig created+104| ... | ... | @@ -0,0 +1,104 @@ |
| 1 | // This is the implementation of the test harness for running translated | |
| 2 | // C code. For the actual test cases, see test/run_translated_c.zig. | |
| 3 | const std = @import("std"); | |
| 4 | const build = std.build; | |
| 5 | const ArrayList = std.ArrayList; | |
| 6 | const fmt = std.fmt; | |
| 7 | const mem = std.mem; | |
| 8 | const fs = std.fs; | |
| 9 | const warn = std.debug.warn; | |
| 10 | ||
| 11 | pub const RunTranslatedCContext = struct { | |
| 12 | b: *build.Builder, | |
| 13 | step: *build.Step, | |
| 14 | test_index: usize, | |
| 15 | test_filter: ?[]const u8, | |
| 16 | ||
| 17 | const TestCase = struct { | |
| 18 | name: []const u8, | |
| 19 | sources: ArrayList(SourceFile), | |
| 20 | expected_stdout: []const u8, | |
| 21 | allow_warnings: bool, | |
| 22 | ||
| 23 | const SourceFile = struct { | |
| 24 | filename: []const u8, | |
| 25 | source: []const u8, | |
| 26 | }; | |
| 27 | ||
| 28 | pub fn addSourceFile(self: *TestCase, filename: []const u8, source: []const u8) void { | |
| 29 | self.sources.append(SourceFile{ | |
| 30 | .filename = filename, | |
| 31 | .source = source, | |
| 32 | }) catch unreachable; | |
| 33 | } | |
| 34 | }; | |
| 35 | ||
| 36 | pub fn create( | |
| 37 | self: *RunTranslatedCContext, | |
| 38 | allow_warnings: bool, | |
| 39 | filename: []const u8, | |
| 40 | name: []const u8, | |
| 41 | source: []const u8, | |
| 42 | expected_stdout: []const u8, | |
| 43 | ) *TestCase { | |
| 44 | const tc = self.b.allocator.create(TestCase) catch unreachable; | |
| 45 | tc.* = TestCase{ | |
| 46 | .name = name, | |
| 47 | .sources = ArrayList(TestCase.SourceFile).init(self.b.allocator), | |
| 48 | .expected_stdout = expected_stdout, | |
| 49 | .allow_warnings = allow_warnings, | |
| 50 | }; | |
| 51 | ||
| 52 | tc.addSourceFile(filename, source); | |
| 53 | return tc; | |
| 54 | } | |
| 55 | ||
| 56 | pub fn add( | |
| 57 | self: *RunTranslatedCContext, | |
| 58 | name: []const u8, | |
| 59 | source: []const u8, | |
| 60 | expected_stdout: []const u8, | |
| 61 | ) void { | |
| 62 | const tc = self.create(false, "source.c", name, source, expected_stdout); | |
| 63 | self.addCase(tc); | |
| 64 | } | |
| 65 | ||
| 66 | pub fn addAllowWarnings( | |
| 67 | self: *RunTranslatedCContext, | |
| 68 | name: []const u8, | |
| 69 | source: []const u8, | |
| 70 | expected_stdout: []const u8, | |
| 71 | ) void { | |
| 72 | const tc = self.create(true, "source.c", name, source, expected_stdout); | |
| 73 | self.addCase(tc); | |
| 74 | } | |
| 75 | ||
| 76 | pub fn addCase(self: *RunTranslatedCContext, case: *const TestCase) void { | |
| 77 | const b = self.b; | |
| 78 | ||
| 79 | const annotated_case_name = fmt.allocPrint(self.b.allocator, "run-translated-c {}", .{case.name}) catch unreachable; | |
| 80 | if (self.test_filter) |filter| { | |
| 81 | if (mem.indexOf(u8, annotated_case_name, filter) == null) return; | |
| 82 | } | |
| 83 | ||
| 84 | const write_src = b.addWriteFiles(); | |
| 85 | for (case.sources.toSliceConst()) |src_file| { | |
| 86 | write_src.add(src_file.filename, src_file.source); | |
| 87 | } | |
| 88 | const translate_c = b.addTranslateC(.{ | |
| 89 | .write_file = .{ | |
| 90 | .step = write_src, | |
| 91 | .basename = case.sources.toSliceConst()[0].filename, | |
| 92 | }, | |
| 93 | }); | |
| 94 | const exe = translate_c.addExecutable(); | |
| 95 | exe.linkLibC(); | |
| 96 | const run = exe.run(); | |
| 97 | if (!case.allow_warnings) { | |
| 98 | run.expectStdErrEqual(""); | |
| 99 | } | |
| 100 | run.expectStdOutEqual(case.expected_stdout); | |
| 101 | ||
| 102 | self.step.dependOn(&run.step); | |
| 103 | } | |
| 104 | }; |
test/src/translate_c.zig created+109| ... | ... | @@ -0,0 +1,109 @@ |
| 1 | // This is the implementation of the test harness. | |
| 2 | // For the actual test cases, see test/translate_c.zig. | |
| 3 | const std = @import("std"); | |
| 4 | const build = std.build; | |
| 5 | const ArrayList = std.ArrayList; | |
| 6 | const fmt = std.fmt; | |
| 7 | const mem = std.mem; | |
| 8 | const fs = std.fs; | |
| 9 | const warn = std.debug.warn; | |
| 10 | ||
| 11 | pub const TranslateCContext = struct { | |
| 12 | b: *build.Builder, | |
| 13 | step: *build.Step, | |
| 14 | test_index: usize, | |
| 15 | test_filter: ?[]const u8, | |
| 16 | ||
| 17 | const TestCase = struct { | |
| 18 | name: []const u8, | |
| 19 | sources: ArrayList(SourceFile), | |
| 20 | expected_lines: ArrayList([]const u8), | |
| 21 | allow_warnings: bool, | |
| 22 | ||
| 23 | const SourceFile = struct { | |
| 24 | filename: []const u8, | |
| 25 | source: []const u8, | |
| 26 | }; | |
| 27 | ||
| 28 | pub fn addSourceFile(self: *TestCase, filename: []const u8, source: []const u8) void { | |
| 29 | self.sources.append(SourceFile{ | |
| 30 | .filename = filename, | |
| 31 | .source = source, | |
| 32 | }) catch unreachable; | |
| 33 | } | |
| 34 | ||
| 35 | pub fn addExpectedLine(self: *TestCase, text: []const u8) void { | |
| 36 | self.expected_lines.append(text) catch unreachable; | |
| 37 | } | |
| 38 | }; | |
| 39 | ||
| 40 | pub fn create( | |
| 41 | self: *TranslateCContext, | |
| 42 | allow_warnings: bool, | |
| 43 | filename: []const u8, | |
| 44 | name: []const u8, | |
| 45 | source: []const u8, | |
| 46 | expected_lines: []const []const u8, | |
| 47 | ) *TestCase { | |
| 48 | const tc = self.b.allocator.create(TestCase) catch unreachable; | |
| 49 | tc.* = TestCase{ | |
| 50 | .name = name, | |
| 51 | .sources = ArrayList(TestCase.SourceFile).init(self.b.allocator), | |
| 52 | .expected_lines = ArrayList([]const u8).init(self.b.allocator), | |
| 53 | .allow_warnings = allow_warnings, | |
| 54 | }; | |
| 55 | ||
| 56 | tc.addSourceFile(filename, source); | |
| 57 | var arg_i: usize = 0; | |
| 58 | while (arg_i < expected_lines.len) : (arg_i += 1) { | |
| 59 | tc.addExpectedLine(expected_lines[arg_i]); | |
| 60 | } | |
| 61 | return tc; | |
| 62 | } | |
| 63 | ||
| 64 | pub fn add( | |
| 65 | self: *TranslateCContext, | |
| 66 | name: []const u8, | |
| 67 | source: []const u8, | |
| 68 | expected_lines: []const []const u8, | |
| 69 | ) void { | |
| 70 | const tc = self.create(false, "source.h", name, source, expected_lines); | |
| 71 | self.addCase(tc); | |
| 72 | } | |
| 73 | ||
| 74 | pub fn addAllowWarnings( | |
| 75 | self: *TranslateCContext, | |
| 76 | name: []const u8, | |
| 77 | source: []const u8, | |
| 78 | expected_lines: []const []const u8, | |
| 79 | ) void { | |
| 80 | const tc = self.create(true, "source.h", name, source, expected_lines); | |
| 81 | self.addCase(tc); | |
| 82 | } | |
| 83 | ||
| 84 | pub fn addCase(self: *TranslateCContext, case: *const TestCase) void { | |
| 85 | const b = self.b; | |
| 86 | ||
| 87 | const translate_c_cmd = "translate-c"; | |
| 88 | const annotated_case_name = fmt.allocPrint(self.b.allocator, "{} {}", .{ translate_c_cmd, case.name }) catch unreachable; | |
| 89 | if (self.test_filter) |filter| { | |
| 90 | if (mem.indexOf(u8, annotated_case_name, filter) == null) return; | |
| 91 | } | |
| 92 | ||
| 93 | const write_src = b.addWriteFiles(); | |
| 94 | for (case.sources.toSliceConst()) |src_file| { | |
| 95 | write_src.add(src_file.filename, src_file.source); | |
| 96 | } | |
| 97 | ||
| 98 | const translate_c = b.addTranslateC(.{ | |
| 99 | .write_file = .{ | |
| 100 | .step = write_src, | |
| 101 | .basename = case.sources.toSliceConst()[0].filename, | |
| 102 | }, | |
| 103 | }); | |
| 104 | ||
| 105 | const check_file = translate_c.addCheckFile(case.expected_lines.toSliceConst()); | |
| 106 | ||
| 107 | self.step.dependOn(&check_file.step); | |
| 108 | } | |
| 109 | }; |
test/tests.zig+46-613| ... | ... | @@ -14,6 +14,7 @@ const builtin = @import("builtin"); |
| 14 | 14 | const Mode = builtin.Mode; |
| 15 | 15 | const LibExeObjStep = build.LibExeObjStep; |
| 16 | 16 | |
| 17 | // Cases | |
| 17 | 18 | const compare_output = @import("compare_output.zig"); |
| 18 | 19 | const standalone = @import("standalone.zig"); |
| 19 | 20 | const stack_traces = @import("stack_traces.zig"); |
| ... | ... | @@ -21,8 +22,14 @@ const compile_errors = @import("compile_errors.zig"); |
| 21 | 22 | const assemble_and_link = @import("assemble_and_link.zig"); |
| 22 | 23 | const runtime_safety = @import("runtime_safety.zig"); |
| 23 | 24 | const translate_c = @import("translate_c.zig"); |
| 25 | const run_translated_c = @import("run_translated_c.zig"); | |
| 24 | 26 | const gen_h = @import("gen_h.zig"); |
| 25 | 27 | |
| 28 | // Implementations | |
| 29 | pub const TranslateCContext = @import("src/translate_c.zig").TranslateCContext; | |
| 30 | pub const RunTranslatedCContext = @import("src/run_translated_c.zig").RunTranslatedCContext; | |
| 31 | pub const CompareOutputContext = @import("src/compare_output.zig").CompareOutputContext; | |
| 32 | ||
| 26 | 33 | const TestTarget = struct { |
| 27 | 34 | target: Target = .Native, |
| 28 | 35 | mode: builtin.Mode = .Debug, |
| ... | ... | @@ -383,6 +390,20 @@ pub fn addTranslateCTests(b: *build.Builder, test_filter: ?[]const u8) *build.St |
| 383 | 390 | return cases.step; |
| 384 | 391 | } |
| 385 | 392 | |
| 393 | pub fn addRunTranslatedCTests(b: *build.Builder, test_filter: ?[]const u8) *build.Step { | |
| 394 | const cases = b.allocator.create(RunTranslatedCContext) catch unreachable; | |
| 395 | cases.* = .{ | |
| 396 | .b = b, | |
| 397 | .step = b.step("test-run-translated-c", "Run the Run-Translated-C tests"), | |
| 398 | .test_index = 0, | |
| 399 | .test_filter = test_filter, | |
| 400 | }; | |
| 401 | ||
| 402 | run_translated_c.addCases(cases); | |
| 403 | ||
| 404 | return cases.step; | |
| 405 | } | |
| 406 | ||
| 386 | 407 | pub fn addGenHTests(b: *build.Builder, test_filter: ?[]const u8) *build.Step { |
| 387 | 408 | const cases = b.allocator.create(GenHContext) catch unreachable; |
| 388 | 409 | cases.* = GenHContext{ |
| ... | ... | @@ -479,356 +500,6 @@ pub fn addPkgTests( |
| 479 | 500 | return step; |
| 480 | 501 | } |
| 481 | 502 | |
| 482 | pub const CompareOutputContext = struct { | |
| 483 | b: *build.Builder, | |
| 484 | step: *build.Step, | |
| 485 | test_index: usize, | |
| 486 | test_filter: ?[]const u8, | |
| 487 | modes: []const Mode, | |
| 488 | ||
| 489 | const Special = enum { | |
| 490 | None, | |
| 491 | Asm, | |
| 492 | RuntimeSafety, | |
| 493 | }; | |
| 494 | ||
| 495 | const TestCase = struct { | |
| 496 | name: []const u8, | |
| 497 | sources: ArrayList(SourceFile), | |
| 498 | expected_output: []const u8, | |
| 499 | link_libc: bool, | |
| 500 | special: Special, | |
| 501 | cli_args: []const []const u8, | |
| 502 | ||
| 503 | const SourceFile = struct { | |
| 504 | filename: []const u8, | |
| 505 | source: []const u8, | |
| 506 | }; | |
| 507 | ||
| 508 | pub fn addSourceFile(self: *TestCase, filename: []const u8, source: []const u8) void { | |
| 509 | self.sources.append(SourceFile{ | |
| 510 | .filename = filename, | |
| 511 | .source = source, | |
| 512 | }) catch unreachable; | |
| 513 | } | |
| 514 | ||
| 515 | pub fn setCommandLineArgs(self: *TestCase, args: []const []const u8) void { | |
| 516 | self.cli_args = args; | |
| 517 | } | |
| 518 | }; | |
| 519 | ||
| 520 | const RunCompareOutputStep = struct { | |
| 521 | step: build.Step, | |
| 522 | context: *CompareOutputContext, | |
| 523 | exe: *LibExeObjStep, | |
| 524 | name: []const u8, | |
| 525 | expected_output: []const u8, | |
| 526 | test_index: usize, | |
| 527 | cli_args: []const []const u8, | |
| 528 | ||
| 529 | pub fn create( | |
| 530 | context: *CompareOutputContext, | |
| 531 | exe: *LibExeObjStep, | |
| 532 | name: []const u8, | |
| 533 | expected_output: []const u8, | |
| 534 | cli_args: []const []const u8, | |
| 535 | ) *RunCompareOutputStep { | |
| 536 | const allocator = context.b.allocator; | |
| 537 | const ptr = allocator.create(RunCompareOutputStep) catch unreachable; | |
| 538 | ptr.* = RunCompareOutputStep{ | |
| 539 | .context = context, | |
| 540 | .exe = exe, | |
| 541 | .name = name, | |
| 542 | .expected_output = expected_output, | |
| 543 | .test_index = context.test_index, | |
| 544 | .step = build.Step.init("RunCompareOutput", allocator, make), | |
| 545 | .cli_args = cli_args, | |
| 546 | }; | |
| 547 | ptr.step.dependOn(&exe.step); | |
| 548 | context.test_index += 1; | |
| 549 | return ptr; | |
| 550 | } | |
| 551 | ||
| 552 | fn make(step: *build.Step) !void { | |
| 553 | const self = @fieldParentPtr(RunCompareOutputStep, "step", step); | |
| 554 | const b = self.context.b; | |
| 555 | ||
| 556 | const full_exe_path = self.exe.getOutputPath(); | |
| 557 | var args = ArrayList([]const u8).init(b.allocator); | |
| 558 | defer args.deinit(); | |
| 559 | ||
| 560 | args.append(full_exe_path) catch unreachable; | |
| 561 | for (self.cli_args) |arg| { | |
| 562 | args.append(arg) catch unreachable; | |
| 563 | } | |
| 564 | ||
| 565 | warn("Test {}/{} {}...", .{ self.test_index + 1, self.context.test_index, self.name }); | |
| 566 | ||
| 567 | const child = std.ChildProcess.init(args.toSliceConst(), b.allocator) catch unreachable; | |
| 568 | defer child.deinit(); | |
| 569 | ||
| 570 | child.stdin_behavior = .Ignore; | |
| 571 | child.stdout_behavior = .Pipe; | |
| 572 | child.stderr_behavior = .Pipe; | |
| 573 | child.env_map = b.env_map; | |
| 574 | ||
| 575 | child.spawn() catch |err| debug.panic("Unable to spawn {}: {}\n", .{ full_exe_path, @errorName(err) }); | |
| 576 | ||
| 577 | var stdout = Buffer.initNull(b.allocator); | |
| 578 | var stderr = Buffer.initNull(b.allocator); | |
| 579 | ||
| 580 | var stdout_file_in_stream = child.stdout.?.inStream(); | |
| 581 | var stderr_file_in_stream = child.stderr.?.inStream(); | |
| 582 | ||
| 583 | stdout_file_in_stream.stream.readAllBuffer(&stdout, max_stdout_size) catch unreachable; | |
| 584 | stderr_file_in_stream.stream.readAllBuffer(&stderr, max_stdout_size) catch unreachable; | |
| 585 | ||
| 586 | const term = child.wait() catch |err| { | |
| 587 | debug.panic("Unable to spawn {}: {}\n", .{ full_exe_path, @errorName(err) }); | |
| 588 | }; | |
| 589 | switch (term) { | |
| 590 | .Exited => |code| { | |
| 591 | if (code != 0) { | |
| 592 | warn("Process {} exited with error code {}\n", .{ full_exe_path, code }); | |
| 593 | printInvocation(args.toSliceConst()); | |
| 594 | return error.TestFailed; | |
| 595 | } | |
| 596 | }, | |
| 597 | else => { | |
| 598 | warn("Process {} terminated unexpectedly\n", .{full_exe_path}); | |
| 599 | printInvocation(args.toSliceConst()); | |
| 600 | return error.TestFailed; | |
| 601 | }, | |
| 602 | } | |
| 603 | ||
| 604 | if (!mem.eql(u8, self.expected_output, stdout.toSliceConst())) { | |
| 605 | warn( | |
| 606 | \\ | |
| 607 | \\========= Expected this output: ========= | |
| 608 | \\{} | |
| 609 | \\========= But found: ==================== | |
| 610 | \\{} | |
| 611 | \\ | |
| 612 | , .{ self.expected_output, stdout.toSliceConst() }); | |
| 613 | return error.TestFailed; | |
| 614 | } | |
| 615 | warn("OK\n", .{}); | |
| 616 | } | |
| 617 | }; | |
| 618 | ||
| 619 | const RuntimeSafetyRunStep = struct { | |
| 620 | step: build.Step, | |
| 621 | context: *CompareOutputContext, | |
| 622 | exe: *LibExeObjStep, | |
| 623 | name: []const u8, | |
| 624 | test_index: usize, | |
| 625 | ||
| 626 | pub fn create(context: *CompareOutputContext, exe: *LibExeObjStep, name: []const u8) *RuntimeSafetyRunStep { | |
| 627 | const allocator = context.b.allocator; | |
| 628 | const ptr = allocator.create(RuntimeSafetyRunStep) catch unreachable; | |
| 629 | ptr.* = RuntimeSafetyRunStep{ | |
| 630 | .context = context, | |
| 631 | .exe = exe, | |
| 632 | .name = name, | |
| 633 | .test_index = context.test_index, | |
| 634 | .step = build.Step.init("RuntimeSafetyRun", allocator, make), | |
| 635 | }; | |
| 636 | ptr.step.dependOn(&exe.step); | |
| 637 | context.test_index += 1; | |
| 638 | return ptr; | |
| 639 | } | |
| 640 | ||
| 641 | fn make(step: *build.Step) !void { | |
| 642 | const self = @fieldParentPtr(RuntimeSafetyRunStep, "step", step); | |
| 643 | const b = self.context.b; | |
| 644 | ||
| 645 | const full_exe_path = self.exe.getOutputPath(); | |
| 646 | ||
| 647 | warn("Test {}/{} {}...", .{ self.test_index + 1, self.context.test_index, self.name }); | |
| 648 | ||
| 649 | const child = std.ChildProcess.init(&[_][]const u8{full_exe_path}, b.allocator) catch unreachable; | |
| 650 | defer child.deinit(); | |
| 651 | ||
| 652 | child.env_map = b.env_map; | |
| 653 | child.stdin_behavior = .Ignore; | |
| 654 | child.stdout_behavior = .Ignore; | |
| 655 | child.stderr_behavior = .Ignore; | |
| 656 | ||
| 657 | const term = child.spawnAndWait() catch |err| { | |
| 658 | debug.panic("Unable to spawn {}: {}\n", .{ full_exe_path, @errorName(err) }); | |
| 659 | }; | |
| 660 | ||
| 661 | const expected_exit_code: u32 = 126; | |
| 662 | switch (term) { | |
| 663 | .Exited => |code| { | |
| 664 | if (code != expected_exit_code) { | |
| 665 | warn("\nProgram expected to exit with code {} but exited with code {}\n", .{ | |
| 666 | expected_exit_code, code, | |
| 667 | }); | |
| 668 | return error.TestFailed; | |
| 669 | } | |
| 670 | }, | |
| 671 | .Signal => |sig| { | |
| 672 | warn("\nProgram expected to exit with code {} but instead signaled {}\n", .{ | |
| 673 | expected_exit_code, sig, | |
| 674 | }); | |
| 675 | return error.TestFailed; | |
| 676 | }, | |
| 677 | else => { | |
| 678 | warn("\nProgram expected to exit with code {} but exited in an unexpected way\n", .{ | |
| 679 | expected_exit_code, | |
| 680 | }); | |
| 681 | return error.TestFailed; | |
| 682 | }, | |
| 683 | } | |
| 684 | ||
| 685 | warn("OK\n", .{}); | |
| 686 | } | |
| 687 | }; | |
| 688 | ||
| 689 | pub fn createExtra(self: *CompareOutputContext, name: []const u8, source: []const u8, expected_output: []const u8, special: Special) TestCase { | |
| 690 | var tc = TestCase{ | |
| 691 | .name = name, | |
| 692 | .sources = ArrayList(TestCase.SourceFile).init(self.b.allocator), | |
| 693 | .expected_output = expected_output, | |
| 694 | .link_libc = false, | |
| 695 | .special = special, | |
| 696 | .cli_args = &[_][]const u8{}, | |
| 697 | }; | |
| 698 | const root_src_name = if (special == Special.Asm) "source.s" else "source.zig"; | |
| 699 | tc.addSourceFile(root_src_name, source); | |
| 700 | return tc; | |
| 701 | } | |
| 702 | ||
| 703 | pub fn create(self: *CompareOutputContext, name: []const u8, source: []const u8, expected_output: []const u8) TestCase { | |
| 704 | return createExtra(self, name, source, expected_output, Special.None); | |
| 705 | } | |
| 706 | ||
| 707 | pub fn addC(self: *CompareOutputContext, name: []const u8, source: []const u8, expected_output: []const u8) void { | |
| 708 | var tc = self.create(name, source, expected_output); | |
| 709 | tc.link_libc = true; | |
| 710 | self.addCase(tc); | |
| 711 | } | |
| 712 | ||
| 713 | pub fn add(self: *CompareOutputContext, name: []const u8, source: []const u8, expected_output: []const u8) void { | |
| 714 | const tc = self.create(name, source, expected_output); | |
| 715 | self.addCase(tc); | |
| 716 | } | |
| 717 | ||
| 718 | pub fn addAsm(self: *CompareOutputContext, name: []const u8, source: []const u8, expected_output: []const u8) void { | |
| 719 | const tc = self.createExtra(name, source, expected_output, Special.Asm); | |
| 720 | self.addCase(tc); | |
| 721 | } | |
| 722 | ||
| 723 | pub fn addRuntimeSafety(self: *CompareOutputContext, name: []const u8, source: []const u8) void { | |
| 724 | const tc = self.createExtra(name, source, undefined, Special.RuntimeSafety); | |
| 725 | self.addCase(tc); | |
| 726 | } | |
| 727 | ||
| 728 | pub fn addCase(self: *CompareOutputContext, case: TestCase) void { | |
| 729 | const b = self.b; | |
| 730 | ||
| 731 | const root_src = fs.path.join( | |
| 732 | b.allocator, | |
| 733 | &[_][]const u8{ b.cache_root, case.sources.items[0].filename }, | |
| 734 | ) catch unreachable; | |
| 735 | ||
| 736 | switch (case.special) { | |
| 737 | Special.Asm => { | |
| 738 | const annotated_case_name = fmt.allocPrint(self.b.allocator, "assemble-and-link {}", .{ | |
| 739 | case.name, | |
| 740 | }) catch unreachable; | |
| 741 | if (self.test_filter) |filter| { | |
| 742 | if (mem.indexOf(u8, annotated_case_name, filter) == null) return; | |
| 743 | } | |
| 744 | ||
| 745 | const exe = b.addExecutable("test", null); | |
| 746 | exe.addAssemblyFile(root_src); | |
| 747 | ||
| 748 | for (case.sources.toSliceConst()) |src_file| { | |
| 749 | const expanded_src_path = fs.path.join( | |
| 750 | b.allocator, | |
| 751 | &[_][]const u8{ b.cache_root, src_file.filename }, | |
| 752 | ) catch unreachable; | |
| 753 | const write_src = b.addWriteFile(expanded_src_path, src_file.source); | |
| 754 | exe.step.dependOn(&write_src.step); | |
| 755 | } | |
| 756 | ||
| 757 | const run_and_cmp_output = RunCompareOutputStep.create( | |
| 758 | self, | |
| 759 | exe, | |
| 760 | annotated_case_name, | |
| 761 | case.expected_output, | |
| 762 | case.cli_args, | |
| 763 | ); | |
| 764 | ||
| 765 | self.step.dependOn(&run_and_cmp_output.step); | |
| 766 | }, | |
| 767 | Special.None => { | |
| 768 | for (self.modes) |mode| { | |
| 769 | const annotated_case_name = fmt.allocPrint(self.b.allocator, "{} {} ({})", .{ | |
| 770 | "compare-output", | |
| 771 | case.name, | |
| 772 | @tagName(mode), | |
| 773 | }) catch unreachable; | |
| 774 | if (self.test_filter) |filter| { | |
| 775 | if (mem.indexOf(u8, annotated_case_name, filter) == null) continue; | |
| 776 | } | |
| 777 | ||
| 778 | const exe = b.addExecutable("test", root_src); | |
| 779 | exe.setBuildMode(mode); | |
| 780 | if (case.link_libc) { | |
| 781 | exe.linkSystemLibrary("c"); | |
| 782 | } | |
| 783 | ||
| 784 | for (case.sources.toSliceConst()) |src_file| { | |
| 785 | const expanded_src_path = fs.path.join( | |
| 786 | b.allocator, | |
| 787 | &[_][]const u8{ b.cache_root, src_file.filename }, | |
| 788 | ) catch unreachable; | |
| 789 | const write_src = b.addWriteFile(expanded_src_path, src_file.source); | |
| 790 | exe.step.dependOn(&write_src.step); | |
| 791 | } | |
| 792 | ||
| 793 | const run_and_cmp_output = RunCompareOutputStep.create( | |
| 794 | self, | |
| 795 | exe, | |
| 796 | annotated_case_name, | |
| 797 | case.expected_output, | |
| 798 | case.cli_args, | |
| 799 | ); | |
| 800 | ||
| 801 | self.step.dependOn(&run_and_cmp_output.step); | |
| 802 | } | |
| 803 | }, | |
| 804 | Special.RuntimeSafety => { | |
| 805 | const annotated_case_name = fmt.allocPrint(self.b.allocator, "safety {}", .{case.name}) catch unreachable; | |
| 806 | if (self.test_filter) |filter| { | |
| 807 | if (mem.indexOf(u8, annotated_case_name, filter) == null) return; | |
| 808 | } | |
| 809 | ||
| 810 | const exe = b.addExecutable("test", root_src); | |
| 811 | if (case.link_libc) { | |
| 812 | exe.linkSystemLibrary("c"); | |
| 813 | } | |
| 814 | ||
| 815 | for (case.sources.toSliceConst()) |src_file| { | |
| 816 | const expanded_src_path = fs.path.join( | |
| 817 | b.allocator, | |
| 818 | &[_][]const u8{ b.cache_root, src_file.filename }, | |
| 819 | ) catch unreachable; | |
| 820 | const write_src = b.addWriteFile(expanded_src_path, src_file.source); | |
| 821 | exe.step.dependOn(&write_src.step); | |
| 822 | } | |
| 823 | ||
| 824 | const run_and_cmp_output = RuntimeSafetyRunStep.create(self, exe, annotated_case_name); | |
| 825 | ||
| 826 | self.step.dependOn(&run_and_cmp_output.step); | |
| 827 | }, | |
| 828 | } | |
| 829 | } | |
| 830 | }; | |
| 831 | ||
| 832 | 503 | pub const StackTracesContext = struct { |
| 833 | 504 | b: *build.Builder, |
| 834 | 505 | step: *build.Step, |
| ... | ... | @@ -846,11 +517,6 @@ pub const StackTracesContext = struct { |
| 846 | 517 | ) void { |
| 847 | 518 | const b = self.b; |
| 848 | 519 | |
| 849 | const source_pathname = fs.path.join( | |
| 850 | b.allocator, | |
| 851 | &[_][]const u8{ b.cache_root, "source.zig" }, | |
| 852 | ) catch unreachable; | |
| 853 | ||
| 854 | 520 | for (self.modes) |mode| { |
| 855 | 521 | const expect_for_mode = expect[@enumToInt(mode)]; |
| 856 | 522 | if (expect_for_mode.len == 0) continue; |
| ... | ... | @@ -864,12 +530,11 @@ pub const StackTracesContext = struct { |
| 864 | 530 | if (mem.indexOf(u8, annotated_case_name, filter) == null) continue; |
| 865 | 531 | } |
| 866 | 532 | |
| 867 | const exe = b.addExecutable("test", source_pathname); | |
| 533 | const src_basename = "source.zig"; | |
| 534 | const write_src = b.addWriteFile(src_basename, source); | |
| 535 | const exe = b.addExecutableFromWriteFileStep("test", write_src, src_basename); | |
| 868 | 536 | exe.setBuildMode(mode); |
| 869 | 537 | |
| 870 | const write_source = b.addWriteFile(source_pathname, source); | |
| 871 | exe.step.dependOn(&write_source.step); | |
| 872 | ||
| 873 | 538 | const run_and_compare = RunAndCompareStep.create( |
| 874 | 539 | self, |
| 875 | 540 | exe, |
| ... | ... | @@ -1072,6 +737,7 @@ pub const CompileErrorContext = struct { |
| 1072 | 737 | test_index: usize, |
| 1073 | 738 | case: *const TestCase, |
| 1074 | 739 | build_mode: Mode, |
| 740 | write_src: *build.WriteFileStep, | |
| 1075 | 741 | |
| 1076 | 742 | const ErrLineIter = struct { |
| 1077 | 743 | lines: mem.SplitIterator, |
| ... | ... | @@ -1091,7 +757,13 @@ pub const CompileErrorContext = struct { |
| 1091 | 757 | } |
| 1092 | 758 | }; |
| 1093 | 759 | |
| 1094 | pub fn create(context: *CompileErrorContext, name: []const u8, case: *const TestCase, build_mode: Mode) *CompileCmpOutputStep { | |
| 760 | pub fn create( | |
| 761 | context: *CompileErrorContext, | |
| 762 | name: []const u8, | |
| 763 | case: *const TestCase, | |
| 764 | build_mode: Mode, | |
| 765 | write_src: *build.WriteFileStep, | |
| 766 | ) *CompileCmpOutputStep { | |
| 1095 | 767 | const allocator = context.b.allocator; |
| 1096 | 768 | const ptr = allocator.create(CompileCmpOutputStep) catch unreachable; |
| 1097 | 769 | ptr.* = CompileCmpOutputStep{ |
| ... | ... | @@ -1101,6 +773,7 @@ pub const CompileErrorContext = struct { |
| 1101 | 773 | .test_index = context.test_index, |
| 1102 | 774 | .case = case, |
| 1103 | 775 | .build_mode = build_mode, |
| 776 | .write_src = write_src, | |
| 1104 | 777 | }; |
| 1105 | 778 | |
| 1106 | 779 | context.test_index += 1; |
| ... | ... | @@ -1111,11 +784,6 @@ pub const CompileErrorContext = struct { |
| 1111 | 784 | const self = @fieldParentPtr(CompileCmpOutputStep, "step", step); |
| 1112 | 785 | const b = self.context.b; |
| 1113 | 786 | |
| 1114 | const root_src = fs.path.join( | |
| 1115 | b.allocator, | |
| 1116 | &[_][]const u8{ b.cache_root, self.case.sources.items[0].filename }, | |
| 1117 | ) catch unreachable; | |
| 1118 | ||
| 1119 | 787 | var zig_args = ArrayList([]const u8).init(b.allocator); |
| 1120 | 788 | zig_args.append(b.zig_exe) catch unreachable; |
| 1121 | 789 | |
| ... | ... | @@ -1126,7 +794,8 @@ pub const CompileErrorContext = struct { |
| 1126 | 794 | } else { |
| 1127 | 795 | try zig_args.append("build-obj"); |
| 1128 | 796 | } |
| 1129 | zig_args.append(b.pathFromRoot(root_src)) catch unreachable; | |
| 797 | const root_src_basename = self.case.sources.toSliceConst()[0].filename; | |
| 798 | try zig_args.append(self.write_src.getOutputPath(root_src_basename)); | |
| 1130 | 799 | |
| 1131 | 800 | zig_args.append("--name") catch unreachable; |
| 1132 | 801 | zig_args.append("test") catch unreachable; |
| ... | ... | @@ -1325,18 +994,14 @@ pub const CompileErrorContext = struct { |
| 1325 | 994 | if (self.test_filter) |filter| { |
| 1326 | 995 | if (mem.indexOf(u8, annotated_case_name, filter) == null) return; |
| 1327 | 996 | } |
| 1328 | ||
| 1329 | const compile_and_cmp_errors = CompileCmpOutputStep.create(self, annotated_case_name, case, .Debug); | |
| 1330 | self.step.dependOn(&compile_and_cmp_errors.step); | |
| 1331 | ||
| 997 | const write_src = b.addWriteFiles(); | |
| 1332 | 998 | for (case.sources.toSliceConst()) |src_file| { |
| 1333 | const expanded_src_path = fs.path.join( | |
| 1334 | b.allocator, | |
| 1335 | &[_][]const u8{ b.cache_root, src_file.filename }, | |
| 1336 | ) catch unreachable; | |
| 1337 | const write_src = b.addWriteFile(expanded_src_path, src_file.source); | |
| 1338 | compile_and_cmp_errors.step.dependOn(&write_src.step); | |
| 999 | write_src.add(src_file.filename, src_file.source); | |
| 1339 | 1000 | } |
| 1001 | ||
| 1002 | const compile_and_cmp_errors = CompileCmpOutputStep.create(self, annotated_case_name, case, .Debug, write_src); | |
| 1003 | compile_and_cmp_errors.step.dependOn(&write_src.step); | |
| 1004 | self.step.dependOn(&compile_and_cmp_errors.step); | |
| 1340 | 1005 | } |
| 1341 | 1006 | }; |
| 1342 | 1007 | |
| ... | ... | @@ -1411,230 +1076,6 @@ pub const StandaloneContext = struct { |
| 1411 | 1076 | } |
| 1412 | 1077 | }; |
| 1413 | 1078 | |
| 1414 | pub const TranslateCContext = struct { | |
| 1415 | b: *build.Builder, | |
| 1416 | step: *build.Step, | |
| 1417 | test_index: usize, | |
| 1418 | test_filter: ?[]const u8, | |
| 1419 | ||
| 1420 | const TestCase = struct { | |
| 1421 | name: []const u8, | |
| 1422 | sources: ArrayList(SourceFile), | |
| 1423 | expected_lines: ArrayList([]const u8), | |
| 1424 | allow_warnings: bool, | |
| 1425 | ||
| 1426 | const SourceFile = struct { | |
| 1427 | filename: []const u8, | |
| 1428 | source: []const u8, | |
| 1429 | }; | |
| 1430 | ||
| 1431 | pub fn addSourceFile(self: *TestCase, filename: []const u8, source: []const u8) void { | |
| 1432 | self.sources.append(SourceFile{ | |
| 1433 | .filename = filename, | |
| 1434 | .source = source, | |
| 1435 | }) catch unreachable; | |
| 1436 | } | |
| 1437 | ||
| 1438 | pub fn addExpectedLine(self: *TestCase, text: []const u8) void { | |
| 1439 | self.expected_lines.append(text) catch unreachable; | |
| 1440 | } | |
| 1441 | }; | |
| 1442 | ||
| 1443 | const TranslateCCmpOutputStep = struct { | |
| 1444 | step: build.Step, | |
| 1445 | context: *TranslateCContext, | |
| 1446 | name: []const u8, | |
| 1447 | test_index: usize, | |
| 1448 | case: *const TestCase, | |
| 1449 | ||
| 1450 | pub fn create(context: *TranslateCContext, name: []const u8, case: *const TestCase) *TranslateCCmpOutputStep { | |
| 1451 | const allocator = context.b.allocator; | |
| 1452 | const ptr = allocator.create(TranslateCCmpOutputStep) catch unreachable; | |
| 1453 | ptr.* = TranslateCCmpOutputStep{ | |
| 1454 | .step = build.Step.init("ParseCCmpOutput", allocator, make), | |
| 1455 | .context = context, | |
| 1456 | .name = name, | |
| 1457 | .test_index = context.test_index, | |
| 1458 | .case = case, | |
| 1459 | }; | |
| 1460 | ||
| 1461 | context.test_index += 1; | |
| 1462 | return ptr; | |
| 1463 | } | |
| 1464 | ||
| 1465 | fn make(step: *build.Step) !void { | |
| 1466 | const self = @fieldParentPtr(TranslateCCmpOutputStep, "step", step); | |
| 1467 | const b = self.context.b; | |
| 1468 | ||
| 1469 | const root_src = fs.path.join( | |
| 1470 | b.allocator, | |
| 1471 | &[_][]const u8{ b.cache_root, self.case.sources.items[0].filename }, | |
| 1472 | ) catch unreachable; | |
| 1473 | ||
| 1474 | var zig_args = ArrayList([]const u8).init(b.allocator); | |
| 1475 | zig_args.append(b.zig_exe) catch unreachable; | |
| 1476 | ||
| 1477 | const translate_c_cmd = "translate-c"; | |
| 1478 | zig_args.append(translate_c_cmd) catch unreachable; | |
| 1479 | zig_args.append(b.pathFromRoot(root_src)) catch unreachable; | |
| 1480 | ||
| 1481 | warn("Test {}/{} {}...", .{ self.test_index + 1, self.context.test_index, self.name }); | |
| 1482 | ||
| 1483 | if (b.verbose) { | |
| 1484 | printInvocation(zig_args.toSliceConst()); | |
| 1485 | } | |
| 1486 | ||
| 1487 | const child = std.ChildProcess.init(zig_args.toSliceConst(), b.allocator) catch unreachable; | |
| 1488 | defer child.deinit(); | |
| 1489 | ||
| 1490 | child.env_map = b.env_map; | |
| 1491 | child.stdin_behavior = .Ignore; | |
| 1492 | child.stdout_behavior = .Pipe; | |
| 1493 | child.stderr_behavior = .Pipe; | |
| 1494 | ||
| 1495 | child.spawn() catch |err| debug.panic("Unable to spawn {}: {}\n", .{ | |
| 1496 | zig_args.toSliceConst()[0], | |
| 1497 | @errorName(err), | |
| 1498 | }); | |
| 1499 | ||
| 1500 | var stdout_buf = Buffer.initNull(b.allocator); | |
| 1501 | var stderr_buf = Buffer.initNull(b.allocator); | |
| 1502 | ||
| 1503 | var stdout_file_in_stream = child.stdout.?.inStream(); | |
| 1504 | var stderr_file_in_stream = child.stderr.?.inStream(); | |
| 1505 | ||
| 1506 | stdout_file_in_stream.stream.readAllBuffer(&stdout_buf, max_stdout_size) catch unreachable; | |
| 1507 | stderr_file_in_stream.stream.readAllBuffer(&stderr_buf, max_stdout_size) catch unreachable; | |
| 1508 | ||
| 1509 | const term = child.wait() catch |err| { | |
| 1510 | debug.panic("Unable to spawn {}: {}\n", .{ zig_args.toSliceConst()[0], @errorName(err) }); | |
| 1511 | }; | |
| 1512 | switch (term) { | |
| 1513 | .Exited => |code| { | |
| 1514 | if (code != 0) { | |
| 1515 | warn("Compilation failed with exit code {}\n{}\n", .{ code, stderr_buf.toSliceConst() }); | |
| 1516 | printInvocation(zig_args.toSliceConst()); | |
| 1517 | return error.TestFailed; | |
| 1518 | } | |
| 1519 | }, | |
| 1520 | .Signal => |code| { | |
| 1521 | warn("Compilation failed with signal {}\n", .{code}); | |
| 1522 | printInvocation(zig_args.toSliceConst()); | |
| 1523 | return error.TestFailed; | |
| 1524 | }, | |
| 1525 | else => { | |
| 1526 | warn("Compilation terminated unexpectedly\n", .{}); | |
| 1527 | printInvocation(zig_args.toSliceConst()); | |
| 1528 | return error.TestFailed; | |
| 1529 | }, | |
| 1530 | } | |
| 1531 | ||
| 1532 | const stdout = stdout_buf.toSliceConst(); | |
| 1533 | const stderr = stderr_buf.toSliceConst(); | |
| 1534 | ||
| 1535 | if (stderr.len != 0 and !self.case.allow_warnings) { | |
| 1536 | warn( | |
| 1537 | \\====== translate-c emitted warnings: ======= | |
| 1538 | \\{} | |
| 1539 | \\============================================ | |
| 1540 | \\ | |
| 1541 | , .{stderr}); | |
| 1542 | printInvocation(zig_args.toSliceConst()); | |
| 1543 | return error.TestFailed; | |
| 1544 | } | |
| 1545 | ||
| 1546 | for (self.case.expected_lines.toSliceConst()) |expected_line| { | |
| 1547 | if (mem.indexOf(u8, stdout, expected_line) == null) { | |
| 1548 | warn( | |
| 1549 | \\ | |
| 1550 | \\========= Expected this output: ================ | |
| 1551 | \\{} | |
| 1552 | \\========= But found: =========================== | |
| 1553 | \\{} | |
| 1554 | \\ | |
| 1555 | , .{ expected_line, stdout }); | |
| 1556 | printInvocation(zig_args.toSliceConst()); | |
| 1557 | return error.TestFailed; | |
| 1558 | } | |
| 1559 | } | |
| 1560 | warn("OK\n", .{}); | |
| 1561 | } | |
| 1562 | }; | |
| 1563 | ||
| 1564 | fn printInvocation(args: []const []const u8) void { | |
| 1565 | for (args) |arg| { | |
| 1566 | warn("{} ", .{arg}); | |
| 1567 | } | |
| 1568 | warn("\n", .{}); | |
| 1569 | } | |
| 1570 | ||
| 1571 | pub fn create( | |
| 1572 | self: *TranslateCContext, | |
| 1573 | allow_warnings: bool, | |
| 1574 | filename: []const u8, | |
| 1575 | name: []const u8, | |
| 1576 | source: []const u8, | |
| 1577 | expected_lines: []const []const u8, | |
| 1578 | ) *TestCase { | |
| 1579 | const tc = self.b.allocator.create(TestCase) catch unreachable; | |
| 1580 | tc.* = TestCase{ | |
| 1581 | .name = name, | |
| 1582 | .sources = ArrayList(TestCase.SourceFile).init(self.b.allocator), | |
| 1583 | .expected_lines = ArrayList([]const u8).init(self.b.allocator), | |
| 1584 | .allow_warnings = allow_warnings, | |
| 1585 | }; | |
| 1586 | ||
| 1587 | tc.addSourceFile(filename, source); | |
| 1588 | var arg_i: usize = 0; | |
| 1589 | while (arg_i < expected_lines.len) : (arg_i += 1) { | |
| 1590 | tc.addExpectedLine(expected_lines[arg_i]); | |
| 1591 | } | |
| 1592 | return tc; | |
| 1593 | } | |
| 1594 | ||
| 1595 | pub fn add( | |
| 1596 | self: *TranslateCContext, | |
| 1597 | name: []const u8, | |
| 1598 | source: []const u8, | |
| 1599 | expected_lines: []const []const u8, | |
| 1600 | ) void { | |
| 1601 | const tc = self.create(false, "source.h", name, source, expected_lines); | |
| 1602 | self.addCase(tc); | |
| 1603 | } | |
| 1604 | ||
| 1605 | pub fn addAllowWarnings( | |
| 1606 | self: *TranslateCContext, | |
| 1607 | name: []const u8, | |
| 1608 | source: []const u8, | |
| 1609 | expected_lines: []const []const u8, | |
| 1610 | ) void { | |
| 1611 | const tc = self.create(true, "source.h", name, source, expected_lines); | |
| 1612 | self.addCase(tc); | |
| 1613 | } | |
| 1614 | ||
| 1615 | pub fn addCase(self: *TranslateCContext, case: *const TestCase) void { | |
| 1616 | const b = self.b; | |
| 1617 | ||
| 1618 | const translate_c_cmd = "translate-c"; | |
| 1619 | const annotated_case_name = fmt.allocPrint(self.b.allocator, "{} {}", .{ translate_c_cmd, case.name }) catch unreachable; | |
| 1620 | if (self.test_filter) |filter| { | |
| 1621 | if (mem.indexOf(u8, annotated_case_name, filter) == null) return; | |
| 1622 | } | |
| 1623 | ||
| 1624 | const translate_c_and_cmp = TranslateCCmpOutputStep.create(self, annotated_case_name, case); | |
| 1625 | self.step.dependOn(&translate_c_and_cmp.step); | |
| 1626 | ||
| 1627 | for (case.sources.toSliceConst()) |src_file| { | |
| 1628 | const expanded_src_path = fs.path.join( | |
| 1629 | b.allocator, | |
| 1630 | &[_][]const u8{ b.cache_root, src_file.filename }, | |
| 1631 | ) catch unreachable; | |
| 1632 | const write_src = b.addWriteFile(expanded_src_path, src_file.source); | |
| 1633 | translate_c_and_cmp.step.dependOn(&write_src.step); | |
| 1634 | } | |
| 1635 | } | |
| 1636 | }; | |
| 1637 | ||
| 1638 | 1079 | pub const GenHContext = struct { |
| 1639 | 1080 | b: *build.Builder, |
| 1640 | 1081 | step: *build.Step, |
| ... | ... | @@ -1754,10 +1195,6 @@ pub const GenHContext = struct { |
| 1754 | 1195 | |
| 1755 | 1196 | pub fn addCase(self: *GenHContext, case: *const TestCase) void { |
| 1756 | 1197 | const b = self.b; |
| 1757 | const root_src = fs.path.join( | |
| 1758 | b.allocator, | |
| 1759 | &[_][]const u8{ b.cache_root, case.sources.items[0].filename }, | |
| 1760 | ) catch unreachable; | |
| 1761 | 1198 | |
| 1762 | 1199 | const mode = builtin.Mode.Debug; |
| 1763 | 1200 | const annotated_case_name = fmt.allocPrint(self.b.allocator, "gen-h {} ({})", .{ case.name, @tagName(mode) }) catch unreachable; |
| ... | ... | @@ -1765,18 +1202,14 @@ pub const GenHContext = struct { |
| 1765 | 1202 | if (mem.indexOf(u8, annotated_case_name, filter) == null) return; |
| 1766 | 1203 | } |
| 1767 | 1204 | |
| 1768 | const obj = b.addObject("test", root_src); | |
| 1769 | obj.setBuildMode(mode); | |
| 1770 | ||
| 1205 | const write_src = b.addWriteFiles(); | |
| 1771 | 1206 | for (case.sources.toSliceConst()) |src_file| { |
| 1772 | const expanded_src_path = fs.path.join( | |
| 1773 | b.allocator, | |
| 1774 | &[_][]const u8{ b.cache_root, src_file.filename }, | |
| 1775 | ) catch unreachable; | |
| 1776 | const write_src = b.addWriteFile(expanded_src_path, src_file.source); | |
| 1777 | obj.step.dependOn(&write_src.step); | |
| 1207 | write_src.add(src_file.filename, src_file.source); | |
| 1778 | 1208 | } |
| 1779 | 1209 | |
| 1210 | const obj = b.addObjectFromWriteFileStep("test", write_src, case.sources.items[0].filename); | |
| 1211 | obj.setBuildMode(mode); | |
| 1212 | ||
| 1780 | 1213 | const cmp_h = GenHCmpOutputStep.create(self, obj, annotated_case_name, case); |
| 1781 | 1214 | |
| 1782 | 1215 | self.step.dependOn(&cmp_h.step); |