authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-01-05 14:50:02-05:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2020-01-05 14:50:02-05:00
loga0ca34979ea5ff9be0d353f539e7c86dedbf8693
treee81c0dbbc2b7b81ce1d6f46b79ee781e31326ebc
parent2e5342512f32384777ebcb0bf3dcb177b9380080
parent242f5d10d57eb9239e7a89d4e705fc05785abe7c
signature Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #4053 from ziglang/test-run-translated-c

add test harness for "run translated C" tests

15 files changed, 1226 insertions(+), 815 deletions(-)

build.zig+1
...@@ -137,6 +137,7 @@ pub fn build(b: *Builder) !void {...@@ -137,6 +137,7 @@ pub fn build(b: *Builder) !void {
137 test_step.dependOn(tests.addAssembleAndLinkTests(b, test_filter, modes));137 test_step.dependOn(tests.addAssembleAndLinkTests(b, test_filter, modes));
138 test_step.dependOn(tests.addRuntimeSafetyTests(b, test_filter, modes));138 test_step.dependOn(tests.addRuntimeSafetyTests(b, test_filter, modes));
139 test_step.dependOn(tests.addTranslateCTests(b, test_filter));139 test_step.dependOn(tests.addTranslateCTests(b, test_filter));
140 test_step.dependOn(tests.addRunTranslatedCTests(b, test_filter));
140 test_step.dependOn(tests.addGenHTests(b, test_filter));141 test_step.dependOn(tests.addGenHTests(b, test_filter));
141 test_step.dependOn(tests.addCompileErrorTests(b, test_filter, modes));142 test_step.dependOn(tests.addCompileErrorTests(b, test_filter, modes));
142 test_step.dependOn(docs_step);143 test_step.dependOn(docs_step);
lib/std/build.zig+146-192
...@@ -17,6 +17,10 @@ const fmt_lib = std.fmt;...@@ -17,6 +17,10 @@ const fmt_lib = std.fmt;
17const File = std.fs.File;17const File = std.fs.File;
1818
19pub const FmtStep = @import("build/fmt.zig").FmtStep;19pub const FmtStep = @import("build/fmt.zig").FmtStep;
20pub const TranslateCStep = @import("build/translate_c.zig").TranslateCStep;
21pub const WriteFileStep = @import("build/write_file.zig").WriteFileStep;
22pub const RunStep = @import("build/run.zig").RunStep;
23pub const CheckFileStep = @import("build/check_file.zig").CheckFileStep;
2024
21pub const Builder = struct {25pub const Builder = struct {
22 install_tls: TopLevelStep,26 install_tls: TopLevelStep,
...@@ -203,23 +207,67 @@ pub const Builder = struct {...@@ -203,23 +207,67 @@ pub const Builder = struct {
203 }207 }
204208
205 pub fn addExecutable(self: *Builder, name: []const u8, root_src: ?[]const u8) *LibExeObjStep {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 return LibExeObjStep.createExecutable(self, name, root_src, false);237 return LibExeObjStep.createExecutable(self, name, root_src, false);
207 }238 }
208239
209 pub fn addObject(self: *Builder, name: []const u8, root_src: ?[]const u8) *LibExeObjStep {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 }
212258
213 pub fn addSharedLibrary(self: *Builder, name: []const u8, root_src: ?[]const u8, ver: Version) *LibExeObjStep {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 }
216263
217 pub fn addStaticLibrary(self: *Builder, name: []const u8, root_src: ?[]const u8) *LibExeObjStep {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 }
220268
221 pub fn addTest(self: *Builder, root_src: []const u8) *LibExeObjStep {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 }
224272
225 pub fn addAssemble(self: *Builder, name: []const u8, src: []const u8) *LibExeObjStep {273 pub fn addAssemble(self: *Builder, name: []const u8, src: []const u8) *LibExeObjStep {
...@@ -256,8 +304,14 @@ pub const Builder = struct {...@@ -256,8 +304,14 @@ pub const Builder = struct {
256 }304 }
257305
258 pub fn addWriteFile(self: *Builder, file_path: []const u8, data: []const u8) *WriteFileStep {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 const write_file_step = self.allocator.create(WriteFileStep) catch unreachable;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 return write_file_step;315 return write_file_step;
262 }316 }
263317
...@@ -278,6 +332,10 @@ pub const Builder = struct {...@@ -278,6 +332,10 @@ pub const Builder = struct {
278 return FmtStep.create(self, paths);332 return FmtStep.create(self, paths);
279 }333 }
280334
335 pub fn addTranslateC(self: *Builder, source: FileSource) *TranslateCStep {
336 return TranslateCStep.create(self, source);
337 }
338
281 pub fn version(self: *const Builder, major: u32, minor: u32, patch: u32) Version {339 pub fn version(self: *const Builder, major: u32, minor: u32, patch: u32) Version {
282 return Version{340 return Version{
283 .major = major,341 .major = major,
...@@ -1002,7 +1060,7 @@ const Pkg = struct {...@@ -1002,7 +1060,7 @@ const Pkg = struct {
1002};1060};
10031061
1004const CSourceFile = struct {1062const CSourceFile = struct {
1005 source_path: []const u8,1063 source: FileSource,
1006 args: []const []const u8,1064 args: []const []const u8,
1007};1065};
10081066
...@@ -1015,6 +1073,33 @@ fn isLibCLibrary(name: []const u8) bool {...@@ -1015,6 +1073,33 @@ fn isLibCLibrary(name: []const u8) bool {
1015 return false;1073 return false;
1016}1074}
10171075
1076pub 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
1018pub const LibExeObjStep = struct {1103pub const LibExeObjStep = struct {
1019 step: Step,1104 step: Step,
1020 builder: *Builder,1105 builder: *Builder,
...@@ -1047,7 +1132,7 @@ pub const LibExeObjStep = struct {...@@ -1047,7 +1132,7 @@ pub const LibExeObjStep = struct {
1047 filter: ?[]const u8,1132 filter: ?[]const u8,
1048 single_threaded: bool,1133 single_threaded: bool,
10491134
1050 root_src: ?[]const u8,1135 root_src: ?FileSource,
1051 out_h_filename: []const u8,1136 out_h_filename: []const u8,
1052 out_lib_filename: []const u8,1137 out_lib_filename: []const u8,
1053 out_pdb_filename: []const u8,1138 out_pdb_filename: []const u8,
...@@ -1099,7 +1184,7 @@ pub const LibExeObjStep = struct {...@@ -1099,7 +1184,7 @@ pub const LibExeObjStep = struct {
1099 StaticPath: []const u8,1184 StaticPath: []const u8,
1100 OtherStep: *LibExeObjStep,1185 OtherStep: *LibExeObjStep,
1101 SystemLib: []const u8,1186 SystemLib: []const u8,
1102 AssemblyFile: []const u8,1187 AssemblyFile: FileSource,
1103 CSourceFile: *CSourceFile,1188 CSourceFile: *CSourceFile,
1104 };1189 };
11051190
...@@ -1116,37 +1201,44 @@ pub const LibExeObjStep = struct {...@@ -1116,37 +1201,44 @@ pub const LibExeObjStep = struct {
1116 Test,1201 Test,
1117 };1202 };
11181203
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 const self = builder.allocator.create(LibExeObjStep) catch unreachable;1205 const self = builder.allocator.create(LibExeObjStep) catch unreachable;
1121 self.* = initExtraArgs(builder, name, root_src, Kind.Lib, true, ver);1206 self.* = initExtraArgs(builder, name, root_src, Kind.Lib, true, ver);
1122 return self;1207 return self;
1123 }1208 }
11241209
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 const self = builder.allocator.create(LibExeObjStep) catch unreachable;1211 const self = builder.allocator.create(LibExeObjStep) catch unreachable;
1127 self.* = initExtraArgs(builder, name, root_src, Kind.Lib, false, builder.version(0, 0, 0));1212 self.* = initExtraArgs(builder, name, root_src, Kind.Lib, false, builder.version(0, 0, 0));
1128 return self;1213 return self;
1129 }1214 }
11301215
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 const self = builder.allocator.create(LibExeObjStep) catch unreachable;1217 const self = builder.allocator.create(LibExeObjStep) catch unreachable;
1133 self.* = initExtraArgs(builder, name, root_src, Kind.Obj, false, builder.version(0, 0, 0));1218 self.* = initExtraArgs(builder, name, root_src, Kind.Obj, false, builder.version(0, 0, 0));
1134 return self;1219 return self;
1135 }1220 }
11361221
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 const self = builder.allocator.create(LibExeObjStep) catch unreachable;1223 const self = builder.allocator.create(LibExeObjStep) catch unreachable;
1139 self.* = initExtraArgs(builder, name, root_src, Kind.Exe, is_dynamic, builder.version(0, 0, 0));1224 self.* = initExtraArgs(builder, name, root_src, Kind.Exe, is_dynamic, builder.version(0, 0, 0));
1140 return self;1225 return self;
1141 }1226 }
11421227
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 const self = builder.allocator.create(LibExeObjStep) catch unreachable;1229 const self = builder.allocator.create(LibExeObjStep) catch unreachable;
1145 self.* = initExtraArgs(builder, name, root_src, Kind.Test, false, builder.version(0, 0, 0));1230 self.* = initExtraArgs(builder, name, root_src, Kind.Test, false, builder.version(0, 0, 0));
1146 return self;1231 return self;
1147 }1232 }
11481233
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 if (mem.indexOf(u8, name, "/") != null or mem.indexOf(u8, name, "\\") != null) {1242 if (mem.indexOf(u8, name, "/") != null or mem.indexOf(u8, name, "\\") != null) {
1151 panic("invalid name: '{}'. It looks like a file path, but it is supposed to be the library or application name.", .{name});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,6 +1288,7 @@ pub const LibExeObjStep = struct {
1196 .install_step = null,1288 .install_step = null,
1197 };1289 };
1198 self.computeOutFileNames();1290 self.computeOutFileNames();
1291 if (root_src) |rs| rs.addStepDependencies(&self.step);
1199 return self;1292 return self;
1200 }1293 }
12011294
...@@ -1486,15 +1579,22 @@ pub const LibExeObjStep = struct {...@@ -1486,15 +1579,22 @@ pub const LibExeObjStep = struct {
1486 }1579 }
14871580
1488 pub fn addCSourceFile(self: *LibExeObjStep, file: []const u8, args: []const []const u8) void {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 const c_source_file = self.builder.allocator.create(CSourceFile) catch unreachable;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;1590
1491 for (args) |arg, i| {1591 const args_copy = self.builder.allocator.alloc([]u8, source.args.len) catch unreachable;
1592 for (source.args) |arg, i| {
1492 args_copy[i] = self.builder.dupe(arg);1593 args_copy[i] = self.builder.dupe(arg);
1493 }1594 }
1494 c_source_file.* = CSourceFile{1595
1495 .source_path = self.builder.dupe(file),1596 c_source_file.* = source;
1496 .args = args_copy,1597 c_source_file.args = args_copy;
1497 };
1498 self.link_objects.append(LinkObject{ .CSourceFile = c_source_file }) catch unreachable;1598 self.link_objects.append(LinkObject{ .CSourceFile = c_source_file }) catch unreachable;
1499 }1599 }
15001600
...@@ -1571,6 +1671,20 @@ pub const LibExeObjStep = struct {...@@ -1571,6 +1671,20 @@ pub const LibExeObjStep = struct {
1571 self.link_objects.append(LinkObject{ .AssemblyFile = self.builder.dupe(path) }) catch unreachable;1671 self.link_objects.append(LinkObject{ .AssemblyFile = self.builder.dupe(path) }) catch unreachable;
1572 }1672 }
15731673
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 pub fn addObjectFile(self: *LibExeObjStep, path: []const u8) void {1688 pub fn addObjectFile(self: *LibExeObjStep, path: []const u8) void {
1575 self.link_objects.append(LinkObject{ .StaticPath = self.builder.dupe(path) }) catch unreachable;1689 self.link_objects.append(LinkObject{ .StaticPath = self.builder.dupe(path) }) catch unreachable;
1576 }1690 }
...@@ -1698,25 +1812,23 @@ pub const LibExeObjStep = struct {...@@ -1698,25 +1812,23 @@ pub const LibExeObjStep = struct {
1698 };1812 };
1699 zig_args.append(cmd) catch unreachable;1813 zig_args.append(cmd) catch unreachable;
17001814
1701 if (self.root_src) |root_src| {1815 if (self.root_src) |root_src| try zig_args.append(root_src.getPath(builder));
1702 zig_args.append(builder.pathFromRoot(root_src)) catch unreachable;
1703 }
17041816
1705 for (self.link_objects.toSlice()) |link_object| {1817 for (self.link_objects.toSlice()) |link_object| {
1706 switch (link_object) {1818 switch (link_object) {
1707 LinkObject.StaticPath => |static_path| {1819 .StaticPath => |static_path| {
1708 try zig_args.append("--object");1820 try zig_args.append("--object");
1709 try zig_args.append(builder.pathFromRoot(static_path));1821 try zig_args.append(builder.pathFromRoot(static_path));
1710 },1822 },
17111823
1712 LinkObject.OtherStep => |other| switch (other.kind) {1824 .OtherStep => |other| switch (other.kind) {
1713 LibExeObjStep.Kind.Exe => unreachable,1825 .Exe => unreachable,
1714 LibExeObjStep.Kind.Test => unreachable,1826 .Test => unreachable,
1715 LibExeObjStep.Kind.Obj => {1827 .Obj => {
1716 try zig_args.append("--object");1828 try zig_args.append("--object");
1717 try zig_args.append(other.getOutputPath());1829 try zig_args.append(other.getOutputPath());
1718 },1830 },
1719 LibExeObjStep.Kind.Lib => {1831 .Lib => {
1720 if (!other.is_dynamic or self.target.isWindows()) {1832 if (!other.is_dynamic or self.target.isWindows()) {
1721 try zig_args.append("--object");1833 try zig_args.append("--object");
1722 try zig_args.append(other.getOutputLibPath());1834 try zig_args.append(other.getOutputLibPath());
...@@ -1732,20 +1844,20 @@ pub const LibExeObjStep = struct {...@@ -1732,20 +1844,20 @@ pub const LibExeObjStep = struct {
1732 }1844 }
1733 },1845 },
1734 },1846 },
1735 LinkObject.SystemLib => |name| {1847 .SystemLib => |name| {
1736 try zig_args.append("--library");1848 try zig_args.append("--library");
1737 try zig_args.append(name);1849 try zig_args.append(name);
1738 },1850 },
1739 LinkObject.AssemblyFile => |asm_file| {1851 .AssemblyFile => |asm_file| {
1740 try zig_args.append("--c-source");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 try zig_args.append("--c-source");1856 try zig_args.append("--c-source");
1745 for (c_source_file.args) |arg| {1857 for (c_source_file.args) |arg| {
1746 try zig_args.append(arg);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,134 +2153,6 @@ pub const LibExeObjStep = struct {
2041 }2153 }
2042};2154};
20432155
2044pub 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
2172const InstallArtifactStep = struct {2156const InstallArtifactStep = struct {
2173 step: Step,2157 step: Step,
2174 builder: *Builder,2158 builder: *Builder,
...@@ -2321,36 +2305,6 @@ pub const InstallDirStep = struct {...@@ -2321,36 +2305,6 @@ pub const InstallDirStep = struct {
2321 }2305 }
2322};2306};
23232307
2324pub 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
2354pub const LogStep = struct {2308pub const LogStep = struct {
2355 step: Step,2309 step: Step,
2356 builder: *Builder,2310 builder: *Builder,
lib/std/build/check_file.zig created+52
...@@ -0,0 +1,52 @@
1const std = @import("../std.zig");
2const build = std.build;
3const Step = build.Step;
4const Builder = build.Builder;
5const fs = std.fs;
6const mem = std.mem;
7const warn = std.debug.warn;
8
9pub 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 @@
1const std = @import("../std.zig");
2const builtin = std.builtin;
3const build = std.build;
4const Step = build.Step;
5const Builder = build.Builder;
6const LibExeObjStep = build.LibExeObjStep;
7const fs = std.fs;
8const mem = std.mem;
9const process = std.process;
10const ArrayList = std.ArrayList;
11const BufMap = std.BufMap;
12const Buffer = std.Buffer;
13const warn = std.debug.warn;
14
15const max_stdout_size = 1 * 1024 * 1024; // 1 MiB
16
17pub 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 @@
1const std = @import("../std.zig");
2const build = std.build;
3const Step = build.Step;
4const Builder = build.Builder;
5const WriteFileStep = build.WriteFileStep;
6const LibExeObjStep = build.LibExeObjStep;
7const CheckFileStep = build.CheckFileStep;
8const fs = std.fs;
9const mem = std.mem;
10
11pub 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 @@
1const std = @import("../std.zig");
2const build = @import("../build.zig");
3const Step = build.Step;
4const Builder = build.Builder;
5const fs = std.fs;
6const warn = std.debug.warn;
7const ArrayList = std.ArrayList;
8
9pub 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,8 +37,11 @@ pub const MAX_PATH_BYTES = switch (builtin.os) {
37 else => @compileError("Unsupported OS"),37 else => @compileError("Unsupported OS"),
38};38};
3939
40// here we replace the standard +/ with -_ so that it can be used in a file name40/// Base64, replacing the standard `+/` with `-_` so that it can be used in a file name on any filesystem.
41const b64_fs_encoder = base64.Base64Encoder.init("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_", base64.standard_pad_char);41pub const base64_encoder = base64.Base64Encoder.init(
42 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_",
43 base64.standard_pad_char,
44);
4245
43/// TODO remove the allocator requirement from this API46/// TODO remove the allocator requirement from this API
44pub fn atomicSymLink(allocator: *Allocator, existing_path: []const u8, new_path: []const u8) !void {47pub 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,7 +61,7 @@ pub fn atomicSymLink(allocator: *Allocator, existing_path: []const u8, new_path:
58 tmp_path[dirname.len] = path.sep;61 tmp_path[dirname.len] = path.sep;
59 while (true) {62 while (true) {
60 try crypto.randomBytes(rand_buf[0..]);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);
6265
63 if (symLink(existing_path, tmp_path)) {66 if (symLink(existing_path, tmp_path)) {
64 return rename(tmp_path, new_path);67 return rename(tmp_path, new_path);
...@@ -227,10 +230,10 @@ pub const AtomicFile = struct {...@@ -227,10 +230,10 @@ pub const AtomicFile = struct {
227230
228 while (true) {231 while (true) {
229 try crypto.randomBytes(rand_buf[0..]);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);
231234
232 const file = my_cwd.createFileC(235 const file = my_cwd.createFileC(
233 tmp_path_slice, 236 tmp_path_slice,
234 .{ .mode = mode, .exclusive = true },237 .{ .mode = mode, .exclusive = true },
235 ) catch |err| switch (err) {238 ) catch |err| switch (err) {
236 error.PathAlreadyExists => continue,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,6 +194,7 @@ static LLVMValueRef build_alloca(CodeGen *g, ZigType *type_entry, const char *na
194static LLVMValueRef gen_await_early_return(CodeGen *g, IrInstruction *source_instr,194static LLVMValueRef gen_await_early_return(CodeGen *g, IrInstruction *source_instr,
195 LLVMValueRef target_frame_ptr, ZigType *result_type, ZigType *ptr_result_type,195 LLVMValueRef target_frame_ptr, ZigType *result_type, ZigType *ptr_result_type,
196 LLVMValueRef result_loc, bool non_async);196 LLVMValueRef result_loc, bool non_async);
197static Error get_tmp_filename(CodeGen *g, Buf *out, Buf *suffix);
197198
198static void addLLVMAttr(LLVMValueRef val, LLVMAttributeIndex attr_index, const char *attr_name) {199static void addLLVMAttr(LLVMValueRef val, LLVMAttributeIndex attr_index, const char *attr_name) {
199 unsigned kind_id = LLVMGetEnumAttributeKindForName(attr_name, strlen(attr_name));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,8 +9103,9 @@ void add_cc_args(CodeGen *g, ZigList<const char *> &args, const char *out_dep_pa
91029103
9103}9104}
91049105
9105void codegen_translate_c(CodeGen *g, Buf *full_path, FILE *out_file) {9106void codegen_translate_c(CodeGen *g, Buf *full_path) {
9106 Error err;9107 Error err;
9108
9107 Buf *src_basename = buf_alloc();9109 Buf *src_basename = buf_alloc();
9108 Buf *src_dirname = buf_alloc();9110 Buf *src_dirname = buf_alloc();
9109 os_path_split(full_path, src_dirname, src_basename);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,12 +9113,61 @@ void codegen_translate_c(CodeGen *g, Buf *full_path, FILE *out_file) {
9111 Buf noextname = BUF_INIT;9113 Buf noextname = BUF_INIT;
9112 os_path_extname(src_basename, &noextname, nullptr);9114 os_path_extname(src_basename, &noextname, nullptr);
91139115
9116 Buf *zig_basename = buf_sprintf("%s.zig", buf_ptr(&noextname));
9117
9114 detect_libc(g);9118 detect_libc(g);
91159119
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 init(g);9153 init(g);
91179154
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 ZigList<const char *> clang_argv = {0};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);
91209171
9121 clang_argv.append(buf_ptr(full_path));9172 clang_argv.append(buf_ptr(full_path));
91229173
...@@ -9160,7 +9211,50 @@ void codegen_translate_c(CodeGen *g, Buf *full_path, FILE *out_file) {...@@ -9160,7 +9211,50 @@ void codegen_translate_c(CodeGen *g, Buf *full_path, FILE *out_file) {
9160 exit(1);9211 exit(1);
9161 }9212 }
91629213
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 stage2_render_ast(ast, out_file);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}
91659259
9166static void update_test_functions_builtin_decl(CodeGen *g) {9260static 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,7 +54,7 @@ ZigPackage *codegen_create_package(CodeGen *g, const char *root_src_dir, const c
54void codegen_add_assembly(CodeGen *g, Buf *path);54void codegen_add_assembly(CodeGen *g, Buf *path);
55void codegen_add_object(CodeGen *g, Buf *object_path);55void codegen_add_object(CodeGen *g, Buf *object_path);
5656
57void codegen_translate_c(CodeGen *g, Buf *full_path, FILE *out_file);57void codegen_translate_c(CodeGen *g, Buf *full_path);
5858
59Buf *codegen_generate_builtin_source(CodeGen *g);59Buf *codegen_generate_builtin_source(CodeGen *g);
6060
src/main.cpp+3-2
...@@ -1164,7 +1164,7 @@ int main(int argc, char **argv) {...@@ -1164,7 +1164,7 @@ int main(int argc, char **argv) {
1164 return print_error_usage(arg0);1164 return print_error_usage(arg0);
1165 }1165 }
11661166
1167 Buf *zig_root_source_file = cmd == CmdTranslateC ? nullptr : in_file_buf;1167 Buf *zig_root_source_file = (cmd == CmdTranslateC) ? nullptr : in_file_buf;
11681168
1169 if (cmd == CmdRun && buf_out_name == nullptr) {1169 if (cmd == CmdRun && buf_out_name == nullptr) {
1170 buf_out_name = buf_create_from_str("run");1170 buf_out_name = buf_create_from_str("run");
...@@ -1330,7 +1330,8 @@ int main(int argc, char **argv) {...@@ -1330,7 +1330,8 @@ int main(int argc, char **argv) {
1330 zig_unreachable();1330 zig_unreachable();
1331 }1331 }
1332 } else if (cmd == CmdTranslateC) {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 if (timing_info)1335 if (timing_info)
1335 codegen_print_timing_report(g, stderr);1336 codegen_print_timing_report(g, stderr);
1336 return main_exit(root_progress_node, EXIT_SUCCESS);1337 return main_exit(root_progress_node, EXIT_SUCCESS);
test/run_translated_c.zig created+26
...@@ -0,0 +1,26 @@
1const std = @import("std");
2const tests = @import("tests.zig");
3const nl = std.cstr.line_sep;
4
5pub 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.
3const std = @import("std");
4const builtin = std.builtin;
5const build = std.build;
6const ArrayList = std.ArrayList;
7const fmt = std.fmt;
8const mem = std.mem;
9const fs = std.fs;
10const warn = std.debug.warn;
11const Mode = builtin.Mode;
12
13pub 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.
3const std = @import("std");
4const build = std.build;
5const ArrayList = std.ArrayList;
6const fmt = std.fmt;
7const mem = std.mem;
8const fs = std.fs;
9const warn = std.debug.warn;
10
11pub 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.
3const std = @import("std");
4const build = std.build;
5const ArrayList = std.ArrayList;
6const fmt = std.fmt;
7const mem = std.mem;
8const fs = std.fs;
9const warn = std.debug.warn;
10
11pub 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,6 +14,7 @@ const builtin = @import("builtin");
14const Mode = builtin.Mode;14const Mode = builtin.Mode;
15const LibExeObjStep = build.LibExeObjStep;15const LibExeObjStep = build.LibExeObjStep;
1616
17// Cases
17const compare_output = @import("compare_output.zig");18const compare_output = @import("compare_output.zig");
18const standalone = @import("standalone.zig");19const standalone = @import("standalone.zig");
19const stack_traces = @import("stack_traces.zig");20const stack_traces = @import("stack_traces.zig");
...@@ -21,8 +22,14 @@ const compile_errors = @import("compile_errors.zig");...@@ -21,8 +22,14 @@ const compile_errors = @import("compile_errors.zig");
21const assemble_and_link = @import("assemble_and_link.zig");22const assemble_and_link = @import("assemble_and_link.zig");
22const runtime_safety = @import("runtime_safety.zig");23const runtime_safety = @import("runtime_safety.zig");
23const translate_c = @import("translate_c.zig");24const translate_c = @import("translate_c.zig");
25const run_translated_c = @import("run_translated_c.zig");
24const gen_h = @import("gen_h.zig");26const gen_h = @import("gen_h.zig");
2527
28// Implementations
29pub const TranslateCContext = @import("src/translate_c.zig").TranslateCContext;
30pub const RunTranslatedCContext = @import("src/run_translated_c.zig").RunTranslatedCContext;
31pub const CompareOutputContext = @import("src/compare_output.zig").CompareOutputContext;
32
26const TestTarget = struct {33const TestTarget = struct {
27 target: Target = .Native,34 target: Target = .Native,
28 mode: builtin.Mode = .Debug,35 mode: builtin.Mode = .Debug,
...@@ -383,6 +390,20 @@ pub fn addTranslateCTests(b: *build.Builder, test_filter: ?[]const u8) *build.St...@@ -383,6 +390,20 @@ pub fn addTranslateCTests(b: *build.Builder, test_filter: ?[]const u8) *build.St
383 return cases.step;390 return cases.step;
384}391}
385392
393pub 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
386pub fn addGenHTests(b: *build.Builder, test_filter: ?[]const u8) *build.Step {407pub fn addGenHTests(b: *build.Builder, test_filter: ?[]const u8) *build.Step {
387 const cases = b.allocator.create(GenHContext) catch unreachable;408 const cases = b.allocator.create(GenHContext) catch unreachable;
388 cases.* = GenHContext{409 cases.* = GenHContext{
...@@ -479,356 +500,6 @@ pub fn addPkgTests(...@@ -479,356 +500,6 @@ pub fn addPkgTests(
479 return step;500 return step;
480}501}
481502
482pub 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
832pub const StackTracesContext = struct {503pub const StackTracesContext = struct {
833 b: *build.Builder,504 b: *build.Builder,
834 step: *build.Step,505 step: *build.Step,
...@@ -846,11 +517,6 @@ pub const StackTracesContext = struct {...@@ -846,11 +517,6 @@ pub const StackTracesContext = struct {
846 ) void {517 ) void {
847 const b = self.b;518 const b = self.b;
848519
849 const source_pathname = fs.path.join(
850 b.allocator,
851 &[_][]const u8{ b.cache_root, "source.zig" },
852 ) catch unreachable;
853
854 for (self.modes) |mode| {520 for (self.modes) |mode| {
855 const expect_for_mode = expect[@enumToInt(mode)];521 const expect_for_mode = expect[@enumToInt(mode)];
856 if (expect_for_mode.len == 0) continue;522 if (expect_for_mode.len == 0) continue;
...@@ -864,12 +530,11 @@ pub const StackTracesContext = struct {...@@ -864,12 +530,11 @@ pub const StackTracesContext = struct {
864 if (mem.indexOf(u8, annotated_case_name, filter) == null) continue;530 if (mem.indexOf(u8, annotated_case_name, filter) == null) continue;
865 }531 }
866532
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 exe.setBuildMode(mode);536 exe.setBuildMode(mode);
869537
870 const write_source = b.addWriteFile(source_pathname, source);
871 exe.step.dependOn(&write_source.step);
872
873 const run_and_compare = RunAndCompareStep.create(538 const run_and_compare = RunAndCompareStep.create(
874 self,539 self,
875 exe,540 exe,
...@@ -1072,6 +737,7 @@ pub const CompileErrorContext = struct {...@@ -1072,6 +737,7 @@ pub const CompileErrorContext = struct {
1072 test_index: usize,737 test_index: usize,
1073 case: *const TestCase,738 case: *const TestCase,
1074 build_mode: Mode,739 build_mode: Mode,
740 write_src: *build.WriteFileStep,
1075741
1076 const ErrLineIter = struct {742 const ErrLineIter = struct {
1077 lines: mem.SplitIterator,743 lines: mem.SplitIterator,
...@@ -1091,7 +757,13 @@ pub const CompileErrorContext = struct {...@@ -1091,7 +757,13 @@ pub const CompileErrorContext = struct {
1091 }757 }
1092 };758 };
1093759
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 const allocator = context.b.allocator;767 const allocator = context.b.allocator;
1096 const ptr = allocator.create(CompileCmpOutputStep) catch unreachable;768 const ptr = allocator.create(CompileCmpOutputStep) catch unreachable;
1097 ptr.* = CompileCmpOutputStep{769 ptr.* = CompileCmpOutputStep{
...@@ -1101,6 +773,7 @@ pub const CompileErrorContext = struct {...@@ -1101,6 +773,7 @@ pub const CompileErrorContext = struct {
1101 .test_index = context.test_index,773 .test_index = context.test_index,
1102 .case = case,774 .case = case,
1103 .build_mode = build_mode,775 .build_mode = build_mode,
776 .write_src = write_src,
1104 };777 };
1105778
1106 context.test_index += 1;779 context.test_index += 1;
...@@ -1111,11 +784,6 @@ pub const CompileErrorContext = struct {...@@ -1111,11 +784,6 @@ pub const CompileErrorContext = struct {
1111 const self = @fieldParentPtr(CompileCmpOutputStep, "step", step);784 const self = @fieldParentPtr(CompileCmpOutputStep, "step", step);
1112 const b = self.context.b;785 const b = self.context.b;
1113786
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 var zig_args = ArrayList([]const u8).init(b.allocator);787 var zig_args = ArrayList([]const u8).init(b.allocator);
1120 zig_args.append(b.zig_exe) catch unreachable;788 zig_args.append(b.zig_exe) catch unreachable;
1121789
...@@ -1126,7 +794,8 @@ pub const CompileErrorContext = struct {...@@ -1126,7 +794,8 @@ pub const CompileErrorContext = struct {
1126 } else {794 } else {
1127 try zig_args.append("build-obj");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));
1130799
1131 zig_args.append("--name") catch unreachable;800 zig_args.append("--name") catch unreachable;
1132 zig_args.append("test") catch unreachable;801 zig_args.append("test") catch unreachable;
...@@ -1325,18 +994,14 @@ pub const CompileErrorContext = struct {...@@ -1325,18 +994,14 @@ pub const CompileErrorContext = struct {
1325 if (self.test_filter) |filter| {994 if (self.test_filter) |filter| {
1326 if (mem.indexOf(u8, annotated_case_name, filter) == null) return;995 if (mem.indexOf(u8, annotated_case_name, filter) == null) return;
1327 }996 }
1328997 const write_src = b.addWriteFiles();
1329 const compile_and_cmp_errors = CompileCmpOutputStep.create(self, annotated_case_name, case, .Debug);
1330 self.step.dependOn(&compile_and_cmp_errors.step);
1331
1332 for (case.sources.toSliceConst()) |src_file| {998 for (case.sources.toSliceConst()) |src_file| {
1333 const expanded_src_path = fs.path.join(999 write_src.add(src_file.filename, src_file.source);
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);
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};
13421007
...@@ -1411,230 +1076,6 @@ pub const StandaloneContext = struct {...@@ -1411,230 +1076,6 @@ pub const StandaloneContext = struct {
1411 }1076 }
1412};1077};
14131078
1414pub 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
1638pub const GenHContext = struct {1079pub const GenHContext = struct {
1639 b: *build.Builder,1080 b: *build.Builder,
1640 step: *build.Step,1081 step: *build.Step,
...@@ -1754,10 +1195,6 @@ pub const GenHContext = struct {...@@ -1754,10 +1195,6 @@ pub const GenHContext = struct {
17541195
1755 pub fn addCase(self: *GenHContext, case: *const TestCase) void {1196 pub fn addCase(self: *GenHContext, case: *const TestCase) void {
1756 const b = self.b;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;
17611198
1762 const mode = builtin.Mode.Debug;1199 const mode = builtin.Mode.Debug;
1763 const annotated_case_name = fmt.allocPrint(self.b.allocator, "gen-h {} ({})", .{ case.name, @tagName(mode) }) catch unreachable;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,18 +1202,14 @@ pub const GenHContext = struct {
1765 if (mem.indexOf(u8, annotated_case_name, filter) == null) return;1202 if (mem.indexOf(u8, annotated_case_name, filter) == null) return;
1766 }1203 }
17671204
1768 const obj = b.addObject("test", root_src);1205 const write_src = b.addWriteFiles();
1769 obj.setBuildMode(mode);
1770
1771 for (case.sources.toSliceConst()) |src_file| {1206 for (case.sources.toSliceConst()) |src_file| {
1772 const expanded_src_path = fs.path.join(1207 write_src.add(src_file.filename, src_file.source);
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);
1778 }1208 }
17791209
1210 const obj = b.addObjectFromWriteFileStep("test", write_src, case.sources.items[0].filename);
1211 obj.setBuildMode(mode);
1212
1780 const cmp_h = GenHCmpOutputStep.create(self, obj, annotated_case_name, case);1213 const cmp_h = GenHCmpOutputStep.create(self, obj, annotated_case_name, case);
17811214
1782 self.step.dependOn(&cmp_h.step);1215 self.step.dependOn(&cmp_h.step);