authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2017-04-20 02:26:36-04:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2017-04-20 02:26:36-04:00
log8654bc18104d64c7a7f9f80bdba75ed4e0c005fa
tree398f7b9d18988b61ad12db21aa72e21da3aec4d4
parent1ff73a8e69a09b3bc993cffb755cc2ca98c7040b

delete test_artifacts directory when tests complete

* add std.os.deleteTree * add std.os.deleteDir * add std.os.page_size * add std.os API for iterating over directories * refactor duplication in build.zig * update documentation on how to run tests

9 files changed, 281 insertions(+), 50 deletions(-)

README.md+3-3
...@@ -45,8 +45,8 @@ compromises backward compatibility....@@ -45,8 +45,8 @@ compromises backward compatibility.
45 * Release mode produces heavily optimized code. What other projects call45 * Release mode produces heavily optimized code. What other projects call
46 "Link Time Optimization" Zig does automatically.46 "Link Time Optimization" Zig does automatically.
47 * Mark functions as tests and automatically run them with `zig test`.47 * Mark functions as tests and automatically run them with `zig test`.
48 * Currently supported architectures: `x86_64`, `i386`48 * Currently supported architectures: `x86_64`
49 * Currently supported operating systems: linux, macosx49 * Currently supported operating systems: linux
50 * Friendly toward package maintainers. Reproducible build, bootstrapping50 * Friendly toward package maintainers. Reproducible build, bootstrapping
51 process carefully documented. Issues filed by package maintainers are51 process carefully documented. Issues filed by package maintainers are
52 considered especially important.52 considered especially important.
...@@ -103,7 +103,7 @@ cd build...@@ -103,7 +103,7 @@ cd build
103cmake .. -DCMAKE_INSTALL_PREFIX=$(pwd) -DZIG_LIBC_LIB_DIR=$(dirname $(cc -print-file-name=crt1.o)) -DZIG_LIBC_INCLUDE_DIR=$(echo -n | cc -E -x c - -v 2>&1 | grep -B1 "End of search list." | head -n1 | cut -c 2- | sed "s/ .*//") -DZIG_LIBC_STATIC_LIB_DIR=$(dirname $(cc -print-file-name=crtbegin.o))103cmake .. -DCMAKE_INSTALL_PREFIX=$(pwd) -DZIG_LIBC_LIB_DIR=$(dirname $(cc -print-file-name=crt1.o)) -DZIG_LIBC_INCLUDE_DIR=$(echo -n | cc -E -x c - -v 2>&1 | grep -B1 "End of search list." | head -n1 | cut -c 2- | sed "s/ .*//") -DZIG_LIBC_STATIC_LIB_DIR=$(dirname $(cc -print-file-name=crtbegin.o))
104make104make
105make install105make install
106./run_tests106./zig build --build-file ../build.zig test
107```107```
108108
109### Release / Install Build109### Release / Install Build
build.zig+13-38
...@@ -5,44 +5,19 @@ pub fn build(b: &Builder) {...@@ -5,44 +5,19 @@ pub fn build(b: &Builder) {
5 const test_filter = b.option([]const u8, "test-filter", "Skip tests that do not match filter");5 const test_filter = b.option([]const u8, "test-filter", "Skip tests that do not match filter");
6 const test_step = b.step("test", "Run all the tests");6 const test_step = b.step("test", "Run all the tests");
77
8 const behavior_tests = b.step("test-behavior", "Run the behavior tests");8 const cleanup = b.addRemoveDirTree("test_artifacts");
9 test_step.dependOn(behavior_tests);9 test_step.dependOn(&cleanup.step);
10 for ([]bool{false, true}) |release| {
11 for ([]bool{false, true}) |link_libc| {
12 const these_tests = b.addTest("test/behavior.zig");
13 these_tests.setNamePrefix(b.fmt("behavior-{}-{} ",
14 if (release) "release" else "debug",
15 if (link_libc) "c" else "bare"));
16 these_tests.setFilter(test_filter);
17 these_tests.setRelease(release);
18 if (link_libc) {
19 these_tests.linkLibrary("c");
20 }
21 behavior_tests.dependOn(&these_tests.step);
22 }
23 }
2410
25 const std_lib_tests = b.step("test-std", "Run the standard library tests");11 cleanup.step.dependOn(tests.addPkgTests(b, test_filter,
26 test_step.dependOn(std_lib_tests);12 "test/behavior.zig", "behavior", "Run the behavior tests"));
27 for ([]bool{false, true}) |release| {
28 for ([]bool{false, true}) |link_libc| {
29 const these_tests = b.addTest("std/index.zig");
30 these_tests.setNamePrefix(b.fmt("std-{}-{} ",
31 if (release) "release" else "debug",
32 if (link_libc) "c" else "bare"));
33 these_tests.setFilter(test_filter);
34 these_tests.setRelease(release);
35 if (link_libc) {
36 these_tests.linkLibrary("c");
37 }
38 std_lib_tests.dependOn(&these_tests.step);
39 }
40 }
4113
42 test_step.dependOn(tests.addCompareOutputTests(b, test_filter));14 cleanup.step.dependOn(tests.addPkgTests(b, test_filter,
43 test_step.dependOn(tests.addBuildExampleTests(b, test_filter));15 "std/index.zig", "std", "Run the standard library tests"));
44 test_step.dependOn(tests.addCompileErrorTests(b, test_filter));16
45 test_step.dependOn(tests.addAssembleAndLinkTests(b, test_filter));17 cleanup.step.dependOn(tests.addCompareOutputTests(b, test_filter));
46 test_step.dependOn(tests.addDebugSafetyTests(b, test_filter));18 cleanup.step.dependOn(tests.addBuildExampleTests(b, test_filter));
47 test_step.dependOn(tests.addParseHTests(b, test_filter));19 cleanup.step.dependOn(tests.addCompileErrorTests(b, test_filter));
20 cleanup.step.dependOn(tests.addAssembleAndLinkTests(b, test_filter));
21 cleanup.step.dependOn(tests.addDebugSafetyTests(b, test_filter));
22 cleanup.step.dependOn(tests.addParseHTests(b, test_filter));
48}23}
std/build.zig+34-2
...@@ -195,6 +195,12 @@ pub const Builder = struct {...@@ -195,6 +195,12 @@ pub const Builder = struct {
195 return log_step;195 return log_step;
196 }196 }
197197
198 pub fn addRemoveDirTree(self: &Builder, dir_path: []const u8) -> &RemoveDirStep {
199 const remove_dir_step = %%self.allocator.create(RemoveDirStep);
200 *remove_dir_step = RemoveDirStep.init(self, dir_path);
201 return remove_dir_step;
202 }
203
198 pub fn version(self: &const Builder, major: u32, minor: u32, patch: u32) -> Version {204 pub fn version(self: &const Builder, major: u32, minor: u32, patch: u32) -> Version {
199 Version {205 Version {
200 .major = major,206 .major = major,
...@@ -1548,10 +1554,12 @@ pub const WriteFileStep = struct {...@@ -1548,10 +1554,12 @@ pub const WriteFileStep = struct {
1548 const full_path = self.builder.pathFromRoot(self.file_path);1554 const full_path = self.builder.pathFromRoot(self.file_path);
1549 const full_path_dir = %%os.path.dirname(self.builder.allocator, full_path);1555 const full_path_dir = %%os.path.dirname(self.builder.allocator, full_path);
1550 os.makePath(self.builder.allocator, full_path_dir) %% |err| {1556 os.makePath(self.builder.allocator, full_path_dir) %% |err| {
1551 debug.panic("unable to make path {}: {}\n", full_path_dir, @errorName(err));1557 %%io.stderr.printf("unable to make path {}: {}\n", full_path_dir, @errorName(err));
1558 return err;
1552 };1559 };
1553 io.writeFile(full_path, self.data, self.builder.allocator) %% |err| {1560 io.writeFile(full_path, self.data, self.builder.allocator) %% |err| {
1554 debug.panic("unable to write {}: {}\n", full_path, @errorName(err));1561 %%io.stderr.printf("unable to write {}: {}\n", full_path, @errorName(err));
1562 return err;
1555 };1563 };
1556 }1564 }
1557};1565};
...@@ -1576,6 +1584,30 @@ pub const LogStep = struct {...@@ -1576,6 +1584,30 @@ pub const LogStep = struct {
1576 }1584 }
1577};1585};
15781586
1587pub const RemoveDirStep = struct {
1588 step: Step,
1589 builder: &Builder,
1590 dir_path: []const u8,
1591
1592 pub fn init(builder: &Builder, dir_path: []const u8) -> RemoveDirStep {
1593 return RemoveDirStep {
1594 .builder = builder,
1595 .step = Step.init(builder.fmt("RemoveDir {}", dir_path), builder.allocator, make),
1596 .dir_path = dir_path,
1597 };
1598 }
1599
1600 fn make(step: &Step) -> %void {
1601 const self = @fieldParentPtr(RemoveDirStep, "step", step);
1602
1603 const full_path = self.builder.pathFromRoot(self.dir_path);
1604 os.deleteTree(self.builder.allocator, full_path) %% |err| {
1605 %%io.stderr.printf("Unable to remove {}: {}\n", full_path, @errorName(err));
1606 return err;
1607 };
1608 }
1609};
1610
1579pub const Step = struct {1611pub const Step = struct {
1580 name: []const u8,1612 name: []const u8,
1581 makeFn: fn(self: &Step) -> %void,1613 makeFn: fn(self: &Step) -> %void,
std/io.zig+4-6
...@@ -57,8 +57,6 @@ error NoMem;...@@ -57,8 +57,6 @@ error NoMem;
57error Unseekable;57error Unseekable;
58error Eof;58error Eof;
5959
60const buffer_size = 4 * 1024;
61
62pub const OpenRead = 0b0001;60pub const OpenRead = 0b0001;
63pub const OpenWrite = 0b0010;61pub const OpenWrite = 0b0010;
64pub const OpenCreate = 0b0100;62pub const OpenCreate = 0b0100;
...@@ -66,7 +64,7 @@ pub const OpenTruncate = 0b1000;...@@ -66,7 +64,7 @@ pub const OpenTruncate = 0b1000;
6664
67pub const OutStream = struct {65pub const OutStream = struct {
68 fd: i32,66 fd: i32,
69 buffer: [buffer_size]u8,67 buffer: [os.page_size]u8,
70 index: usize,68 index: usize,
7169
72 /// `path` may need to be copied in memory to add a null terminating byte. In this case70 /// `path` may need to be copied in memory to add a null terminating byte. In this case
...@@ -97,7 +95,7 @@ pub const OutStream = struct {...@@ -97,7 +95,7 @@ pub const OutStream = struct {
97 }95 }
9896
99 pub fn write(self: &OutStream, bytes: []const u8) -> %void {97 pub fn write(self: &OutStream, bytes: []const u8) -> %void {
100 if (bytes.len >= buffer_size) {98 if (bytes.len >= self.buffer.len) {
101 %return self.flush();99 %return self.flush();
102 return os.posixWrite(self.fd, bytes);100 return os.posixWrite(self.fd, bytes);
103 }101 }
...@@ -329,7 +327,7 @@ pub const InStream = struct {...@@ -329,7 +327,7 @@ pub const InStream = struct {
329 }327 }
330328
331 pub fn readAll(is: &InStream, buf: &Buffer0) -> %void {329 pub fn readAll(is: &InStream, buf: &Buffer0) -> %void {
332 %return buf.resize(buffer_size);330 %return buf.resize(os.page_size);
333331
334 var actual_buf_len: usize = 0;332 var actual_buf_len: usize = 0;
335 while (true) {333 while (true) {
...@@ -341,7 +339,7 @@ pub const InStream = struct {...@@ -341,7 +339,7 @@ pub const InStream = struct {
341 return buf.resize(actual_buf_len);339 return buf.resize(actual_buf_len);
342 }340 }
343341
344 %return buf.resize(actual_buf_len + buffer_size);342 %return buf.resize(actual_buf_len + os.page_size);
345 }343 }
346 }344 }
347};345};
std/list.zig+9-1
...@@ -61,10 +61,15 @@ pub fn List(comptime T: type) -> type{...@@ -61,10 +61,15 @@ pub fn List(comptime T: type) -> type{
61 l.len = new_length;61 l.len = new_length;
62 return result;62 return result;
63 }63 }
64
65 pub fn pop(self: &Self) -> T {
66 self.len -= 1;
67 return self.items[self.len];
68 }
64 }69 }
65}70}
6671
67test "basicListTest" {72test "basic list test" {
68 var list = List(i32).init(&debug.global_allocator);73 var list = List(i32).init(&debug.global_allocator);
69 defer list.deinit();74 defer list.deinit();
7075
...@@ -75,4 +80,7 @@ test "basicListTest" {...@@ -75,4 +80,7 @@ test "basicListTest" {
75 {var i: usize = 0; while (i < 10; i += 1) {80 {var i: usize = 0; while (i < 10; i += 1) {
76 assert(list.items[i] == i32(i + 1));81 assert(list.items[i] == i32(i + 1));
77 }}82 }}
83
84 assert(list.pop() == 10);
85 assert(list.len == 9);
78}86}
std/mem.zig+2
...@@ -9,6 +9,8 @@ error NoMem;...@@ -9,6 +9,8 @@ error NoMem;
99
10pub const Allocator = struct {10pub const Allocator = struct {
11 allocFn: fn (self: &Allocator, n: usize) -> %[]u8,11 allocFn: fn (self: &Allocator, n: usize) -> %[]u8,
12 /// Note that old_mem may be a slice of length 0, in which case reallocFn
13 /// should simply call allocFn
12 reallocFn: fn (self: &Allocator, old_mem: []u8, new_size: usize) -> %[]u8,14 reallocFn: fn (self: &Allocator, old_mem: []u8, new_size: usize) -> %[]u8,
13 freeFn: fn (self: &Allocator, mem: []u8),15 freeFn: fn (self: &Allocator, mem: []u8),
1416
std/os/index.zig+178
...@@ -17,6 +17,8 @@ pub const line_sep = switch (@compileVar("os")) {...@@ -17,6 +17,8 @@ pub const line_sep = switch (@compileVar("os")) {
17 else => "\n",17 else => "\n",
18};18};
1919
20pub const page_size = 4 * 1024;
21
20const debug = @import("../debug.zig");22const debug = @import("../debug.zig");
21const assert = debug.assert;23const assert = debug.assert;
2224
...@@ -32,6 +34,7 @@ const cstr = @import("../cstr.zig");...@@ -32,6 +34,7 @@ const cstr = @import("../cstr.zig");
3234
33const io = @import("../io.zig");35const io = @import("../io.zig");
34const base64 = @import("../base64.zig");36const base64 = @import("../base64.zig");
37const List = @import("../list.zig").List;
3538
36error Unexpected;39error Unexpected;
37error SystemResources;40error SystemResources;
...@@ -46,6 +49,7 @@ error SymLinkLoop;...@@ -46,6 +49,7 @@ error SymLinkLoop;
46error ReadOnlyFileSystem;49error ReadOnlyFileSystem;
47error LinkQuotaExceeded;50error LinkQuotaExceeded;
48error RenameAcrossMountPoints;51error RenameAcrossMountPoints;
52error DirNotEmpty;
4953
50/// Fills `buf` with random bytes. If linking against libc, this calls the54/// Fills `buf` with random bytes. If linking against libc, this calls the
51/// appropriate OS-specific library call. Otherwise it uses the zig standard55/// appropriate OS-specific library call. Otherwise it uses the zig standard
...@@ -585,3 +589,177 @@ pub fn makePath(allocator: &Allocator, full_path: []const u8) -> %void {...@@ -585,3 +589,177 @@ pub fn makePath(allocator: &Allocator, full_path: []const u8) -> %void {
585 // TODO stat the file and return an error if it's not a directory589 // TODO stat the file and return an error if it's not a directory
586 };590 };
587}591}
592
593/// Returns ::error.DirNotEmpty if the directory is not empty.
594/// To delete a directory recursively, see ::deleteTree
595pub fn deleteDir(allocator: &Allocator, dir_path: []const u8) -> %void {
596 const path_buf = %return allocator.alloc(u8, dir_path.len + 1);
597 defer allocator.free(path_buf);
598
599 mem.copy(u8, path_buf, dir_path);
600 path_buf[dir_path.len] = 0;
601
602 const err = posix.getErrno(posix.rmdir(path_buf.ptr));
603 if (err > 0) {
604 return switch (err) {
605 errno.EACCES, errno.EPERM => error.AccessDenied,
606 errno.EBUSY => error.FileBusy,
607 errno.EFAULT, errno.EINVAL => unreachable,
608 errno.ELOOP => error.SymLinkLoop,
609 errno.ENAMETOOLONG => error.NameTooLong,
610 errno.ENOENT => error.FileNotFound,
611 errno.ENOMEM => error.SystemResources,
612 errno.ENOTDIR => error.NotDir,
613 errno.EEXIST, errno.ENOTEMPTY => error.DirNotEmpty,
614 errno.EROFS => error.ReadOnlyFileSystem,
615 else => error.Unexpected,
616 };
617 }
618}
619
620/// Whether ::full_path describes a symlink, file, or directory, this function
621/// removes it. If it cannot be removed because it is a non-empty directory,
622/// this function recursively removes its entries and then tries again.
623// TODO non-recursive implementation
624pub fn deleteTree(allocator: &Allocator, full_path: []const u8) -> %void {
625start_over:
626 // First, try deleting the item as a file. This way we don't follow sym links.
627 try (deleteFile(allocator, full_path)) {
628 return;
629 } else |err| {
630 if (err == error.FileNotFound)
631 return;
632 if (err != error.IsDir)
633 return err;
634 }
635 {
636 var dir = Dir.open(allocator, full_path) %% |err| {
637 if (err == error.FileNotFound)
638 return;
639 if (err == error.NotDir)
640 goto start_over;
641 return err;
642 };
643 defer dir.close();
644
645 var full_entry_buf = List(u8).init(allocator);
646 defer full_entry_buf.deinit();
647
648 while (true) {
649 const entry = (%return dir.next()) ?? break;
650
651 %return full_entry_buf.resize(full_path.len + entry.name.len + 1);
652 const full_entry_path = full_entry_buf.toSlice();
653 mem.copy(u8, full_entry_path, full_path);
654 full_entry_path[full_path.len] = '/';
655 mem.copy(u8, full_entry_path[full_path.len + 1...], entry.name);
656
657 %return deleteTree(allocator, full_entry_path);
658 }
659 }
660 return deleteDir(allocator, full_path);
661}
662
663pub const Dir = struct {
664 fd: i32,
665 allocator: &Allocator,
666 buf: []u8,
667 index: usize,
668 end_index: usize,
669
670 const LinuxEntry = extern struct {
671 d_ino: usize,
672 d_off: usize,
673 d_reclen: u16,
674 d_name: u8, // field address is the address of first byte of name
675 };
676
677 pub const Entry = struct {
678 name: []const u8,
679 kind: Kind,
680
681 pub const Kind = enum {
682 BlockDevice,
683 CharacterDevice,
684 Directory,
685 NamedPipe,
686 SymLink,
687 File,
688 UnixDomainSocket,
689 Unknown,
690 };
691 };
692
693 pub fn open(allocator: &Allocator, dir_path: []const u8) -> %Dir {
694 const fd = %return posixOpen(dir_path, posix.O_RDONLY|posix.O_DIRECTORY|posix.O_CLOEXEC, 0, allocator);
695 return Dir {
696 .allocator = allocator,
697 .fd = fd,
698 .index = 0,
699 .end_index = 0,
700 .buf = []u8{},
701 };
702 }
703
704 pub fn close(self: &Dir) {
705 self.allocator.free(self.buf);
706 posixClose(self.fd);
707 }
708
709 /// Memory such as file names referenced in this returned entry becomes invalid
710 /// with subsequent calls to next, as well as when this ::Dir is deinitialized.
711 pub fn next(self: &Dir) -> %?Entry {
712 start_over:
713 if (self.index >= self.end_index) {
714 if (self.buf.len == 0) {
715 self.buf = %return self.allocator.alloc(u8, 2); //page_size);
716 }
717
718 while (true) {
719 const result = posix.getdents(self.fd, self.buf.ptr, self.buf.len);
720 const err = linux.getErrno(result);
721 if (err > 0) {
722 switch (err) {
723 errno.EBADF, errno.EFAULT, errno.ENOTDIR => unreachable,
724 errno.EINVAL => {
725 self.buf = %return self.allocator.realloc(u8, self.buf, self.buf.len * 2);
726 continue;
727 },
728 else => return error.Unexpected,
729 };
730 }
731 if (result == 0)
732 return null;
733 self.index = 0;
734 self.end_index = result;
735 break;
736 }
737 }
738 const linux_entry = @ptrcast(&LinuxEntry, &self.buf[self.index]);
739 const next_index = self.index + linux_entry.d_reclen;
740 self.index = next_index;
741
742 const name = (&linux_entry.d_name)[0...cstr.len(&linux_entry.d_name)];
743
744 // skip . and .. entries
745 if (mem.eql(u8, name, ".") or mem.eql(u8, name, "..")) {
746 goto start_over;
747 }
748
749 const type_char = self.buf[next_index - 1];
750 const entry_kind = switch (type_char) {
751 posix.DT_BLK => Entry.Kind.BlockDevice,
752 posix.DT_CHR => Entry.Kind.CharacterDevice,
753 posix.DT_DIR => Entry.Kind.Directory,
754 posix.DT_FIFO => Entry.Kind.NamedPipe,
755 posix.DT_LNK => Entry.Kind.SymLink,
756 posix.DT_REG => Entry.Kind.File,
757 posix.DT_SOCK => Entry.Kind.UnixDomainSocket,
758 else => Entry.Kind.Unknown,
759 };
760 return Entry {
761 .name = name,
762 .kind = entry_kind,
763 };
764 }
765};
std/os/linux.zig+17
...@@ -241,6 +241,15 @@ pub const AF_NFC = PF_NFC;...@@ -241,6 +241,15 @@ pub const AF_NFC = PF_NFC;
241pub const AF_VSOCK = PF_VSOCK;241pub const AF_VSOCK = PF_VSOCK;
242pub const AF_MAX = PF_MAX;242pub const AF_MAX = PF_MAX;
243243
244pub const DT_UNKNOWN = 0;
245pub const DT_FIFO = 1;
246pub const DT_CHR = 2;
247pub const DT_DIR = 4;
248pub const DT_BLK = 6;
249pub const DT_REG = 8;
250pub const DT_LNK = 10;
251pub const DT_SOCK = 12;
252pub const DT_WHT = 14;
244253
245fn unsigned(s: i32) -> u32 { *@ptrcast(&u32, &s) }254fn unsigned(s: i32) -> u32 { *@ptrcast(&u32, &s) }
246fn signed(s: u32) -> i32 { *@ptrcast(&i32, &s) }255fn signed(s: u32) -> i32 { *@ptrcast(&i32, &s) }
...@@ -273,6 +282,10 @@ pub fn getcwd(buf: &u8, size: usize) -> usize {...@@ -273,6 +282,10 @@ pub fn getcwd(buf: &u8, size: usize) -> usize {
273 arch.syscall2(arch.SYS_getcwd, usize(buf), size)282 arch.syscall2(arch.SYS_getcwd, usize(buf), size)
274}283}
275284
285pub fn getdents(fd: i32, dirp: &u8, count: usize) -> usize {
286 arch.syscall3(arch.SYS_getdents, usize(fd), usize(dirp), usize(count))
287}
288
276pub fn mkdir(path: &const u8, mode: usize) -> usize {289pub fn mkdir(path: &const u8, mode: usize) -> usize {
277 arch.syscall2(arch.SYS_mkdir, usize(path), mode)290 arch.syscall2(arch.SYS_mkdir, usize(path), mode)
278}291}
...@@ -291,6 +304,10 @@ pub fn read(fd: i32, buf: &u8, count: usize) -> usize {...@@ -291,6 +304,10 @@ pub fn read(fd: i32, buf: &u8, count: usize) -> usize {
291 arch.syscall3(arch.SYS_read, usize(fd), usize(buf), count)304 arch.syscall3(arch.SYS_read, usize(fd), usize(buf), count)
292}305}
293306
307pub fn rmdir(path: &const u8) -> usize {
308 arch.syscall1(arch.SYS_rmdir, usize(path))
309}
310
294pub fn symlink(existing: &const u8, new: &const u8) -> usize {311pub fn symlink(existing: &const u8, new: &const u8) -> usize {
295 arch.syscall2(arch.SYS_symlink, usize(existing), usize(new))312 arch.syscall2(arch.SYS_symlink, usize(existing), usize(new))
296}313}
test/tests.zig+21
...@@ -103,6 +103,27 @@ pub fn addParseHTests(b: &build.Builder, test_filter: ?[]const u8) -> &build.Ste...@@ -103,6 +103,27 @@ pub fn addParseHTests(b: &build.Builder, test_filter: ?[]const u8) -> &build.Ste
103 return cases.step;103 return cases.step;
104}104}
105105
106pub fn addPkgTests(b: &build.Builder, test_filter: ?[]const u8, root_src: []const u8,
107 name:[] const u8, desc: []const u8) -> &build.Step
108{
109 const step = b.step(b.fmt("test-{}", name), desc);
110 for ([]bool{false, true}) |release| {
111 for ([]bool{false, true}) |link_libc| {
112 const these_tests = b.addTest(root_src);
113 these_tests.setNamePrefix(b.fmt("{}-{}-{} ", name,
114 if (release) "release" else "debug",
115 if (link_libc) "c" else "bare"));
116 these_tests.setFilter(test_filter);
117 these_tests.setRelease(release);
118 if (link_libc) {
119 these_tests.linkLibrary("c");
120 }
121 step.dependOn(&these_tests.step);
122 }
123 }
124 return step;
125}
126
106pub const CompareOutputContext = struct {127pub const CompareOutputContext = struct {
107 b: &build.Builder,128 b: &build.Builder,
108 step: &build.Step,129 step: &build.Step,