authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2017-04-21 01:56:12-04:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2017-04-21 01:56:12-04:00
logfb492d19ebb56466f04a2a88c7d3c0a9833f2e0d
tree2d87121db6510c014c34d9a8f7071eb1805ba52a
parent599215cee46b83d052f57a3e6566098900332e38

zig build system supports building a library

See #329 Supporting work: * move std.cstr.Buffer0 to std.buffer.Buffer * add build.zig to example/shared_library/ and add an automated test for it * add std.list.List.resizeDown * improve std.os.makePath - no longer recursive - takes into account . and .. * add std.os.path.isAbsolute * add std.os.path.resolve * reimplement std.os.path.dirname - no longer requires an allocator - handles edge cases correctly

17 files changed, 641 insertions(+), 245 deletions(-)

CMakeLists.txt+1
......@@ -199,6 +199,7 @@ install(FILES ${C_HEADERS} DESTINATION ${C_HEADERS_DEST})
199199install(FILES "${CMAKE_SOURCE_DIR}/std/base64.zig" DESTINATION "${ZIG_STD_DEST}")
200200install(FILES "${CMAKE_SOURCE_DIR}/std/buf_map.zig" DESTINATION "${ZIG_STD_DEST}")
201201install(FILES "${CMAKE_SOURCE_DIR}/std/buf_set.zig" DESTINATION "${ZIG_STD_DEST}")
202install(FILES "${CMAKE_SOURCE_DIR}/std/buffer.zig" DESTINATION "${ZIG_STD_DEST}")
202203install(FILES "${CMAKE_SOURCE_DIR}/std/build.zig" DESTINATION "${ZIG_STD_DEST}")
203204install(FILES "${CMAKE_SOURCE_DIR}/std/c/darwin.zig" DESTINATION "${ZIG_STD_DEST}/c")
204205install(FILES "${CMAKE_SOURCE_DIR}/std/c/index.zig" DESTINATION "${ZIG_STD_DEST}/c")
example/shared_library/build.zig created+20
......@@ -0,0 +1,20 @@
1const Builder = @import("std").build.Builder;
2
3pub fn build(b: &Builder) {
4 const lib = b.addSharedLibrary("mathtest", "mathtest.zig", b.version(1, 0, 0));
5
6 const exe = b.addCExecutable("test");
7 exe.addCompileFlags([][]const u8 {
8 "-std=c99",
9 });
10 exe.addSourceFile("test.c");
11 exe.linkLibrary(lib);
12
13 b.default_step.dependOn(&exe.step);
14
15 const run_cmd = b.addCommand(b.out_dir, b.env_map, "./test", [][]const u8{});
16 run_cmd.step.dependOn(&exe.step);
17
18 const test_step = b.step("test", "Test the program");
19 test_step.dependOn(&run_cmd.step);
20}
example/shared_library/mathtest.zig-4
......@@ -1,7 +1,3 @@
11export fn add(a: i32, b: i32) -> i32 {
22 a + b
33}
4
5export fn hang() -> unreachable {
6 while (true) { }
7}
example/shared_library/test.c+2-2
......@@ -1,7 +1,7 @@
11#include "mathtest.h"
2#include <stdio.h>
2#include <assert.h>
33
44int main(int argc, char **argv) {
5 printf("%d\n", add(42, 1137));
5 assert(add(42, 1337) == 1379);
66 return 0;
77}
src/codegen.cpp+6
......@@ -155,6 +155,12 @@ void codegen_set_test_name_prefix(CodeGen *g, Buf *prefix) {
155155 g->test_name_prefix = prefix;
156156}
157157
158void codegen_set_lib_version(CodeGen *g, size_t major, size_t minor, size_t patch) {
159 g->version_major = major;
160 g->version_minor = minor;
161 g->version_patch = patch;
162}
163
158164void codegen_set_is_test(CodeGen *g, bool is_test_build) {
159165 g->is_test_build = is_test_build;
160166}
src/codegen.hpp+1
......@@ -46,6 +46,7 @@ void codegen_set_linker_script(CodeGen *g, const char *linker_script);
4646void codegen_set_omit_zigrt(CodeGen *g, bool omit_zigrt);
4747void codegen_set_test_filter(CodeGen *g, Buf *filter);
4848void codegen_set_test_name_prefix(CodeGen *g, Buf *prefix);
49void codegen_set_lib_version(CodeGen *g, size_t major, size_t minor, size_t patch);
4950
5051PackageTableEntry *new_package(const char *root_src_dir, const char *root_src_path);
5152void codegen_add_root_code(CodeGen *g, Buf *source_dir, Buf *source_basename, Buf *source_code);
src/main.cpp+14
......@@ -67,6 +67,10 @@ static int usage(const char *arg0) {
6767 "Test Options:\n"
6868 " --test-filter [text] skip tests that do not match filter\n"
6969 " --test-name-prefix [text] add prefix to all tests\n"
70 "Dynamic Library Options:\n"
71 " --ver-major [ver] semver major version\n"
72 " --ver-minor [ver] semver minor version\n"
73 " --ver-patch [ver] semver patch version\n"
7074 , arg0);
7175 return EXIT_FAILURE;
7276}
......@@ -156,6 +160,9 @@ int main(int argc, char **argv) {
156160 ZigList<const char *> objects = {0};
157161 const char *test_filter = nullptr;
158162 const char *test_name_prefix = nullptr;
163 size_t ver_major = 0;
164 size_t ver_minor = 0;
165 size_t ver_patch = 0;
159166
160167 if (argc >= 2 && strcmp(argv[1], "build") == 0) {
161168 const char *zig_exe_path = arg0;
......@@ -350,6 +357,12 @@ int main(int argc, char **argv) {
350357 test_filter = argv[i];
351358 } else if (strcmp(arg, "--test-name-prefix") == 0) {
352359 test_name_prefix = argv[i];
360 } else if (strcmp(arg, "--ver-major") == 0) {
361 ver_major = atoi(argv[i]);
362 } else if (strcmp(arg, "--ver-minor") == 0) {
363 ver_minor = atoi(argv[i]);
364 } else if (strcmp(arg, "--ver-patch") == 0) {
365 ver_patch = atoi(argv[i]);
353366 } else {
354367 fprintf(stderr, "Invalid argument: %s\n", arg);
355368 return usage(arg0);
......@@ -509,6 +522,7 @@ int main(int argc, char **argv) {
509522 }
510523
511524 CodeGen *g = codegen_create(&root_source_dir, target);
525 codegen_set_lib_version(g, ver_major, ver_minor, ver_patch);
512526 codegen_set_is_release(g, is_release_build);
513527 codegen_set_is_test(g, cmd == CmdTest);
514528 codegen_set_linker_script(g, linker_script);
std/buffer.zig created+117
......@@ -0,0 +1,117 @@
1const debug = @import("debug.zig");
2const mem = @import("mem.zig");
3const Allocator = mem.Allocator;
4const assert = debug.assert;
5const List = @import("list.zig").List;
6
7/// A buffer that allocates memory and maintains a null byte at the end.
8pub const Buffer = struct {
9 list: List(u8),
10
11 /// Must deinitialize with deinit.
12 pub fn init(allocator: &Allocator, m: []const u8) -> %Buffer {
13 var self = %return initSize(allocator, m.len);
14 mem.copy(u8, self.list.items, m);
15 return self;
16 }
17
18 /// Must deinitialize with deinit.
19 pub fn initSize(allocator: &Allocator, size: usize) -> %Buffer {
20 var self = initNull(allocator);
21 %return self.resize(size);
22 return self;
23 }
24
25 /// Must deinitialize with deinit.
26 /// None of the other operations are valid until you do one of these:
27 /// * ::replaceContents
28 /// * ::replaceContentsBuffer
29 /// * ::resize
30 pub fn initNull(allocator: &Allocator) -> Buffer {
31 Buffer {
32 .list = List(u8).init(allocator),
33 }
34 }
35
36 /// Must deinitialize with deinit.
37 pub fn initFromBuffer(buffer: &const Buffer) -> %Buffer {
38 return Buffer.init(buffer.list.allocator, buffer.toSliceConst());
39 }
40
41 pub fn deinit(self: &Buffer) {
42 self.list.deinit();
43 }
44
45 pub fn toSlice(self: &Buffer) -> []u8 {
46 return self.list.toSlice()[0...self.len()];
47 }
48
49 pub fn toSliceConst(self: &const Buffer) -> []const u8 {
50 return self.list.toSliceConst()[0...self.len()];
51 }
52
53 pub fn resize(self: &Buffer, new_len: usize) -> %void {
54 %return self.list.resize(new_len + 1);
55 self.list.items[self.len()] = 0;
56 }
57
58 pub fn isNull(self: &const Buffer) -> bool {
59 return self.list.len == 0;
60 }
61
62 pub fn len(self: &const Buffer) -> usize {
63 return self.list.len - 1;
64 }
65
66 pub fn append(self: &Buffer, m: []const u8) -> %void {
67 const old_len = self.len();
68 %return self.resize(old_len + m.len);
69 mem.copy(u8, self.list.toSlice()[old_len...], m);
70 }
71
72 pub fn appendByte(self: &Buffer, byte: u8) -> %void {
73 %return self.resize(self.len() + 1);
74 self.list.items[self.len() - 1] = byte;
75 }
76
77 pub fn eql(self: &const Buffer, m: []const u8) -> bool {
78 mem.eql(u8, self.toSliceConst(), m)
79 }
80
81 pub fn startsWith(self: &const Buffer, m: []const u8) -> bool {
82 if (self.len() < m.len) return false;
83 return mem.eql(u8, self.list.items[0...m.len], m);
84 }
85
86 pub fn endsWith(self: &const Buffer, m: []const u8) -> bool {
87 const l = self.len();
88 if (l < m.len) return false;
89 const start = l - m.len;
90 return mem.eql(u8, self.list.items[start...], m);
91 }
92
93 pub fn replaceContents(self: &const Buffer, m: []const u8) -> %void {
94 %return self.resize(m.len);
95 mem.copy(u8, self.list.toSlice(), m);
96 }
97};
98
99test "simple Buffer" {
100 const cstr = @import("cstr.zig");
101
102 var buf = %%Buffer.init(&debug.global_allocator, "");
103 assert(buf.len() == 0);
104 %%buf.append("hello");
105 %%buf.appendByte(' ');
106 %%buf.append("world");
107 assert(buf.eql("hello world"));
108 assert(mem.eql(u8, cstr.toSliceConst(buf.toSliceConst().ptr), buf.toSliceConst()));
109
110 var buf2 = %%Buffer.initFromBuffer(&buf);
111 assert(buf.eql(buf2.toSliceConst()));
112
113 assert(buf.startsWith("hell"));
114
115 %%buf2.resize(4);
116 assert(buf.startsWith(buf2.toSliceConst()));
117}
std/build.zig+263-52
......@@ -120,12 +120,30 @@ pub const Builder = struct {
120120 self.lib_dir = %%os.path.join(self.allocator, self.prefix, "lib");
121121 }
122122
123 pub fn addExecutable(self: &Builder, name: []const u8, root_src: []const u8) -> &Exe {
124 const exe = %%self.allocator.create(Exe);
125 *exe = Exe.init(self, name, root_src);
123 pub fn addExecutable(self: &Builder, name: []const u8, root_src: []const u8) -> &LibOrExeStep {
124 const exe = %%self.allocator.create(LibOrExeStep);
125 *exe = LibOrExeStep.initExecutable(self, name, root_src);
126126 return exe;
127127 }
128128
129 pub fn addObject(self: &Builder, name: []const u8, root_src: []const u8) -> &ObjectStep {
130 const obj_step = %%self.allocator.create(ObjectStep);
131 *obj_step = ObjectStep.init(self, name, src);
132 return obj_step;
133 }
134
135 pub fn addSharedLibrary(self: &Builder, name: []const u8, root_src: []const u8, ver: &const Version) -> &LibOrExeStep {
136 const lib_step = %%self.allocator.create(LibOrExeStep);
137 *lib_step = LibOrExeStep.initSharedLibrary(self, name, root_src, ver);
138 return lib_step;
139 }
140
141 pub fn addStaticLibrary(self: &Builder, name: []const u8, root_src: []const u8) -> &LibOrExeStep {
142 const lib_step = %%self.allocator.create(LibOrExeStep);
143 *lib_step = LibOrExeStep.initStaticLibrary(self, name, root_src);
144 return lib_step;
145 }
146
129147 pub fn addTest(self: &Builder, root_src: []const u8) -> &TestStep {
130148 const test_step = %%self.allocator.create(TestStep);
131149 *test_step = TestStep.init(self, root_src);
......@@ -487,11 +505,13 @@ pub const Builder = struct {
487505 return self.invalid_user_input;
488506 }
489507
490 fn spawnChild(self: &Builder, exe_path: []const u8, args: []const []const u8) {
508 fn spawnChild(self: &Builder, exe_path: []const u8, args: []const []const u8) -> %void {
491509 return self.spawnChildEnvMap(&self.env_map, exe_path, args);
492510 }
493511
494 fn spawnChildEnvMap(self: &Builder, env_map: &const BufMap, exe_path: []const u8, args: []const []const u8) {
512 fn spawnChildEnvMap(self: &Builder, env_map: &const BufMap, exe_path: []const u8,
513 args: []const []const u8) -> %void
514 {
495515 if (self.verbose) {
496516 %%io.stderr.printf("{}", exe_path);
497517 for (args) |arg| {
......@@ -501,18 +521,26 @@ pub const Builder = struct {
501521 }
502522
503523 var child = os.ChildProcess.spawn(exe_path, args, env_map,
504 StdIo.Ignore, StdIo.Inherit, StdIo.Inherit, self.allocator)
505 %% |err| debug.panic("Unable to spawn {}: {}\n", exe_path, @errorName(err));
524 StdIo.Ignore, StdIo.Inherit, StdIo.Inherit, self.allocator) %% |err|
525 {
526 %%io.stderr.printf("Unable to spawn {}: {}\n", exe_path, @errorName(err));
527 return err;
528 };
506529
507 const term = %%child.wait();
530 const term = child.wait() %% |err| {
531 %%io.stderr.printf("Unable to spawn {}: {}\n", exe_path, @errorName(err));
532 return err;
533 };
508534 switch (term) {
509535 Term.Clean => |code| {
510536 if (code != 0) {
511 debug.panic("Process {} exited with error code {}\n", exe_path, code);
537 %%io.stderr.printf("Process {} exited with error code {}\n", exe_path, code);
538 return error.UncleanExit;
512539 }
513540 },
514541 else => {
515 debug.panic("Process {} terminated unexpectedly\n", exe_path);
542 %%io.stderr.printf("Process {} terminated unexpectedly\n", exe_path);
543 return error.UncleanExit;
516544 },
517545 };
518546
......@@ -547,7 +575,7 @@ pub const Builder = struct {
547575 }
548576
549577 fn pathFromRoot(self: &Builder, rel_path: []const u8) -> []u8 {
550 return %%os.path.join(self.allocator, self.build_root, rel_path);
578 return %%os.path.resolve(self.allocator, self.build_root, rel_path);
551579 }
552580
553581 pub fn fmt(self: &Builder, comptime format: []const u8, args: ...) -> []u8 {
......@@ -600,7 +628,7 @@ const LinkerScript = enum {
600628 Path: []const u8,
601629};
602630
603pub const Exe = struct {
631pub const LibOrExeStep = struct {
604632 step: Step,
605633 builder: &Builder,
606634 root_src: []const u8,
......@@ -610,13 +638,42 @@ pub const Exe = struct {
610638 link_libs: BufSet,
611639 verbose: bool,
612640 release: bool,
641 static: bool,
613642 output_path: ?[]const u8,
643 kind: Kind,
644 version: Version,
645 out_filename: []const u8,
646 out_filename_major_only: []const u8,
647 out_filename_name_only: []const u8,
648
649 const Kind = enum {
650 Exe,
651 Lib,
652 };
653
654 pub fn initExecutable(builder: &Builder, name: []const u8, root_src: []const u8) -> LibOrExeStep {
655 return initExtraArgs(builder, name, root_src, Kind.Exe, false, builder.version(0, 0, 0));
656 }
657
658 pub fn initSharedLibrary(builder: &Builder, name: []const u8, root_src: []const u8,
659 ver: &const Version) -> LibOrExeStep
660 {
661 return initExtraArgs(builder, name, root_src, Kind.Lib, false, ver);
662 }
663
664 pub fn initStaticLibrary(builder: &Builder, name: []const u8, root_src: []const u8) -> LibOrExeStep {
665 return initExtraArgs(builder, name, root_src, Kind.Lib, true, builder.version(0, 0, 0));
666 }
614667
615 pub fn init(builder: &Builder, name: []const u8, root_src: []const u8) -> Exe {
616 Exe {
668 fn initExtraArgs(builder: &Builder, name: []const u8, root_src: []const u8, kind: Kind,
669 static: bool, ver: &const Version) -> LibOrExeStep
670 {
671 var self = LibOrExeStep {
617672 .builder = builder,
618673 .verbose = false,
619674 .release = false,
675 .static = static,
676 .kind = kind,
620677 .root_src = root_src,
621678 .name = name,
622679 .target = Target.Native,
......@@ -624,14 +681,34 @@ pub const Exe = struct {
624681 .link_libs = BufSet.init(builder.allocator),
625682 .step = Step.init(name, builder.allocator, make),
626683 .output_path = null,
627 }
684 .version = *ver,
685 .out_filename = undefined,
686 .out_filename_major_only = undefined,
687 .out_filename_name_only = undefined,
688 };
689 self.computeOutFileNames();
690 return self;
628691 }
629692
630 pub fn deinit(self: &Exe) {
631 self.link_libs.deinit();
693 fn computeOutFileNames(self: &LibOrExeStep) {
694 switch (self.kind) {
695 Kind.Exe => {
696 self.out_filename = self.builder.fmt("{}{}", self.name, self.target.exeFileExt());
697 },
698 Kind.Lib => {
699 if (self.static) {
700 self.out_filename = self.builder.fmt("lib{}.a", self.name);
701 } else {
702 self.out_filename = self.builder.fmt("lib{}.so.{d}.{d}.{d}",
703 self.name, self.version.major, self.version.minor, self.version.patch);
704 self.out_filename_major_only = self.builder.fmt("lib{}.so.{d}", self.name, self.version.major);
705 self.out_filename_name_only = self.builder.fmt("lib{}.so", self.name);
706 }
707 },
708 }
632709 }
633710
634 pub fn setTarget(self: &Exe, target_arch: Arch, target_os: Os, target_environ: Environ) {
711 pub fn setTarget(self: &LibOrExeStep, target_arch: Arch, target_os: Os, target_environ: Environ) {
635712 self.target = Target.Cross {
636713 CrossTarget {
637714 .arch = target_arch,
......@@ -641,59 +718,74 @@ pub const Exe = struct {
641718 };
642719 }
643720
644 /// Exe keeps a reference to script for its lifetime or until this function
721 /// LibOrExeStep keeps a reference to script for its lifetime or until this function
645722 /// is called again.
646 pub fn setLinkerScriptContents(self: &Exe, script: []const u8) {
723 pub fn setLinkerScriptContents(self: &LibOrExeStep, script: []const u8) {
647724 self.linker_script = LinkerScript.Embed { script };
648725 }
649726
650 pub fn setLinkerScriptPath(self: &Exe, path: []const u8) {
727 pub fn setLinkerScriptPath(self: &LibOrExeStep, path: []const u8) {
651728 self.linker_script = LinkerScript.Path { path };
652729 }
653730
654 pub fn linkLibrary(self: &Exe, name: []const u8) {
731 pub fn linkSystemLibrary(self: &LibOrExeStep, name: []const u8) {
655732 %%self.link_libs.put(name);
656733 }
657734
658 pub fn setVerbose(self: &Exe, value: bool) {
735 pub fn setVerbose(self: &LibOrExeStep, value: bool) {
659736 self.verbose = value;
660737 }
661738
662 pub fn setRelease(self: &Exe, value: bool) {
739 pub fn setRelease(self: &LibOrExeStep, value: bool) {
663740 self.release = value;
664741 }
665742
666 pub fn setOutputPath(self: &Exe, value: []const u8) {
743 pub fn setOutputPath(self: &LibOrExeStep, value: []const u8) {
667744 self.output_path = value;
668745 }
669746
670747 fn make(step: &Step) -> %void {
671 const exe = @fieldParentPtr(Exe, "step", step);
672 const builder = exe.builder;
748 const self = @fieldParentPtr(LibOrExeStep, "step", step);
749 const builder = self.builder;
673750
674751 var zig_args = List([]const u8).init(builder.allocator);
675752 defer zig_args.deinit();
676753
677 %%zig_args.append("build_exe");
678 %%zig_args.append(builder.pathFromRoot(exe.root_src));
754 const cmd = switch (self.kind) {
755 Kind.Lib => "build_lib",
756 Kind.Exe => "build_exe",
757 };
758 %%zig_args.append(cmd);
759 %%zig_args.append(builder.pathFromRoot(self.root_src));
679760
680 if (exe.verbose) {
761 if (self.verbose) {
681762 %%zig_args.append("--verbose");
682763 }
683764
684 if (exe.release) {
765 if (self.release) {
685766 %%zig_args.append("--release");
686767 }
687768
688 if (const output_path ?= exe.output_path) {
769 if (const output_path ?= self.output_path) {
689770 %%zig_args.append("--output");
690771 %%zig_args.append(builder.pathFromRoot(output_path));
691772 }
692773
693774 %%zig_args.append("--name");
694 %%zig_args.append(exe.name);
775 %%zig_args.append(self.name);
695776
696 switch (exe.target) {
777 if (self.kind == Kind.Lib and !self.static) {
778 %%zig_args.append("--ver-major");
779 %%zig_args.append(builder.fmt("{}", self.version.major));
780
781 %%zig_args.append("--ver-minor");
782 %%zig_args.append(builder.fmt("{}", self.version.minor));
783
784 %%zig_args.append("--ver-patch");
785 %%zig_args.append(builder.fmt("{}", self.version.patch));
786 }
787
788 switch (self.target) {
697789 Target.Native => {},
698790 Target.Cross => |cross_target| {
699791 %%zig_args.append("--target-arch");
......@@ -707,7 +799,7 @@ pub const Exe = struct {
707799 },
708800 }
709801
710 switch (exe.linker_script) {
802 switch (self.linker_script) {
711803 LinkerScript.None => {},
712804 LinkerScript.Embed => |script| {
713805 const tmp_file_name = "linker.ld.tmp"; // TODO issue #298
......@@ -723,7 +815,7 @@ pub const Exe = struct {
723815 }
724816
725817 {
726 var it = exe.link_libs.iterator();
818 var it = self.link_libs.iterator();
727819 while (true) {
728820 const entry = it.next() ?? break;
729821 %%zig_args.append("--library");
......@@ -746,7 +838,118 @@ pub const Exe = struct {
746838 %%zig_args.append(lib_path);
747839 }
748840
749 builder.spawnChild(builder.zig_exe, zig_args.toSliceConst());
841 %%builder.spawnChild(builder.zig_exe, zig_args.toSliceConst());
842
843 if (self.kind == Kind.Lib and !self.static) {
844 // sym link for libfoo.so.1 to libfoo.so.1.2.3
845 %%os.atomicSymLink(builder.allocator, self.out_filename, self.out_filename_major_only);
846 // sym link for libfoo.so to libfoo.so.1
847 %%os.atomicSymLink(builder.allocator, self.out_filename_major_only, self.out_filename_name_only);
848 }
849 }
850};
851
852pub const ObjectStep = struct {
853 step: Step,
854 builder: &Builder,
855 root_src: []const u8,
856 name: []const u8,
857 target: Target,
858 verbose: bool,
859 release: bool,
860 output_path: ?[]const u8,
861
862 pub fn init(builder: &Builder, name: []const u8, root_src: []const u8) -> ObjectStep {
863 ObjectStep {
864 .builder = builder,
865 .verbose = false,
866 .release = false,
867 .root_src = root_src,
868 .name = name,
869 .target = Target.Native,
870 .step = Step.init(name, builder.allocator, make),
871 .output_path = null,
872 }
873 }
874
875 pub fn setTarget(self: &ObjectStep, target_arch: Arch, target_os: Os, target_environ: Environ) {
876 self.target = Target.Cross {
877 CrossTarget {
878 .arch = target_arch,
879 .os = target_os,
880 .environ = target_environ,
881 }
882 };
883 }
884
885 pub fn setVerbose(self: &ObjectStep, value: bool) {
886 self.verbose = value;
887 }
888
889 pub fn setRelease(self: &ObjectStep, value: bool) {
890 self.release = value;
891 }
892
893 pub fn setOutputPath(self: &ObjectStep, value: []const u8) {
894 self.output_path = value;
895 }
896
897 fn make(step: &Step) -> %void {
898 const self = @fieldParentPtr(ObjectStep, "step", step);
899 const builder = self.builder;
900
901 var zig_args = List([]const u8).init(builder.allocator);
902 defer zig_args.deinit();
903
904 %%zig_args.append("build_obj");
905 %%zig_args.append(builder.pathFromRoot(self.root_src));
906
907 if (self.verbose) {
908 %%zig_args.append("--verbose");
909 }
910
911 if (self.release) {
912 %%zig_args.append("--release");
913 }
914
915 if (const output_path ?= self.output_path) {
916 %%zig_args.append("--output");
917 %%zig_args.append(builder.pathFromRoot(output_path));
918 }
919
920 %%zig_args.append("--name");
921 %%zig_args.append(self.name);
922
923 switch (self.target) {
924 Target.Native => {},
925 Target.Cross => |cross_target| {
926 %%zig_args.append("--target-arch");
927 %%zig_args.append(@enumTagName(cross_target.arch));
928
929 %%zig_args.append("--target-os");
930 %%zig_args.append(@enumTagName(cross_target.os));
931
932 %%zig_args.append("--target-environ");
933 %%zig_args.append(@enumTagName(cross_target.environ));
934 },
935 }
936
937 for (builder.include_paths.toSliceConst()) |include_path| {
938 %%zig_args.append("-isystem");
939 %%zig_args.append(include_path);
940 }
941
942 for (builder.rpaths.toSliceConst()) |rpath| {
943 %%zig_args.append("-rpath");
944 %%zig_args.append(rpath);
945 }
946
947 for (builder.lib_paths.toSliceConst()) |lib_path| {
948 %%zig_args.append("--library-path");
949 %%zig_args.append(lib_path);
950 }
951
952 %%builder.spawnChild(builder.zig_exe, zig_args.toSliceConst());
750953 }
751954};
752955
......@@ -836,7 +1039,7 @@ pub const AsmStep = struct {
8361039 },
8371040 }
8381041
839 builder.spawnChild(builder.zig_exe, zig_args.toSliceConst());
1042 %%builder.spawnChild(builder.zig_exe, zig_args.toSliceConst());
8401043 }
8411044};
8421045
......@@ -941,7 +1144,7 @@ pub const LinkStep = struct {
9411144 self.linker_script = LinkerScript.Path { path };
9421145 }
9431146
944 pub fn linkLibrary(self: &LinkStep, name: []const u8) {
1147 pub fn linkSystemLibrary(self: &LinkStep, name: []const u8) {
9451148 %%self.link_libs.put(name);
9461149 }
9471150
......@@ -1047,7 +1250,7 @@ pub const LinkStep = struct {
10471250 %%zig_args.append(lib_path);
10481251 }
10491252
1050 builder.spawnChild(builder.zig_exe, zig_args.toSliceConst());
1253 %%builder.spawnChild(builder.zig_exe, zig_args.toSliceConst());
10511254 }
10521255};
10531256
......@@ -1083,7 +1286,7 @@ pub const TestStep = struct {
10831286 self.release = value;
10841287 }
10851288
1086 pub fn linkLibrary(self: &TestStep, name: []const u8) {
1289 pub fn linkSystemLibrary(self: &TestStep, name: []const u8) {
10871290 %%self.link_libs.put(name);
10881291 }
10891292
......@@ -1147,7 +1350,7 @@ pub const TestStep = struct {
11471350 %%zig_args.append(lib_path);
11481351 }
11491352
1150 builder.spawnChild(builder.zig_exe, zig_args.toSliceConst());
1353 %%builder.spawnChild(builder.zig_exe, zig_args.toSliceConst());
11511354 }
11521355};
11531356
......@@ -1207,7 +1410,7 @@ pub const CLibrary = struct {
12071410 }
12081411 }
12091412
1210 pub fn linkLibrary(self: &CLibrary, name: []const u8) {
1413 pub fn linkSystemLibrary(self: &CLibrary, name: []const u8) {
12111414 %%self.link_libs.put(name);
12121415 }
12131416
......@@ -1276,7 +1479,7 @@ pub const CLibrary = struct {
12761479 %%cc_args.append(dir);
12771480 }
12781481
1279 builder.spawnChild(cc, cc_args.toSliceConst());
1482 %return builder.spawnChild(cc, cc_args.toSliceConst());
12801483
12811484 %%self.object_files.append(o_file);
12821485 }
......@@ -1300,7 +1503,7 @@ pub const CLibrary = struct {
13001503 %%cc_args.append(builder.pathFromRoot(object_file));
13011504 }
13021505
1303 builder.spawnChild(cc, cc_args.toSliceConst());
1506 %return builder.spawnChild(cc, cc_args.toSliceConst());
13041507
13051508 // sym link for libfoo.so.1 to libfoo.so.1.2.3
13061509 %%os.atomicSymLink(builder.allocator, self.out_filename, self.major_only_filename);
......@@ -1347,7 +1550,7 @@ pub const CExecutable = struct {
13471550 }
13481551 }
13491552
1350 pub fn linkLibrary(self: &CExecutable, name: []const u8) {
1553 pub fn linkSystemLibrary(self: &CExecutable, name: []const u8) {
13511554 %%self.link_libs.put(name);
13521555 }
13531556
......@@ -1356,6 +1559,14 @@ pub const CExecutable = struct {
13561559 %%self.full_path_libs.append(clib.out_filename);
13571560 }
13581561
1562 pub fn linkLibrary(self: &CExecutable, lib: &LibOrExeStep) {
1563 assert(lib.kind == LibOrExeStep.Kind.Lib);
1564 self.step.dependOn(&lib.step);
1565 %%self.full_path_libs.append(lib.out_filename);
1566 // TODO should be some kind of isolated directory that only has this header in it
1567 %%self.include_dirs.append(self.builder.out_dir);
1568 }
1569
13591570 pub fn addSourceFile(self: &CExecutable, file: []const u8) {
13601571 %%self.source_files.append(file);
13611572 }
......@@ -1395,7 +1606,7 @@ pub const CExecutable = struct {
13951606 %%cc_args.resize(0);
13961607
13971608 %%cc_args.append("-c");
1398 %%cc_args.append(source_file);
1609 %%cc_args.append(builder.pathFromRoot(source_file));
13991610
14001611 // TODO don't dump the .o file in the same place as the source file
14011612 const o_file = builder.fmt("{}{}", source_file, self.target.oFileExt());
......@@ -1409,10 +1620,10 @@ pub const CExecutable = struct {
14091620
14101621 for (self.include_dirs.toSliceConst()) |dir| {
14111622 %%cc_args.append("-I");
1412 %%cc_args.append(dir);
1623 %%cc_args.append(builder.pathFromRoot(dir));
14131624 }
14141625
1415 builder.spawnChild(cc, cc_args.toSliceConst());
1626 %return builder.spawnChild(cc, cc_args.toSliceConst());
14161627
14171628 %%self.object_files.append(o_file);
14181629 }
......@@ -1436,7 +1647,7 @@ pub const CExecutable = struct {
14361647 %%cc_args.append(full_path_lib);
14371648 }
14381649
1439 builder.spawnChild(cc, cc_args.toSliceConst());
1650 %return builder.spawnChild(cc, cc_args.toSliceConst());
14401651 }
14411652
14421653 pub fn setTarget(self: &CExecutable, target_arch: Arch, target_os: Os, target_environ: Environ) {
......@@ -1475,7 +1686,7 @@ pub const CommandStep = struct {
14751686 const self = @fieldParentPtr(CommandStep, "step", step);
14761687
14771688 // TODO set cwd
1478 self.builder.spawnChildEnvMap(self.env_map, self.exe_path, self.args);
1689 return self.builder.spawnChildEnvMap(self.env_map, self.exe_path, self.args);
14791690 }
14801691};
14811692
......@@ -1552,7 +1763,7 @@ pub const WriteFileStep = struct {
15521763 fn make(step: &Step) -> %void {
15531764 const self = @fieldParentPtr(WriteFileStep, "step", step);
15541765 const full_path = self.builder.pathFromRoot(self.file_path);
1555 const full_path_dir = %%os.path.dirname(self.builder.allocator, full_path);
1766 const full_path_dir = os.path.dirname(full_path);
15561767 os.makePath(self.builder.allocator, full_path_dir) %% |err| {
15571768 %%io.stderr.printf("unable to make path {}: {}\n", full_path_dir, @errorName(err));
15581769 return err;
std/cstr.zig+2-135
......@@ -1,11 +1,6 @@
1const List = @import("list.zig").List;
2const mem = @import("mem.zig");
3const Allocator = mem.Allocator;
41const debug = @import("debug.zig");
52const assert = debug.assert;
63
7const strlen = len;
8
94pub fn len(ptr: &const u8) -> usize {
105 var count: usize = 0;
116 while (ptr[count] != 0; count += 1) {}
......@@ -25,139 +20,11 @@ pub fn cmp(a: &const u8, b: &const u8) -> i8 {
2520}
2621
2722pub fn toSliceConst(str: &const u8) -> []const u8 {
28 return str[0...strlen(str)];
23 return str[0...len(str)];
2924}
3025
3126pub fn toSlice(str: &u8) -> []u8 {
32 return str[0...strlen(str)];
33}
34
35
36/// A buffer that allocates memory and maintains a null byte at the end.
37pub const Buffer0 = struct {
38 list: List(u8),
39
40 /// Must deinitialize with deinit.
41 pub fn initEmpty(allocator: &Allocator) -> %Buffer0 {
42 return initSize(allocator, 0);
43 }
44
45 /// Must deinitialize with deinit.
46 pub fn initFromMem(allocator: &Allocator, m: []const u8) -> %Buffer0 {
47 var self = %return initSize(allocator, m.len);
48 mem.copy(u8, self.list.items, m);
49 return self;
50 }
51
52 /// Must deinitialize with deinit.
53 pub fn initFromCStr(allocator: &Allocator, s: &const u8) -> %Buffer0 {
54 return Buffer0.initFromMem(allocator, s[0...strlen(s)]);
55 }
56
57 /// Must deinitialize with deinit.
58 pub fn initFromOther(cbuf: &const Buffer0) -> %Buffer0 {
59 return Buffer0.initFromMem(cbuf.list.allocator, cbuf.list.items[0...cbuf.len()]);
60 }
61
62 /// Must deinitialize with deinit.
63 pub fn initFromSlice(other: &const Buffer0, start: usize, end: usize) -> %Buffer0 {
64 return Buffer0.initFromMem(other.list.allocator, other.list.items[start...end]);
65 }
66
67 /// Must deinitialize with deinit.
68 pub fn initSize(allocator: &Allocator, size: usize) -> %Buffer0 {
69 var self = Buffer0 {
70 .list = List(u8).init(allocator),
71 };
72 %return self.resize(size);
73 return self;
74 }
75
76 pub fn deinit(self: &Buffer0) {
77 self.list.deinit();
78 }
79
80 pub fn toSlice(self: &Buffer0) -> []u8 {
81 return self.list.toSlice()[0...self.len()];
82 }
83
84 pub fn toSliceConst(self: &const Buffer0) -> []const u8 {
85 return self.list.toSliceConst()[0...self.len()];
86 }
87
88 pub fn resize(self: &Buffer0, new_len: usize) -> %void {
89 %return self.list.resize(new_len + 1);
90 self.list.items[self.len()] = 0;
91 }
92
93 pub fn len(self: &const Buffer0) -> usize {
94 return self.list.len - 1;
95 }
96
97 pub fn appendMem(self: &Buffer0, m: []const u8) -> %void {
98 const old_len = self.len();
99 %return self.resize(old_len + m.len);
100 mem.copy(u8, self.list.toSlice()[old_len...], m);
101 }
102
103 pub fn appendOther(self: &Buffer0, other: &const Buffer0) -> %void {
104 return self.appendMem(other.toSliceConst());
105 }
106
107 pub fn appendCStr(self: &Buffer0, s: &const u8) -> %void {
108 self.appendMem(s[0...strlen(s)])
109 }
110
111 pub fn appendByte(self: &Buffer0, byte: u8) -> %void {
112 %return self.resize(self.len() + 1);
113 self.list.items[self.len() - 1] = byte;
114 }
115
116 pub fn eqlMem(self: &const Buffer0, m: []const u8) -> bool {
117 if (self.len() != m.len) return false;
118 return mem.cmp(u8, self.list.items[0...m.len], m) == mem.Cmp.Equal;
119 }
120
121 pub fn eqlCStr(self: &const Buffer0, s: &const u8) -> bool {
122 self.eqlMem(s[0...strlen(s)])
123 }
124
125 pub fn eqlOther(self: &const Buffer0, other: &const Buffer0) -> bool {
126 self.eqlMem(other.list.items[0...other.len()])
127 }
128
129 pub fn startsWithMem(self: &const Buffer0, m: []const u8) -> bool {
130 if (self.len() < m.len) return false;
131 return mem.cmp(u8, self.list.items[0...m.len], m) == mem.Cmp.Equal;
132 }
133
134 pub fn startsWithOther(self: &const Buffer0, other: &const Buffer0) -> bool {
135 self.startsWithMem(other.list.items[0...other.len()])
136 }
137
138 pub fn startsWithCStr(self: &const Buffer0, s: &const u8) -> bool {
139 self.startsWithMem(s[0...strlen(s)])
140 }
141};
142
143test "simple Buffer0" {
144 var buf = %%Buffer0.initEmpty(&debug.global_allocator);
145 assert(buf.len() == 0);
146 %%buf.appendCStr(c"hello");
147 %%buf.appendByte(' ');
148 %%buf.appendMem("world");
149 assert(buf.eqlCStr(c"hello world"));
150 assert(buf.eqlMem("hello world"));
151 assert(mem.eql(u8, buf.toSliceConst(), "hello world"));
152
153 var buf2 = %%Buffer0.initFromOther(&buf);
154 assert(buf.eqlOther(&buf2));
155
156 assert(buf.startsWithMem("hell"));
157 assert(buf.startsWithCStr(c"hell"));
158
159 %%buf2.resize(4);
160 assert(buf.startsWithOther(&buf2));
27 return str[0...len(str)];
16128}
16229
16330test "cstr fns" {
std/index.zig+1
......@@ -1,4 +1,5 @@
11pub const base64 = @import("base64.zig");
2pub const buffer = @import("buffer.zig");
23pub const build = @import("build.zig");
34pub const c = @import("c/index.zig");
45pub const cstr = @import("cstr.zig");
std/io.zig+2-2
......@@ -10,7 +10,7 @@ const debug = @import("debug.zig");
1010const assert = debug.assert;
1111const os = @import("os/index.zig");
1212const mem = @import("mem.zig");
13const Buffer0 = @import("cstr.zig").Buffer0;
13const Buffer = @import("buffer.zig").Buffer;
1414const fmt = @import("fmt.zig");
1515
1616pub var stdin = InStream {
......@@ -326,7 +326,7 @@ pub const InStream = struct {
326326 return usize(stat.size);
327327 }
328328
329 pub fn readAll(is: &InStream, buf: &Buffer0) -> %void {
329 pub fn readAll(is: &InStream, buf: &Buffer) -> %void {
330330 %return buf.resize(os.page_size);
331331
332332 var actual_buf_len: usize = 0;
std/list.zig+5
......@@ -44,6 +44,11 @@ pub fn List(comptime T: type) -> type{
4444 l.len = new_len;
4545 }
4646
47 pub fn resizeDown(l: &Self, new_len: usize) {
48 assert(new_len <= l.len);
49 l.len = new_len;
50 }
51
4752 pub fn ensureCapacity(l: &Self, new_capacity: usize) -> %void {
4853 var better_capacity = l.items.len;
4954 if (better_capacity >= new_capacity) return;
std/os/index.zig+41-24
......@@ -226,11 +226,11 @@ pub fn posixDup2(old_fd: i32, new_fd: i32) -> %void {
226226pub fn posixExecve(exe_path: []const u8, argv: []const []const u8, env_map: &const BufMap,
227227 allocator: &Allocator) -> %void
228228{
229 const argv_buf = %return allocator.alloc(?&const u8, argv.len + 2);
230 mem.set(?&const u8, argv_buf, null);
229 const argv_buf = %return allocator.alloc(?&u8, argv.len + 2);
230 mem.set(?&u8, argv_buf, null);
231231 defer {
232232 for (argv_buf) |arg| {
233 const arg_buf = if (const ptr ?= arg) ptr[0...cstr.len(ptr)] else break;
233 const arg_buf = if (const ptr ?= arg) cstr.toSlice(ptr) else break;
234234 allocator.free(arg_buf);
235235 }
236236 allocator.free(argv_buf);
......@@ -253,11 +253,11 @@ pub fn posixExecve(exe_path: []const u8, argv: []const []const u8, env_map: &con
253253 argv_buf[argv.len + 1] = null;
254254
255255 const envp_count = env_map.count();
256 const envp_buf = %return allocator.alloc(?&const u8, envp_count + 1);
257 mem.set(?&const u8, envp_buf, null);
256 const envp_buf = %return allocator.alloc(?&u8, envp_count + 1);
257 mem.set(?&u8, envp_buf, null);
258258 defer {
259259 for (envp_buf) |env| {
260 const env_buf = if (const ptr ?= env) ptr[0...cstr.len(ptr)] else break;
260 const env_buf = if (const ptr ?= env) cstr.toSlice(ptr) else break;
261261 allocator.free(env_buf);
262262 }
263263 allocator.free(envp_buf);
......@@ -380,7 +380,7 @@ pub const args = struct {
380380 }
381381 pub fn at(i: usize) -> []const u8 {
382382 const s = raw[i];
383 return s[0...cstr.len(s)];
383 return cstr.toSlice(s);
384384 }
385385};
386386
......@@ -397,7 +397,7 @@ pub fn getCwd(allocator: &Allocator) -> %[]u8 {
397397 return error.Unexpected;
398398 }
399399
400 return buf;
400 return cstr.toSlice(buf.ptr);
401401 }
402402}
403403
......@@ -572,22 +572,39 @@ pub fn makeDir(allocator: &Allocator, dir_path: []const u8) -> %void {
572572/// Calls makeDir recursively to make an entire path. Returns success if the path
573573/// already exists and is a directory.
574574pub fn makePath(allocator: &Allocator, full_path: []const u8) -> %void {
575 const child_dir = %return path.dirname(allocator, full_path);
576 defer allocator.free(child_dir);
575 const resolved_path = %return path.resolve(allocator, full_path);
576 defer allocator.free(resolved_path);
577577
578 if (mem.eql(u8, child_dir, full_path))
579 return;
580
581 makePath(allocator, child_dir) %% |err| {
582 if (err != error.PathAlreadyExists)
583 return err;
584 };
585
586 makeDir(allocator, full_path) %% |err| {
587 if (err != error.PathAlreadyExists)
588 return err;
589 // TODO stat the file and return an error if it's not a directory
590 };
578 var end_index: usize = resolved_path.len;
579 while (true) {
580 makeDir(allocator, resolved_path[0...end_index]) %% |err| {
581 if (err == error.PathAlreadyExists) {
582 // TODO stat the file and return an error if it's not a directory
583 // this is important because otherwise a dangling symlink
584 // could cause an infinite loop
585 if (end_index == resolved_path.len)
586 return;
587 } else if (err == error.FileNotFound) {
588 // march end_index backward until next path component
589 while (true) {
590 end_index -= 1;
591 if (resolved_path[end_index] == '/')
592 break;
593 }
594 continue;
595 } else {
596 return err;
597 }
598 };
599 if (end_index == resolved_path.len)
600 return;
601 // march end_index forward until next path component
602 while (true) {
603 end_index += 1;
604 if (end_index == resolved_path.len or resolved_path[end_index] == '/')
605 break;
606 }
607 }
591608}
592609
593610/// Returns ::error.DirNotEmpty if the directory is not empty.
......@@ -739,7 +756,7 @@ pub const Dir = struct {
739756 const next_index = self.index + linux_entry.d_reclen;
740757 self.index = next_index;
741758
742 const name = (&linux_entry.d_name)[0...cstr.len(&linux_entry.d_name)];
759 const name = cstr.toSlice(&linux_entry.d_name);
743760
744761 // skip . and .. entries
745762 if (mem.eql(u8, name, ".") or mem.eql(u8, name, "..")) {
std/os/path.zig+125-15
......@@ -2,7 +2,11 @@ const debug = @import("../debug.zig");
22const assert = debug.assert;
33const mem = @import("../mem.zig");
44const Allocator = mem.Allocator;
5const os = @import("index.zig");
56
7pub const sep = '/';
8
9/// Naively combines a series of paths with the native path seperator.
610/// Allocates memory for the result, which must be freed by the caller.
711pub fn join(allocator: &Allocator, paths: ...) -> %[]u8 {
812 assert(paths.len >= 2);
......@@ -26,8 +30,8 @@ pub fn join(allocator: &Allocator, paths: ...) -> %[]u8 {
2630 mem.copy(u8, buf[buf_index...], arg);
2731 buf_index += arg.len;
2832 if (path_i >= paths.len) break;
29 if (arg[arg.len - 1] != '/') {
30 buf[buf_index] = '/';
33 if (arg[arg.len - 1] != sep) {
34 buf[buf_index] = sep;
3135 buf_index += 1;
3236 }
3337 }
......@@ -43,22 +47,128 @@ test "os.path.join" {
4347 assert(mem.eql(u8, %%join(&debug.global_allocator, "/a/", "b/", "c"), "/a/b/c"));
4448}
4549
46pub fn dirname(allocator: &Allocator, path: []const u8) -> %[]u8 {
47 if (path.len != 0) {
48 var last_index: usize = path.len - 1;
49 if (path[last_index] == '/')
50 last_index -= 1;
50pub fn isAbsolute(path: []const u8) -> bool {
51 switch (@compileVar("os")) {
52 Os.windows => @compileError("Unsupported OS"),
53 else => return path[0] == sep,
54 }
55}
56
57/// This function is like a series of `cd` statements executed one after another.
58/// The result does not have a trailing path separator.
59pub fn resolve(allocator: &Allocator, args: ...) -> %[]u8 {
60 var paths: [args.len][]const u8 = undefined;
61 comptime var arg_i = 0;
62 inline while (arg_i < args.len; arg_i += 1) {
63 paths[arg_i] = args[arg_i];
64 }
65 return resolveSlice(allocator, paths);
66}
67
68pub fn resolveSlice(allocator: &Allocator, paths: []const []const u8) -> %[]u8 {
69 if (paths.len == 0)
70 return os.getCwd(allocator);
5171
52 var i: usize = last_index;
72 var first_index: usize = 0;
73 var have_abs = false;
74 var max_size: usize = 0;
75 for (paths) |p, i| {
76 if (isAbsolute(p)) {
77 first_index = i;
78 have_abs = true;
79 max_size = 0;
80 }
81 max_size += p.len + 1;
82 }
83
84 var result: []u8 = undefined;
85 var result_index: usize = 0;
86
87 if (have_abs) {
88 result = %return allocator.alloc(u8, max_size);
89 } else {
90 const cwd = %return os.getCwd(allocator);
91 defer allocator.free(cwd);
92 result = %return allocator.alloc(u8, max_size + cwd.len + 1);
93 mem.copy(u8, result, cwd);
94 result_index += cwd.len;
95 }
96 %defer allocator.free(result);
97
98 for (paths[first_index...]) |p, i| {
99 var it = mem.split(p, '/');
53100 while (true) {
54 const c = path[i];
55 if (c == '/')
56 return mem.dupe(allocator, u8, path[0...i]);
57 if (i == 0)
58 break;
59 i -= 1;
101 const component = it.next() ?? break;
102 if (mem.eql(u8, component, ".")) {
103 continue;
104 } else if (mem.eql(u8, component, "..")) {
105 while (true) {
106 if (result_index == 0)
107 break;
108 result_index -= 1;
109 if (result[result_index] == '/')
110 break;
111 }
112 } else {
113 result[result_index] = '/';
114 result_index += 1;
115 mem.copy(u8, result[result_index...], component);
116 result_index += component.len;
117 }
60118 }
61119 }
62120
63 return mem.dupe(allocator, u8, ".");
121 if (result_index == 0) {
122 result[0] = '/';
123 result_index += 1;
124 }
125
126 return result[0...result_index];
127}
128
129test "os.path.resolve" {
130 assert(mem.eql(u8, testResolve("/a/b", "c"), "/a/b/c"));
131 assert(mem.eql(u8, testResolve("/a/b", "c", "//d", "e///"), "/d/e"));
132 assert(mem.eql(u8, testResolve("/a/b/c", "..", "../"), "/a"));
133 assert(mem.eql(u8, testResolve("/", "..", ".."), "/"));
134}
135fn testResolve(args: ...) -> []u8 {
136 return %%resolve(&debug.global_allocator, args);
137}
138
139pub fn dirname(path: []const u8) -> []const u8 {
140 if (path.len == 0)
141 return path[0...0];
142 var end_index: usize = path.len - 1;
143 while (path[end_index] == '/') {
144 if (end_index == 0)
145 return path[0...1];
146 end_index -= 1;
147 }
148
149 while (path[end_index] != '/') {
150 if (end_index == 0)
151 return path[0...0];
152 end_index -= 1;
153 }
154
155 if (end_index == 0 and path[end_index] == '/')
156 return path[0...1];
157
158 return path[0...end_index];
159}
160
161test "os.path.dirname" {
162 testDirname("/a/b/c", "/a/b");
163 testDirname("/a/b/c///", "/a/b");
164 testDirname("/a", "/");
165 testDirname("/", "/");
166 testDirname("////", "/");
167 testDirname("", "");
168 testDirname("a", "");
169 testDirname("a/", "");
170 testDirname("a//", "");
171}
172fn testDirname(input: []const u8, expected_output: []const u8) {
173 assert(mem.eql(u8, dirname(input), expected_output));
64174}
test/build_examples.zig+1
......@@ -5,4 +5,5 @@ pub fn addCases(cases: &tests.BuildExamplesContext) {
55 cases.addC("example/hello_world/hello_libc.zig");
66 cases.add("example/cat/main.zig");
77 cases.add("example/guess_number/main.zig");
8 cases.addBuildFile("example/shared_library/build.zig");
89}
test/tests.zig+40-11
......@@ -4,7 +4,7 @@ const build = std.build;
44const os = std.os;
55const StdIo = os.ChildProcess.StdIo;
66const Term = os.ChildProcess.Term;
7const Buffer0 = std.cstr.Buffer0;
7const Buffer = std.buffer.Buffer;
88const io = std.io;
99const mem = std.mem;
1010const fmt = std.fmt;
......@@ -116,7 +116,7 @@ pub fn addPkgTests(b: &build.Builder, test_filter: ?[]const u8, root_src: []cons
116116 these_tests.setFilter(test_filter);
117117 these_tests.setRelease(release);
118118 if (link_libc) {
119 these_tests.linkLibrary("c");
119 these_tests.linkSystemLibrary("c");
120120 }
121121 step.dependOn(&these_tests.step);
122122 }
......@@ -211,8 +211,8 @@ pub const CompareOutputContext = struct {
211211 },
212212 };
213213
214 var stdout = %%Buffer0.initEmpty(b.allocator);
215 var stderr = %%Buffer0.initEmpty(b.allocator);
214 var stdout = Buffer.initNull(b.allocator);
215 var stderr = Buffer.initNull(b.allocator);
216216
217217 %%(??child.stdout).readAll(&stdout);
218218 %%(??child.stderr).readAll(&stderr);
......@@ -388,7 +388,7 @@ pub const CompareOutputContext = struct {
388388 exe.setOutputPath(exe_path);
389389 exe.setRelease(release);
390390 if (case.link_libc) {
391 exe.linkLibrary("c");
391 exe.linkSystemLibrary("c");
392392 }
393393
394394 for (case.sources.toSliceConst()) |src_file| {
......@@ -415,7 +415,7 @@ pub const CompareOutputContext = struct {
415415 const exe = b.addExecutable("test", root_src);
416416 exe.setOutputPath(exe_path);
417417 if (case.link_libc) {
418 exe.linkLibrary("c");
418 exe.linkSystemLibrary("c");
419419 }
420420
421421 for (case.sources.toSliceConst()) |src_file| {
......@@ -537,8 +537,8 @@ pub const CompileErrorContext = struct {
537537 },
538538 };
539539
540 var stdout_buf = %%Buffer0.initEmpty(b.allocator);
541 var stderr_buf = %%Buffer0.initEmpty(b.allocator);
540 var stdout_buf = Buffer.initNull(b.allocator);
541 var stderr_buf = Buffer.initNull(b.allocator);
542542
543543 %%(??child.stdout).readAll(&stdout_buf);
544544 %%(??child.stderr).readAll(&stderr_buf);
......@@ -657,6 +657,35 @@ pub const BuildExamplesContext = struct {
657657 self.addAllArgs(root_src, false);
658658 }
659659
660 pub fn addBuildFile(self: &BuildExamplesContext, build_file: []const u8) {
661 const b = self.b;
662
663 const annotated_case_name = b.fmt("build {}", build_file);
664 if (const filter ?= self.test_filter) {
665 if (mem.indexOf(u8, annotated_case_name, filter) == null)
666 return;
667 }
668
669 var zig_args = List([]const u8).init(b.allocator);
670 %%zig_args.append("build");
671
672 %%zig_args.append("--build-file");
673 %%zig_args.append(b.pathFromRoot(build_file));
674
675 %%zig_args.append("test");
676
677 if (b.verbose) {
678 %%zig_args.append("--verbose");
679 }
680
681 const run_cmd = b.addCommand(b.out_dir, b.env_map, b.zig_exe, zig_args.toSliceConst());
682
683 const log_step = b.addLog("PASS {}\n", annotated_case_name);
684 log_step.step.dependOn(&run_cmd.step);
685
686 self.step.dependOn(&log_step.step);
687 }
688
660689 pub fn addAllArgs(self: &BuildExamplesContext, root_src: []const u8, link_libc: bool) {
661690 const b = self.b;
662691
......@@ -671,7 +700,7 @@ pub const BuildExamplesContext = struct {
671700 const exe = b.addExecutable("test", root_src);
672701 exe.setRelease(release);
673702 if (link_libc) {
674 exe.linkLibrary("c");
703 exe.linkSystemLibrary("c");
675704 }
676705
677706 const log_step = b.addLog("PASS {}\n", annotated_case_name);
......@@ -774,8 +803,8 @@ pub const ParseHContext = struct {
774803 },
775804 };
776805
777 var stdout_buf = %%Buffer0.initEmpty(b.allocator);
778 var stderr_buf = %%Buffer0.initEmpty(b.allocator);
806 var stdout_buf = Buffer.initNull(b.allocator);
807 var stderr_buf = Buffer.initNull(b.allocator);
779808
780809 %%(??child.stdout).readAll(&stdout_buf);
781810 %%(??child.stderr).readAll(&stderr_buf);