| author | |
| committer | |
| log | 8654bc18104d64c7a7f9f80bdba75ed4e0c005fa |
| tree | 398f7b9d18988b61ad12db21aa72e21da3aec4d4 |
| parent | 1ff73a8e69a09b3bc993cffb755cc2ca98c7040b |
* 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 tests9 files changed, 281 insertions(+), 50 deletions(-)
README.md+3-3| ... | ... | @@ -45,8 +45,8 @@ compromises backward compatibility. |
| 45 | 45 | * Release mode produces heavily optimized code. What other projects call |
| 46 | 46 | "Link Time Optimization" Zig does automatically. |
| 47 | 47 | * Mark functions as tests and automatically run them with `zig test`. |
| 48 | * Currently supported architectures: `x86_64`, `i386` | |
| 49 | * Currently supported operating systems: linux, macosx | |
| 48 | * Currently supported architectures: `x86_64` | |
| 49 | * Currently supported operating systems: linux | |
| 50 | 50 | * Friendly toward package maintainers. Reproducible build, bootstrapping |
| 51 | 51 | process carefully documented. Issues filed by package maintainers are |
| 52 | 52 | considered especially important. |
| ... | ... | @@ -103,7 +103,7 @@ cd build |
| 103 | 103 | cmake .. -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)) |
| 104 | 104 | make |
| 105 | 105 | make install |
| 106 | ./run_tests | |
| 106 | ./zig build --build-file ../build.zig test | |
| 107 | 107 | ``` |
| 108 | 108 | |
| 109 | 109 | ### Release / Install Build |
build.zig+13-38| ... | ... | @@ -5,44 +5,19 @@ pub fn build(b: &Builder) { |
| 5 | 5 | const test_filter = b.option([]const u8, "test-filter", "Skip tests that do not match filter"); |
| 6 | 6 | const test_step = b.step("test", "Run all the tests"); |
| 7 | 7 | |
| 8 | const behavior_tests = b.step("test-behavior", "Run the behavior tests"); | |
| 9 | test_step.dependOn(behavior_tests); | |
| 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 | } | |
| 8 | const cleanup = b.addRemoveDirTree("test_artifacts"); | |
| 9 | test_step.dependOn(&cleanup.step); | |
| 24 | 10 | |
| 25 | const std_lib_tests = b.step("test-std", "Run the standard library tests"); | |
| 26 | test_step.dependOn(std_lib_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 | } | |
| 11 | cleanup.step.dependOn(tests.addPkgTests(b, test_filter, | |
| 12 | "test/behavior.zig", "behavior", "Run the behavior tests")); | |
| 41 | 13 | |
| 42 | test_step.dependOn(tests.addCompareOutputTests(b, test_filter)); | |
| 43 | test_step.dependOn(tests.addBuildExampleTests(b, test_filter)); | |
| 44 | test_step.dependOn(tests.addCompileErrorTests(b, test_filter)); | |
| 45 | test_step.dependOn(tests.addAssembleAndLinkTests(b, test_filter)); | |
| 46 | test_step.dependOn(tests.addDebugSafetyTests(b, test_filter)); | |
| 47 | test_step.dependOn(tests.addParseHTests(b, test_filter)); | |
| 14 | cleanup.step.dependOn(tests.addPkgTests(b, test_filter, | |
| 15 | "std/index.zig", "std", "Run the standard library tests")); | |
| 16 | ||
| 17 | cleanup.step.dependOn(tests.addCompareOutputTests(b, test_filter)); | |
| 18 | cleanup.step.dependOn(tests.addBuildExampleTests(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 | 195 | return log_step; |
| 196 | 196 | } |
| 197 | 197 | |
| 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 | 204 | pub fn version(self: &const Builder, major: u32, minor: u32, patch: u32) -> Version { |
| 199 | 205 | Version { |
| 200 | 206 | .major = major, |
| ... | ... | @@ -1548,10 +1554,12 @@ pub const WriteFileStep = struct { |
| 1548 | 1554 | const full_path = self.builder.pathFromRoot(self.file_path); |
| 1549 | 1555 | const full_path_dir = %%os.path.dirname(self.builder.allocator, full_path); |
| 1550 | 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 | 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 | 1584 | } |
| 1577 | 1585 | }; |
| 1578 | 1586 | |
| 1587 | pub 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 | ||
| 1579 | 1611 | pub const Step = struct { |
| 1580 | 1612 | name: []const u8, |
| 1581 | 1613 | makeFn: fn(self: &Step) -> %void, |
std/io.zig+4-6| ... | ... | @@ -57,8 +57,6 @@ error NoMem; |
| 57 | 57 | error Unseekable; |
| 58 | 58 | error Eof; |
| 59 | 59 | |
| 60 | const buffer_size = 4 * 1024; | |
| 61 | ||
| 62 | 60 | pub const OpenRead = 0b0001; |
| 63 | 61 | pub const OpenWrite = 0b0010; |
| 64 | 62 | pub const OpenCreate = 0b0100; |
| ... | ... | @@ -66,7 +64,7 @@ pub const OpenTruncate = 0b1000; |
| 66 | 64 | |
| 67 | 65 | pub const OutStream = struct { |
| 68 | 66 | fd: i32, |
| 69 | buffer: [buffer_size]u8, | |
| 67 | buffer: [os.page_size]u8, | |
| 70 | 68 | index: usize, |
| 71 | 69 | |
| 72 | 70 | /// `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 | 95 | } |
| 98 | 96 | |
| 99 | 97 | pub fn write(self: &OutStream, bytes: []const u8) -> %void { |
| 100 | if (bytes.len >= buffer_size) { | |
| 98 | if (bytes.len >= self.buffer.len) { | |
| 101 | 99 | %return self.flush(); |
| 102 | 100 | return os.posixWrite(self.fd, bytes); |
| 103 | 101 | } |
| ... | ... | @@ -329,7 +327,7 @@ pub const InStream = struct { |
| 329 | 327 | } |
| 330 | 328 | |
| 331 | 329 | pub fn readAll(is: &InStream, buf: &Buffer0) -> %void { |
| 332 | %return buf.resize(buffer_size); | |
| 330 | %return buf.resize(os.page_size); | |
| 333 | 331 | |
| 334 | 332 | var actual_buf_len: usize = 0; |
| 335 | 333 | while (true) { |
| ... | ... | @@ -341,7 +339,7 @@ pub const InStream = struct { |
| 341 | 339 | return buf.resize(actual_buf_len); |
| 342 | 340 | } |
| 343 | 341 | |
| 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 | 61 | l.len = new_length; |
| 62 | 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 | } |
| 66 | 71 | |
| 67 | test "basicListTest" { | |
| 72 | test "basic list test" { | |
| 68 | 73 | var list = List(i32).init(&debug.global_allocator); |
| 69 | 74 | defer list.deinit(); |
| 70 | 75 | |
| ... | ... | @@ -75,4 +80,7 @@ test "basicListTest" { |
| 75 | 80 | {var i: usize = 0; while (i < 10; i += 1) { |
| 76 | 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 | 9 | |
| 10 | 10 | pub const Allocator = struct { |
| 11 | 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 | 14 | reallocFn: fn (self: &Allocator, old_mem: []u8, new_size: usize) -> %[]u8, |
| 13 | 15 | freeFn: fn (self: &Allocator, mem: []u8), |
| 14 | 16 |
std/os/index.zig+178| ... | ... | @@ -17,6 +17,8 @@ pub const line_sep = switch (@compileVar("os")) { |
| 17 | 17 | else => "\n", |
| 18 | 18 | }; |
| 19 | 19 | |
| 20 | pub const page_size = 4 * 1024; | |
| 21 | ||
| 20 | 22 | const debug = @import("../debug.zig"); |
| 21 | 23 | const assert = debug.assert; |
| 22 | 24 | |
| ... | ... | @@ -32,6 +34,7 @@ const cstr = @import("../cstr.zig"); |
| 32 | 34 | |
| 33 | 35 | const io = @import("../io.zig"); |
| 34 | 36 | const base64 = @import("../base64.zig"); |
| 37 | const List = @import("../list.zig").List; | |
| 35 | 38 | |
| 36 | 39 | error Unexpected; |
| 37 | 40 | error SystemResources; |
| ... | ... | @@ -46,6 +49,7 @@ error SymLinkLoop; |
| 46 | 49 | error ReadOnlyFileSystem; |
| 47 | 50 | error LinkQuotaExceeded; |
| 48 | 51 | error RenameAcrossMountPoints; |
| 52 | error DirNotEmpty; | |
| 49 | 53 | |
| 50 | 54 | /// Fills `buf` with random bytes. If linking against libc, this calls the |
| 51 | 55 | /// 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 | 589 | // 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 | |
| 595 | pub 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 | |
| 624 | pub fn deleteTree(allocator: &Allocator, full_path: []const u8) -> %void { | |
| 625 | start_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 | ||
| 663 | pub 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 | 241 | pub const AF_VSOCK = PF_VSOCK; |
| 242 | 242 | pub const AF_MAX = PF_MAX; |
| 243 | 243 | |
| 244 | pub const DT_UNKNOWN = 0; | |
| 245 | pub const DT_FIFO = 1; | |
| 246 | pub const DT_CHR = 2; | |
| 247 | pub const DT_DIR = 4; | |
| 248 | pub const DT_BLK = 6; | |
| 249 | pub const DT_REG = 8; | |
| 250 | pub const DT_LNK = 10; | |
| 251 | pub const DT_SOCK = 12; | |
| 252 | pub const DT_WHT = 14; | |
| 244 | 253 | |
| 245 | 254 | fn unsigned(s: i32) -> u32 { *@ptrcast(&u32, &s) } |
| 246 | 255 | fn signed(s: u32) -> i32 { *@ptrcast(&i32, &s) } |
| ... | ... | @@ -273,6 +282,10 @@ pub fn getcwd(buf: &u8, size: usize) -> usize { |
| 273 | 282 | arch.syscall2(arch.SYS_getcwd, usize(buf), size) |
| 274 | 283 | } |
| 275 | 284 | |
| 285 | pub fn getdents(fd: i32, dirp: &u8, count: usize) -> usize { | |
| 286 | arch.syscall3(arch.SYS_getdents, usize(fd), usize(dirp), usize(count)) | |
| 287 | } | |
| 288 | ||
| 276 | 289 | pub fn mkdir(path: &const u8, mode: usize) -> usize { |
| 277 | 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 | 304 | arch.syscall3(arch.SYS_read, usize(fd), usize(buf), count) |
| 292 | 305 | } |
| 293 | 306 | |
| 307 | pub fn rmdir(path: &const u8) -> usize { | |
| 308 | arch.syscall1(arch.SYS_rmdir, usize(path)) | |
| 309 | } | |
| 310 | ||
| 294 | 311 | pub fn symlink(existing: &const u8, new: &const u8) -> usize { |
| 295 | 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 | 103 | return cases.step; |
| 104 | 104 | } |
| 105 | 105 | |
| 106 | pub 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 | ||
| 106 | 127 | pub const CompareOutputContext = struct { |
| 107 | 128 | b: &build.Builder, |
| 108 | 129 | step: &build.Step, |