authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2017-04-19 14:00:12-04:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2017-04-19 14:00:12-04:00
logd1e01e43d3b2078bfb07defb693d819e99eaa6c5
treeee5a96ae18383a89b62533c9aada1d867a485959
parent666435195fff867417f92e1b4f8ef7e0608470ee

convert assemble and link tests to zig build system


11 files changed, 921 insertions(+), 541 deletions(-)

build.zig+1
......@@ -38,4 +38,5 @@ pub fn build(b: &Builder) {
3838 test_step.dependOn(tests.addCompareOutputTests(b, test_filter));
3939 test_step.dependOn(tests.addBuildExampleTests(b, test_filter));
4040 test_step.dependOn(tests.addCompileErrorTests(b, test_filter));
41 test_step.dependOn(tests.addAssembleAndLinkTests(b, test_filter));
4142}
src/link.cpp+8
......@@ -770,6 +770,14 @@ void codegen_link(CodeGen *g, const char *out_file) {
770770 if (g->want_h_file) {
771771 codegen_generate_h_file(g);
772772 }
773 if (override_out_file) {
774 assert(g->link_objects.length == 1);
775 Buf *o_file_path = g->link_objects.at(0);
776 int err;
777 if ((err = os_rename(o_file_path, &lj.out_file))) {
778 zig_panic("unable to rename object file into final output: %s", err_str(err));
779 }
780 }
773781 if (g->verbose) {
774782 fprintf(stderr, "OK\n");
775783 }
src/os.cpp+7
......@@ -683,3 +683,10 @@ int os_delete_file(Buf *path) {
683683void os_init(void) {
684684 srand((unsigned)time(NULL));
685685}
686
687int os_rename(Buf *src_path, Buf *dest_path) {
688 if (rename(buf_ptr(src_path), buf_ptr(dest_path)) == -1) {
689 return ErrorFileSystem;
690 }
691 return 0;
692}
src/os.hpp+2
......@@ -55,6 +55,8 @@ int os_delete_file(Buf *path);
5555
5656int os_file_exists(Buf *full_path, bool *result);
5757
58int os_rename(Buf *src_path, Buf *dest_path);
59
5860#if defined(__APPLE__)
5961#define ZIG_OS_DARWIN
6062#elif defined(_WIN32)
std/build.zig+342-2
......@@ -17,6 +17,7 @@ error UncleanExit;
1717error InvalidStepName;
1818error DependencyLoopDetected;
1919error NoCompilerFound;
20error NeedAnObject;
2021
2122pub const Builder = struct {
2223 uninstall_tls: TopLevelStep,
......@@ -131,6 +132,30 @@ pub const Builder = struct {
131132 return test_step;
132133 }
133134
135 pub fn addAssemble(self: &Builder, name: []const u8, src: []const u8) -> &AsmStep {
136 const asm_step = %%self.allocator.create(AsmStep);
137 *asm_step = AsmStep.init(self, name, src);
138 return asm_step;
139 }
140
141 pub fn addLinkExecutable(self: &Builder, name: []const u8) -> &LinkStep {
142 const exe = %%self.allocator.create(LinkStep);
143 *exe = LinkStep.initExecutable(self, name);
144 return exe;
145 }
146
147 pub fn addLinkStaticLibrary(self: &Builder, name: []const u8) -> &LinkStep {
148 const exe = %%self.allocator.create(LinkStep);
149 *exe = LinkStep.initStaticLibrary(self, name);
150 return exe;
151 }
152
153 pub fn addLinkSharedLibrary(self: &Builder, name: []const u8, ver: &const Version) -> &LinkStep {
154 const exe = %%self.allocator.create(LinkStep);
155 *exe = LinkStep.initSharedLibrary(self, name, ver);
156 return exe;
157 }
158
134159 pub fn addCStaticLibrary(self: &Builder, name: []const u8) -> &CLibrary {
135160 const lib = %%self.allocator.create(CLibrary);
136161 *lib = CLibrary.initStatic(self, name);
......@@ -546,6 +571,17 @@ const Target = enum {
546571 else => ".o",
547572 };
548573 }
574
575 pub fn exeFileExt(self: &const Target) -> []const u8 {
576 const target_os = switch (*self) {
577 Target.Native => @compileVar("os"),
578 Target.Cross => |t| t.os,
579 };
580 return switch (target_os) {
581 Os.windows => ".exe",
582 else => "",
583 };
584 }
549585};
550586
551587const LinkerScript = enum {
......@@ -704,6 +740,310 @@ pub const Exe = struct {
704740 }
705741};
706742
743pub const AsmStep = struct {
744 step: Step,
745 builder: &Builder,
746 name: []const u8,
747 target: Target,
748 verbose: bool,
749 release: bool,
750 output_path: ?[]const u8,
751 src_path: []const u8,
752
753 pub fn init(builder: &Builder, name: []const u8, src_path: []const u8) -> AsmStep {
754 var self = AsmStep {
755 .step = Step.init(name, builder.allocator, make),
756 .builder = builder,
757 .name = name,
758 .target = Target.Native,
759 .verbose = false,
760 .release = false,
761 .output_path = null,
762 .src_path = src_path,
763 };
764 return self;
765 }
766
767 pub fn setTarget(self: &AsmStep, target_arch: Arch, target_os: Os, target_environ: Environ) {
768 self.target = Target.Cross {
769 CrossTarget {
770 .arch = target_arch,
771 .os = target_os,
772 .environ = target_environ,
773 }
774 };
775 }
776
777 pub fn setVerbose(self: &AsmStep, value: bool) {
778 self.verbose = value;
779 }
780
781 pub fn setRelease(self: &AsmStep, value: bool) {
782 self.release = value;
783 }
784
785 pub fn setOutputPath(self: &AsmStep, value: []const u8) {
786 self.output_path = value;
787 }
788
789 fn make(step: &Step) -> %void {
790 const self = @fieldParentPtr(AsmStep, "step", step);
791 const builder = self.builder;
792
793 var zig_args = List([]const u8).init(builder.allocator);
794 defer zig_args.deinit();
795
796 %%zig_args.append("asm");
797 %%zig_args.append(builder.pathFromRoot(self.src_path));
798
799 if (self.verbose) {
800 %%zig_args.append("--verbose");
801 }
802
803 if (self.release) {
804 %%zig_args.append("--release");
805 }
806
807 if (const output_path ?= self.output_path) {
808 %%zig_args.append("--output");
809 %%zig_args.append(builder.pathFromRoot(output_path));
810 }
811
812 %%zig_args.append("--name");
813 %%zig_args.append(self.name);
814
815 switch (self.target) {
816 Target.Native => {},
817 Target.Cross => |cross_target| {
818 %%zig_args.append("--target-arch");
819 %%zig_args.append(@enumTagName(cross_target.arch));
820
821 %%zig_args.append("--target-os");
822 %%zig_args.append(@enumTagName(cross_target.os));
823
824 %%zig_args.append("--target-environ");
825 %%zig_args.append(@enumTagName(cross_target.environ));
826 },
827 }
828
829 builder.spawnChild(builder.zig_exe, zig_args.toSliceConst());
830 }
831};
832
833pub const LinkStep = struct {
834 step: Step,
835 builder: &Builder,
836 name: []const u8,
837 target: Target,
838 linker_script: LinkerScript,
839 link_libs: BufSet,
840 verbose: bool,
841 release: bool,
842 output_path: ?[]const u8,
843 object_files: List([]const u8),
844 static: bool,
845 out_filename: []const u8,
846 out_type: OutType,
847 version: Version,
848 major_only_filename: []const u8,
849 name_only_filename: []const u8,
850
851 const OutType = enum {
852 Exe,
853 Lib,
854 };
855
856 pub fn initExecutable(builder: &Builder, name: []const u8) -> LinkStep {
857 return init(builder, name, OutType.Exe, builder.version(0, 0, 0), false)
858 }
859
860 pub fn initSharedLibrary(builder: &Builder, name: []const u8, version: &const Version) -> LinkStep {
861 return init(builder, name, OutType.Lib, version, false)
862 }
863
864 pub fn initStaticLibrary(builder: &Builder, name: []const u8) -> LinkStep {
865 return init(builder, name, OutType.Lib, builder.version(0, 0, 0), true)
866 }
867
868 fn init(builder: &Builder, name: []const u8, out_type: OutType, version: &const Version, static: bool) -> LinkStep {
869 var self = LinkStep {
870 .builder = builder,
871 .verbose = false,
872 .release = false,
873 .name = name,
874 .target = Target.Native,
875 .linker_script = LinkerScript.None,
876 .link_libs = BufSet.init(builder.allocator),
877 .step = Step.init(name, builder.allocator, make),
878 .output_path = null,
879 .object_files = List([]const u8).init(builder.allocator),
880 .out_type = out_type,
881 .version = *version,
882 .static = static,
883 .out_filename = undefined,
884 .major_only_filename = undefined,
885 .name_only_filename = undefined,
886 };
887 self.computeOutFileName();
888 return self;
889 }
890
891 fn computeOutFileName(self: &LinkStep) {
892 switch (self.out_type) {
893 OutType.Exe => {
894 self.out_filename = %%fmt.allocPrint(self.builder.allocator, "{}{}",
895 self.name, self.target.exeFileExt());
896 },
897 OutType.Lib => {
898 if (self.static) {
899 self.out_filename = %%fmt.allocPrint(self.builder.allocator, "lib{}.a", self.name);
900 } else {
901 self.out_filename = %%fmt.allocPrint(self.builder.allocator, "lib{}.so.{d}.{d}.{d}",
902 self.name, self.version.major, self.version.minor, self.version.patch);
903 self.major_only_filename = %%fmt.allocPrint(self.builder.allocator,
904 "lib{}.so.{d}", self.name, self.version.major);
905 self.name_only_filename = %%fmt.allocPrint(self.builder.allocator,
906 "lib{}.so", self.name);
907 }
908 },
909 }
910 }
911
912 pub fn addObjectFile(self: &LinkStep, file: []const u8) {
913 %%self.object_files.append(file);
914 }
915
916 pub fn setTarget(self: &LinkStep, target_arch: Arch, target_os: Os, target_environ: Environ) {
917 self.target = Target.Cross {
918 CrossTarget {
919 .arch = target_arch,
920 .os = target_os,
921 .environ = target_environ,
922 }
923 };
924 self.computeOutFileName();
925 }
926
927 /// LinkStep keeps a reference to script for its lifetime or until this function
928 /// is called again.
929 pub fn setLinkerScriptContents(self: &LinkStep, script: []const u8) {
930 self.linker_script = LinkerScript.Embed { script };
931 }
932
933 pub fn setLinkerScriptPath(self: &LinkStep, path: []const u8) {
934 self.linker_script = LinkerScript.Path { path };
935 }
936
937 pub fn linkLibrary(self: &LinkStep, name: []const u8) {
938 %%self.link_libs.put(name);
939 }
940
941 pub fn setVerbose(self: &LinkStep, value: bool) {
942 self.verbose = value;
943 }
944
945 pub fn setRelease(self: &LinkStep, value: bool) {
946 self.release = value;
947 }
948
949 pub fn setOutputPath(self: &LinkStep, value: []const u8) {
950 self.output_path = value;
951 }
952
953 fn make(step: &Step) -> %void {
954 const self = @fieldParentPtr(LinkStep, "step", step);
955 const builder = self.builder;
956
957 if (self.object_files.len == 0) {
958 %%io.stderr.printf("{}: linker needs 1 or more objects to link\n", step.name);
959 return error.NeedAnObject;
960 }
961
962 var zig_args = List([]const u8).init(builder.allocator);
963 defer zig_args.deinit();
964
965 const cmd = switch (self.out_type) {
966 OutType.Exe => "link_exe",
967 OutType.Lib => "link_lib",
968 };
969 %%zig_args.append(cmd);
970
971 for (self.object_files.toSliceConst()) |object_file| {
972 %%zig_args.append(builder.pathFromRoot(object_file));
973 }
974
975 if (self.verbose) {
976 %%zig_args.append("--verbose");
977 }
978
979 if (self.release) {
980 %%zig_args.append("--release");
981 }
982
983 if (self.static) {
984 %%zig_args.append("--static");
985 }
986
987 if (const output_path ?= self.output_path) {
988 %%zig_args.append("--output");
989 %%zig_args.append(builder.pathFromRoot(output_path));
990 }
991
992 %%zig_args.append("--name");
993 %%zig_args.append(self.name);
994
995 switch (self.target) {
996 Target.Native => {},
997 Target.Cross => |cross_target| {
998 %%zig_args.append("--target-arch");
999 %%zig_args.append(@enumTagName(cross_target.arch));
1000
1001 %%zig_args.append("--target-os");
1002 %%zig_args.append(@enumTagName(cross_target.os));
1003
1004 %%zig_args.append("--target-environ");
1005 %%zig_args.append(@enumTagName(cross_target.environ));
1006 },
1007 }
1008
1009 switch (self.linker_script) {
1010 LinkerScript.None => {},
1011 LinkerScript.Embed => |script| {
1012 const tmp_file_name = "linker.ld.tmp"; // TODO issue #298
1013 io.writeFile(tmp_file_name, script, builder.allocator)
1014 %% |err| debug.panic("unable to write linker script: {}\n", @errorName(err));
1015 %%zig_args.append("--linker-script");
1016 %%zig_args.append(tmp_file_name);
1017 },
1018 LinkerScript.Path => |path| {
1019 %%zig_args.append("--linker-script");
1020 %%zig_args.append(path);
1021 },
1022 }
1023
1024 {
1025 var it = self.link_libs.iterator();
1026 while (true) {
1027 const entry = it.next() ?? break;
1028 %%zig_args.append("--library");
1029 %%zig_args.append(entry.key);
1030 }
1031 }
1032
1033 for (builder.rpaths.toSliceConst()) |rpath| {
1034 %%zig_args.append("-rpath");
1035 %%zig_args.append(rpath);
1036 }
1037
1038 for (builder.lib_paths.toSliceConst()) |lib_path| {
1039 %%zig_args.append("--library-path");
1040 %%zig_args.append(lib_path);
1041 }
1042
1043 builder.spawnChild(builder.zig_exe, zig_args.toSliceConst());
1044 }
1045};
1046
7071047pub const TestStep = struct {
7081048 step: Step,
7091049 builder: &Builder,
......@@ -803,7 +1143,7 @@ pub const CLibrary = struct {
8031143 }
8041144
8051145 pub fn initStatic(builder: &Builder, name: []const u8) -> CLibrary {
806 return init(builder, name, undefined, true);
1146 return init(builder, name, builder.version(0, 0, 0), true);
8071147 }
8081148
8091149 fn init(builder: &Builder, name: []const u8, version: &const Version, static: bool) -> CLibrary {
......@@ -931,7 +1271,7 @@ pub const CLibrary = struct {
9311271 %%cc_args.append(self.out_filename);
9321272
9331273 for (self.object_files.toSliceConst()) |object_file| {
934 %%cc_args.append(object_file);
1274 %%cc_args.append(builder.pathFromRoot(object_file));
9351275 }
9361276
9371277 builder.spawnChild(cc, cc_args.toSliceConst());
test/assemble_and_link.zig created+26
......@@ -0,0 +1,26 @@
1const tests = @import("tests.zig");
2
3pub fn addCases(cases: &tests.CompareOutputContext) {
4 if (@compileVar("os") == Os.linux and @compileVar("arch") == Arch.x86_64) {
5 cases.addAsm("hello world linux x86_64",
6 \\.text
7 \\.globl _start
8 \\
9 \\_start:
10 \\ mov rax, 1
11 \\ mov rdi, 1
12 \\ lea rsi, msg
13 \\ mov rdx, 14
14 \\ syscall
15 \\
16 \\ mov rax, 60
17 \\ mov rdi, 0
18 \\ syscall
19 \\
20 \\.data
21 \\
22 \\msg:
23 \\ .ascii "Hello, world!\n"
24 , "Hello, world!\n");
25 }
26}
test/build_examples.zig+2-54
......@@ -1,60 +1,8 @@
1const std = @import("std");
2const build = std.build;
3const mem = std.mem;
4const fmt = std.fmt;
5
6pub fn addBuildExampleTests(b: &build.Builder, test_filter: ?[]const u8) -> &build.Step {
7 const cases = %%b.allocator.create(BuildExamplesContext);
8 *cases = BuildExamplesContext {
9 .b = b,
10 .step = b.step("test-build-examples", "Build the examples"),
11 .test_index = 0,
12 .test_filter = test_filter,
13 };
1const tests = @import("tests.zig");
142
3pub fn addCases(cases: &tests.BuildExamplesContext) {
154 cases.add("example/hello_world/hello.zig");
165 cases.addC("example/hello_world/hello_libc.zig");
176 cases.add("example/cat/main.zig");
187 cases.add("example/guess_number/main.zig");
19
20 return cases.step;
218}
22
23const BuildExamplesContext = struct {
24 b: &build.Builder,
25 step: &build.Step,
26 test_index: usize,
27 test_filter: ?[]const u8,
28
29 pub fn addC(self: &BuildExamplesContext, root_src: []const u8) {
30 self.addAllArgs(root_src, true);
31 }
32
33 pub fn add(self: &BuildExamplesContext, root_src: []const u8) {
34 self.addAllArgs(root_src, false);
35 }
36
37 pub fn addAllArgs(self: &BuildExamplesContext, root_src: []const u8, link_libc: bool) {
38 const b = self.b;
39
40 for ([]bool{false, true}) |release| {
41 const annotated_case_name = %%fmt.allocPrint(self.b.allocator, "build {} ({})",
42 root_src, if (release) "release" else "debug");
43 if (const filter ?= self.test_filter) {
44 if (mem.indexOf(u8, annotated_case_name, filter) == null)
45 continue;
46 }
47
48 const exe = b.addExecutable("test", root_src);
49 exe.setRelease(release);
50 if (link_libc) {
51 exe.linkLibrary("c");
52 }
53
54 const log_step = b.addLog("PASS {}\n", annotated_case_name);
55 log_step.step.dependOn(&exe.step);
56
57 self.step.dependOn(&log_step.step);
58 }
59 }
60};
test/compare_output.zig+3-186
......@@ -1,26 +1,7 @@
1const std = @import("std");
2const debug = std.debug;
3const build = std.build;
4const os = std.os;
5const StdIo = os.ChildProcess.StdIo;
6const Term = os.ChildProcess.Term;
7const Buffer0 = std.cstr.Buffer0;
8const io = std.io;
9const mem = std.mem;
10const fmt = std.fmt;
11const List = std.list.List;
12
13error TestFailed;
14
15pub fn addCompareOutputTests(b: &build.Builder, test_filter: ?[]const u8) -> &build.Step {
16 const cases = %%b.allocator.create(CompareOutputContext);
17 *cases = CompareOutputContext {
18 .b = b,
19 .compare_output_tests = b.step("test-compare-output", "Run the compare output tests"),
20 .test_index = 0,
21 .test_filter = test_filter,
22 };
1const os = @import("std").os;
2const tests = @import("tests.zig");
233
4pub fn addCases(cases: &tests.CompareOutputContext) {
245 cases.addC("hello world with libc",
256 \\const c = @cImport(@cInclude("stdio.h"));
267 \\export fn main(argc: c_int, argv: &&u8) -> c_int {
......@@ -420,168 +401,4 @@ pub fn addCompareOutputTests(b: &build.Builder, test_filter: ?[]const u8) -> &bu
420401
421402 tc
422403 });
423
424 return cases.compare_output_tests;
425404}
426
427const CompareOutputContext = struct {
428 b: &build.Builder,
429 compare_output_tests: &build.Step,
430 test_index: usize,
431 test_filter: ?[]const u8,
432
433 const TestCase = struct {
434 name: []const u8,
435 sources: List(SourceFile),
436 expected_output: []const u8,
437 link_libc: bool,
438
439 const SourceFile = struct {
440 filename: []const u8,
441 source: []const u8,
442 };
443
444 pub fn addSourceFile(self: &TestCase, filename: []const u8, source: []const u8) {
445 %%self.sources.append(SourceFile {
446 .filename = filename,
447 .source = source,
448 });
449 }
450 };
451
452 pub fn create(self: &CompareOutputContext, name: []const u8, source: []const u8,
453 expected_output: []const u8) -> TestCase
454 {
455 var tc = TestCase {
456 .name = name,
457 .sources = List(TestCase.SourceFile).init(self.b.allocator),
458 .expected_output = expected_output,
459 .link_libc = false,
460 };
461 tc.addSourceFile("source.zig", source);
462 return tc;
463 }
464
465 pub fn addC(self: &CompareOutputContext, name: []const u8, source: []const u8, expected_output: []const u8) {
466 var tc = self.create(name, source, expected_output);
467 tc.link_libc = true;
468 self.addCase(tc);
469 }
470
471 pub fn add(self: &CompareOutputContext, name: []const u8, source: []const u8, expected_output: []const u8) {
472 const tc = self.create(name, source, expected_output);
473 self.addCase(tc);
474 }
475
476 pub fn addCase(self: &CompareOutputContext, case: &const TestCase) {
477 const b = self.b;
478
479 const root_src = %%os.path.join(b.allocator, "test_artifacts", case.sources.items[0].filename);
480 const exe_path = %%os.path.join(b.allocator, "test_artifacts", "test");
481
482 for ([]bool{false, true}) |release| {
483 const annotated_case_name = %%fmt.allocPrint(self.b.allocator, "{} ({})",
484 case.name, if (release) "release" else "debug");
485 if (const filter ?= self.test_filter) {
486 if (mem.indexOf(u8, annotated_case_name, filter) == null)
487 continue;
488 }
489
490 const exe = b.addExecutable("test", root_src);
491 exe.setOutputPath(exe_path);
492 exe.setRelease(release);
493 if (case.link_libc) {
494 exe.linkLibrary("c");
495 }
496
497 for (case.sources.toSliceConst()) |src_file| {
498 const expanded_src_path = %%os.path.join(b.allocator, "test_artifacts", src_file.filename);
499 const write_src = b.addWriteFile(expanded_src_path, src_file.source);
500 exe.step.dependOn(&write_src.step);
501 }
502
503 const run_and_cmp_output = RunCompareOutputStep.create(self, exe_path, annotated_case_name,
504 case.expected_output);
505 run_and_cmp_output.step.dependOn(&exe.step);
506
507 self.compare_output_tests.dependOn(&run_and_cmp_output.step);
508 }
509 }
510};
511
512const RunCompareOutputStep = struct {
513 step: build.Step,
514 context: &CompareOutputContext,
515 exe_path: []const u8,
516 name: []const u8,
517 expected_output: []const u8,
518 test_index: usize,
519
520 pub fn create(context: &CompareOutputContext, exe_path: []const u8,
521 name: []const u8, expected_output: []const u8) -> &RunCompareOutputStep
522 {
523 const allocator = context.b.allocator;
524 const ptr = %%allocator.create(RunCompareOutputStep);
525 *ptr = RunCompareOutputStep {
526 .context = context,
527 .exe_path = exe_path,
528 .name = name,
529 .expected_output = expected_output,
530 .test_index = context.test_index,
531 .step = build.Step.init("RunCompareOutput", allocator, make),
532 };
533 context.test_index += 1;
534 return ptr;
535 }
536
537 fn make(step: &build.Step) -> %void {
538 const self = @fieldParentPtr(RunCompareOutputStep, "step", step);
539 const b = self.context.b;
540
541 const full_exe_path = b.pathFromRoot(self.exe_path);
542
543 %%io.stderr.printf("Test {}/{} {}...", self.test_index+1, self.context.test_index, self.name);
544
545 var child = os.ChildProcess.spawn(full_exe_path, [][]u8{}, &b.env_map,
546 StdIo.Ignore, StdIo.Pipe, StdIo.Pipe, b.allocator) %% |err|
547 {
548 debug.panic("Unable to spawn {}: {}\n", full_exe_path, @errorName(err));
549 };
550
551 const term = child.wait() %% |err| {
552 debug.panic("Unable to spawn {}: {}\n", full_exe_path, @errorName(err));
553 };
554 switch (term) {
555 Term.Clean => |code| {
556 if (code != 0) {
557 %%io.stderr.printf("Process {} exited with error code {}\n", full_exe_path, code);
558 return error.TestFailed;
559 }
560 },
561 else => {
562 %%io.stderr.printf("Process {} terminated unexpectedly\n", full_exe_path);
563 return error.TestFailed;
564 },
565 };
566
567 var stdout = %%Buffer0.initEmpty(b.allocator);
568 var stderr = %%Buffer0.initEmpty(b.allocator);
569
570 %%(??child.stdout).readAll(&stdout);
571 %%(??child.stderr).readAll(&stderr);
572
573 if (!mem.eql(u8, self.expected_output, stdout.toSliceConst())) {
574 %%io.stderr.printf(
575 \\
576 \\========= Expected this output: =========
577 \\{}
578 \\================================================
579 \\{}
580 \\
581 , self.expected_output, stdout.toSliceConst());
582 return error.TestFailed;
583 }
584 %%io.stderr.printf("OK\n");
585 }
586};
587
test/compile_errors.zig+2-237
......@@ -1,26 +1,6 @@
1const std = @import("std");
2const debug = std.debug;
3const build = std.build;
4const os = std.os;
5const StdIo = os.ChildProcess.StdIo;
6const Term = os.ChildProcess.Term;
7const Buffer0 = std.cstr.Buffer0;
8const io = std.io;
9const mem = std.mem;
10const fmt = std.fmt;
11const List = std.list.List;
12
13error TestFailed;
14
15pub fn addCompileErrorTests(b: &build.Builder, test_filter: ?[]const u8) -> &build.Step {
16 const cases = %%b.allocator.create(CompileErrorContext);
17 *cases = CompileErrorContext {
18 .b = b,
19 .step = b.step("test-compile-errors", "Run the compile error tests"),
20 .test_index = 0,
21 .test_filter = test_filter,
22 };
1const tests = @import("tests.zig");
232
3pub fn addCases(cases: &tests.CompileErrorContext) {
244 cases.add("implicit semicolon - block statement",
255 \\export fn entry() {
266 \\ {}
......@@ -1594,219 +1574,4 @@ pub fn addCompileErrorTests(b: &build.Builder, test_filter: ?[]const u8) -> &bui
15941574 ,
15951575 "error: 'main' is private",
15961576 ".tmp_source.zig:1:1: note: declared here");
1597
1598
1599
1600
1601 return cases.step;
16021577}
1603
1604const CompileErrorContext = struct {
1605 b: &build.Builder,
1606 step: &build.Step,
1607 test_index: usize,
1608 test_filter: ?[]const u8,
1609
1610 const TestCase = struct {
1611 name: []const u8,
1612 sources: List(SourceFile),
1613 expected_errors: List([]const u8),
1614 link_libc: bool,
1615 is_exe: bool,
1616
1617 const SourceFile = struct {
1618 filename: []const u8,
1619 source: []const u8,
1620 };
1621
1622 pub fn addSourceFile(self: &TestCase, filename: []const u8, source: []const u8) {
1623 %%self.sources.append(SourceFile {
1624 .filename = filename,
1625 .source = source,
1626 });
1627 }
1628
1629 pub fn addExpectedError(self: &TestCase, text: []const u8) {
1630 %%self.expected_errors.append(text);
1631 }
1632 };
1633
1634 const CompileCmpOutputStep = struct {
1635 step: build.Step,
1636 context: &CompileErrorContext,
1637 name: []const u8,
1638 test_index: usize,
1639 case: &const TestCase,
1640 release: bool,
1641
1642 pub fn create(context: &CompileErrorContext, name: []const u8,
1643 case: &const TestCase, release: bool) -> &CompileCmpOutputStep
1644 {
1645 const allocator = context.b.allocator;
1646 const ptr = %%allocator.create(CompileCmpOutputStep);
1647 *ptr = CompileCmpOutputStep {
1648 .step = build.Step.init("CompileCmpOutput", allocator, make),
1649 .context = context,
1650 .name = name,
1651 .test_index = context.test_index,
1652 .case = case,
1653 .release = release,
1654 };
1655 context.test_index += 1;
1656 return ptr;
1657 }
1658
1659 fn make(step: &build.Step) -> %void {
1660 const self = @fieldParentPtr(CompileCmpOutputStep, "step", step);
1661 const b = self.context.b;
1662
1663 const root_src = %%os.path.join(b.allocator, "test_artifacts", self.case.sources.items[0].filename);
1664 const obj_path = %%os.path.join(b.allocator, "test_artifacts", "test.o");
1665
1666 var zig_args = List([]const u8).init(b.allocator);
1667 %%zig_args.append(if (self.case.is_exe) "build_exe" else "build_obj");
1668 %%zig_args.append(b.pathFromRoot(root_src));
1669
1670 %%zig_args.append("--name");
1671 %%zig_args.append("test");
1672
1673 %%zig_args.append("--output");
1674 %%zig_args.append(b.pathFromRoot(obj_path));
1675
1676 if (self.release) {
1677 %%zig_args.append("--release");
1678 }
1679
1680 %%io.stderr.printf("Test {}/{} {}...", self.test_index+1, self.context.test_index, self.name);
1681
1682 if (b.verbose) {
1683 printInvocation(b.zig_exe, zig_args.toSliceConst());
1684 }
1685
1686 var child = os.ChildProcess.spawn(b.zig_exe, zig_args.toSliceConst(), &b.env_map,
1687 StdIo.Ignore, StdIo.Pipe, StdIo.Pipe, b.allocator) %% |err|
1688 {
1689 debug.panic("Unable to spawn {}: {}\n", b.zig_exe, @errorName(err));
1690 };
1691
1692 const term = child.wait() %% |err| {
1693 debug.panic("Unable to spawn {}: {}\n", b.zig_exe, @errorName(err));
1694 };
1695 switch (term) {
1696 Term.Clean => |code| {
1697 if (code == 0) {
1698 %%io.stderr.printf("Compilation incorrectly succeeded\n");
1699 return error.TestFailed;
1700 }
1701 },
1702 else => {
1703 %%io.stderr.printf("Process {} terminated unexpectedly\n", b.zig_exe);
1704 return error.TestFailed;
1705 },
1706 };
1707
1708 var stdout_buf = %%Buffer0.initEmpty(b.allocator);
1709 var stderr_buf = %%Buffer0.initEmpty(b.allocator);
1710
1711 %%(??child.stdout).readAll(&stdout_buf);
1712 %%(??child.stderr).readAll(&stderr_buf);
1713
1714 const stdout = stdout_buf.toSliceConst();
1715 const stderr = stderr_buf.toSliceConst();
1716
1717 if (stdout.len != 0) {
1718 %%io.stderr.printf(
1719 \\
1720 \\Expected empty stdout, instead found:
1721 \\================================================
1722 \\{}
1723 \\================================================
1724 \\
1725 , stdout);
1726 return error.TestFailed;
1727 }
1728
1729 for (self.case.expected_errors.toSliceConst()) |expected_error| {
1730 if (mem.indexOf(u8, stderr, expected_error) == null) {
1731 %%io.stderr.printf(
1732 \\
1733 \\========= Expected this compile error: =========
1734 \\{}
1735 \\================================================
1736 \\{}
1737 \\
1738 , expected_error, stderr);
1739 return error.TestFailed;
1740 }
1741 }
1742 %%io.stderr.printf("OK\n");
1743 }
1744 };
1745
1746 fn printInvocation(exe_path: []const u8, args: []const []const u8) {
1747 %%io.stderr.printf("{}", exe_path);
1748 for (args) |arg| {
1749 %%io.stderr.printf(" {}", arg);
1750 }
1751 %%io.stderr.printf("\n");
1752 }
1753
1754 pub fn create(self: &CompileErrorContext, name: []const u8, source: []const u8,
1755 expected_lines: ...) -> &TestCase
1756 {
1757 const tc = %%self.b.allocator.create(TestCase);
1758 *tc = TestCase {
1759 .name = name,
1760 .sources = List(TestCase.SourceFile).init(self.b.allocator),
1761 .expected_errors = List([]const u8).init(self.b.allocator),
1762 .link_libc = false,
1763 .is_exe = false,
1764 };
1765 tc.addSourceFile(".tmp_source.zig", source);
1766 comptime var arg_i = 0;
1767 inline while (arg_i < expected_lines.len; arg_i += 1) {
1768 // TODO mem.dupe is because of issue #336
1769 tc.addExpectedError(%%mem.dupe(self.b.allocator, u8, expected_lines[arg_i]));
1770 }
1771 return tc;
1772 }
1773
1774 pub fn addC(self: &CompileErrorContext, name: []const u8, source: []const u8, expected_lines: ...) {
1775 var tc = self.create(name, source, expected_lines);
1776 tc.link_libc = true;
1777 self.addCase(tc);
1778 }
1779
1780 pub fn addExe(self: &CompileErrorContext, name: []const u8, source: []const u8, expected_lines: ...) {
1781 var tc = self.create(name, source, expected_lines);
1782 tc.is_exe = true;
1783 self.addCase(tc);
1784 }
1785
1786 pub fn add(self: &CompileErrorContext, name: []const u8, source: []const u8, expected_lines: ...) {
1787 const tc = self.create(name, source, expected_lines);
1788 self.addCase(tc);
1789 }
1790
1791 pub fn addCase(self: &CompileErrorContext, case: &const TestCase) {
1792 const b = self.b;
1793
1794 for ([]bool{false, true}) |release| {
1795 const annotated_case_name = %%fmt.allocPrint(self.b.allocator, "{} ({})",
1796 case.name, if (release) "release" else "debug");
1797 if (const filter ?= self.test_filter) {
1798 if (mem.indexOf(u8, annotated_case_name, filter) == null)
1799 continue;
1800 }
1801
1802 const compile_and_cmp_errors = CompileCmpOutputStep.create(self, annotated_case_name, case, release);
1803 self.step.dependOn(&compile_and_cmp_errors.step);
1804
1805 for (case.sources.toSliceConst()) |src_file| {
1806 const expanded_src_path = %%os.path.join(b.allocator, "test_artifacts", src_file.filename);
1807 const write_src = b.addWriteFile(expanded_src_path, src_file.source);
1808 compile_and_cmp_errors.step.dependOn(&write_src.step);
1809 }
1810 }
1811 }
1812};
test/run_tests.cpp-59
......@@ -58,37 +58,6 @@ static const char *zig_exe = "./zig";
5858#define NL "\n"
5959#endif
6060
61static TestCase *add_asm_case(const char *case_name, const char *source, const char *output) {
62 TestCase *test_case = allocate<TestCase>(1);
63 test_case->case_name = case_name;
64 test_case->output = output;
65 test_case->special = TestSpecialLinkStep;
66
67 test_case->source_files.resize(1);
68 test_case->source_files.at(0).relative_path = ".tmp_source.s";
69 test_case->source_files.at(0).source_code = source;
70
71 test_case->compiler_args.append("asm");
72 test_case->compiler_args.append(".tmp_source.s");
73 test_case->compiler_args.append("--name");
74 test_case->compiler_args.append("test");
75 test_case->compiler_args.append("--color");
76 test_case->compiler_args.append("on");
77
78 test_case->linker_args.append("link_exe");
79 test_case->linker_args.append("test.o");
80 test_case->linker_args.append("--name");
81 test_case->linker_args.append("test");
82 test_case->linker_args.append("--output");
83 test_case->linker_args.append(tmp_exe_path);
84 test_case->linker_args.append("--color");
85 test_case->linker_args.append("on");
86
87 test_cases.append(test_case);
88
89 return test_case;
90}
91
9261static void add_debug_safety_case(const char *case_name, const char *source) {
9362 TestCase *test_case = allocate<TestCase>(1);
9463 test_case->is_debug_safety = true;
......@@ -566,33 +535,6 @@ struct comptime {
566535 R"(pub const FOO_CHAR = 63;)");
567536}
568537
569static void add_asm_tests(void) {
570#if defined(ZIG_OS_LINUX) && defined(ZIG_ARCH_X86_64)
571 add_asm_case("assemble and link hello world linux x86_64", R"SOURCE(
572.text
573.globl _start
574
575_start:
576 mov rax, 1
577 mov rdi, 1
578 lea rsi, msg
579 mov rdx, 14
580 syscall
581
582 mov rax, 60
583 mov rdi, 0
584 syscall
585
586.data
587
588msg:
589 .ascii "Hello, world!\n"
590 )SOURCE", "Hello, world!\n");
591
592#endif
593}
594
595
596538static void print_compiler_invocation(TestCase *test_case) {
597539 printf("%s", zig_exe);
598540 for (size_t i = 0; i < test_case->compiler_args.length; i += 1) {
......@@ -800,7 +742,6 @@ int main(int argc, char **argv) {
800742 }
801743 add_debug_safety_test_cases();
802744 add_parseh_test_cases();
803 add_asm_tests();
804745 run_all_tests(grep_text);
805746 cleanup();
806747}
test/tests.zig+528-3
......@@ -1,3 +1,528 @@
1pub const addCompareOutputTests = @import("compare_output.zig").addCompareOutputTests;
2pub const addBuildExampleTests = @import("build_examples.zig").addBuildExampleTests;
3pub const addCompileErrorTests = @import("compile_errors.zig").addCompileErrorTests;
1const std = @import("std");
2const debug = std.debug;
3const build = std.build;
4const os = std.os;
5const StdIo = os.ChildProcess.StdIo;
6const Term = os.ChildProcess.Term;
7const Buffer0 = std.cstr.Buffer0;
8const io = std.io;
9const mem = std.mem;
10const fmt = std.fmt;
11const List = std.list.List;
12
13error TestFailed;
14
15pub const compare_output = @import("compare_output.zig");
16pub const build_examples = @import("build_examples.zig");
17pub const compile_errors = @import("compile_errors.zig");
18pub const assemble_and_link = @import("assemble_and_link.zig");
19
20pub fn addCompareOutputTests(b: &build.Builder, test_filter: ?[]const u8) -> &build.Step {
21 const cases = %%b.allocator.create(CompareOutputContext);
22 *cases = CompareOutputContext {
23 .b = b,
24 .step = b.step("test-compare-output", "Run the compare output tests"),
25 .test_index = 0,
26 .test_filter = test_filter,
27 };
28
29 compare_output.addCases(cases);
30
31 return cases.step;
32}
33
34pub fn addCompileErrorTests(b: &build.Builder, test_filter: ?[]const u8) -> &build.Step {
35 const cases = %%b.allocator.create(CompileErrorContext);
36 *cases = CompileErrorContext {
37 .b = b,
38 .step = b.step("test-compile-errors", "Run the compile error tests"),
39 .test_index = 0,
40 .test_filter = test_filter,
41 };
42
43 compile_errors.addCases(cases);
44
45 return cases.step;
46}
47
48pub fn addBuildExampleTests(b: &build.Builder, test_filter: ?[]const u8) -> &build.Step {
49 const cases = %%b.allocator.create(BuildExamplesContext);
50 *cases = BuildExamplesContext {
51 .b = b,
52 .step = b.step("test-build-examples", "Build the examples"),
53 .test_index = 0,
54 .test_filter = test_filter,
55 };
56
57 build_examples.addCases(cases);
58
59 return cases.step;
60}
61
62pub fn addAssembleAndLinkTests(b: &build.Builder, test_filter: ?[]const u8) -> &build.Step {
63 const cases = %%b.allocator.create(CompareOutputContext);
64 *cases = CompareOutputContext {
65 .b = b,
66 .step = b.step("test-asm-link", "Run the assemble and link tests"),
67 .test_index = 0,
68 .test_filter = test_filter,
69 };
70
71 assemble_and_link.addCases(cases);
72
73 return cases.step;
74}
75
76pub const CompareOutputContext = struct {
77 b: &build.Builder,
78 step: &build.Step,
79 test_index: usize,
80 test_filter: ?[]const u8,
81
82 const TestCase = struct {
83 name: []const u8,
84 sources: List(SourceFile),
85 expected_output: []const u8,
86 link_libc: bool,
87 is_asm: bool,
88
89 const SourceFile = struct {
90 filename: []const u8,
91 source: []const u8,
92 };
93
94 pub fn addSourceFile(self: &TestCase, filename: []const u8, source: []const u8) {
95 %%self.sources.append(SourceFile {
96 .filename = filename,
97 .source = source,
98 });
99 }
100 };
101
102 const RunCompareOutputStep = struct {
103 step: build.Step,
104 context: &CompareOutputContext,
105 exe_path: []const u8,
106 name: []const u8,
107 expected_output: []const u8,
108 test_index: usize,
109
110 pub fn create(context: &CompareOutputContext, exe_path: []const u8,
111 name: []const u8, expected_output: []const u8) -> &RunCompareOutputStep
112 {
113 const allocator = context.b.allocator;
114 const ptr = %%allocator.create(RunCompareOutputStep);
115 *ptr = RunCompareOutputStep {
116 .context = context,
117 .exe_path = exe_path,
118 .name = name,
119 .expected_output = expected_output,
120 .test_index = context.test_index,
121 .step = build.Step.init("RunCompareOutput", allocator, make),
122 };
123 context.test_index += 1;
124 return ptr;
125 }
126
127 fn make(step: &build.Step) -> %void {
128 const self = @fieldParentPtr(RunCompareOutputStep, "step", step);
129 const b = self.context.b;
130
131 const full_exe_path = b.pathFromRoot(self.exe_path);
132
133 %%io.stderr.printf("Test {}/{} {}...", self.test_index+1, self.context.test_index, self.name);
134
135 var child = os.ChildProcess.spawn(full_exe_path, [][]u8{}, &b.env_map,
136 StdIo.Ignore, StdIo.Pipe, StdIo.Pipe, b.allocator) %% |err|
137 {
138 debug.panic("Unable to spawn {}: {}\n", full_exe_path, @errorName(err));
139 };
140
141 const term = child.wait() %% |err| {
142 debug.panic("Unable to spawn {}: {}\n", full_exe_path, @errorName(err));
143 };
144 switch (term) {
145 Term.Clean => |code| {
146 if (code != 0) {
147 %%io.stderr.printf("Process {} exited with error code {}\n", full_exe_path, code);
148 return error.TestFailed;
149 }
150 },
151 else => {
152 %%io.stderr.printf("Process {} terminated unexpectedly\n", full_exe_path);
153 return error.TestFailed;
154 },
155 };
156
157 var stdout = %%Buffer0.initEmpty(b.allocator);
158 var stderr = %%Buffer0.initEmpty(b.allocator);
159
160 %%(??child.stdout).readAll(&stdout);
161 %%(??child.stderr).readAll(&stderr);
162
163 if (!mem.eql(u8, self.expected_output, stdout.toSliceConst())) {
164 %%io.stderr.printf(
165 \\
166 \\========= Expected this output: =========
167 \\{}
168 \\================================================
169 \\{}
170 \\
171 , self.expected_output, stdout.toSliceConst());
172 return error.TestFailed;
173 }
174 %%io.stderr.printf("OK\n");
175 }
176 };
177
178 pub fn createExtra(self: &CompareOutputContext, name: []const u8, source: []const u8,
179 expected_output: []const u8, is_asm: bool) -> TestCase
180 {
181 var tc = TestCase {
182 .name = name,
183 .sources = List(TestCase.SourceFile).init(self.b.allocator),
184 .expected_output = expected_output,
185 .link_libc = false,
186 .is_asm = is_asm,
187 };
188 const root_src_name = if (is_asm) "source.s" else "source.zig";
189 tc.addSourceFile(root_src_name, source);
190 return tc;
191 }
192
193 pub fn create(self: &CompareOutputContext, name: []const u8, source: []const u8,
194 expected_output: []const u8) -> TestCase
195 {
196 return createExtra(self, name, source, expected_output, false);
197 }
198
199 pub fn addC(self: &CompareOutputContext, name: []const u8, source: []const u8, expected_output: []const u8) {
200 var tc = self.create(name, source, expected_output);
201 tc.link_libc = true;
202 self.addCase(tc);
203 }
204
205 pub fn add(self: &CompareOutputContext, name: []const u8, source: []const u8, expected_output: []const u8) {
206 const tc = self.create(name, source, expected_output);
207 self.addCase(tc);
208 }
209
210 pub fn addAsm(self: &CompareOutputContext, name: []const u8, source: []const u8, expected_output: []const u8) {
211 const tc = self.createExtra(name, source, expected_output, true);
212 self.addCase(tc);
213 }
214
215 pub fn addCase(self: &CompareOutputContext, case: &const TestCase) {
216 const b = self.b;
217
218 const root_src = %%os.path.join(b.allocator, "test_artifacts", case.sources.items[0].filename);
219 const exe_path = %%os.path.join(b.allocator, "test_artifacts", "test");
220
221 if (case.is_asm) {
222 const obj_path = %%os.path.join(b.allocator, "test_artifacts", "test.o");
223 const annotated_case_name = %%fmt.allocPrint(self.b.allocator, "assemble-and-link {}", case.name);
224 if (const filter ?= self.test_filter) {
225 if (mem.indexOf(u8, annotated_case_name, filter) == null)
226 return;
227 }
228
229 const obj = b.addAssemble("test", root_src);
230 obj.setOutputPath(obj_path);
231
232 for (case.sources.toSliceConst()) |src_file| {
233 const expanded_src_path = %%os.path.join(b.allocator, "test_artifacts", src_file.filename);
234 const write_src = b.addWriteFile(expanded_src_path, src_file.source);
235 obj.step.dependOn(&write_src.step);
236 }
237
238 const exe = b.addLinkExecutable("test");
239 exe.step.dependOn(&obj.step);
240 exe.addObjectFile(obj_path);
241 exe.setOutputPath(exe_path);
242
243 const run_and_cmp_output = RunCompareOutputStep.create(self, exe_path, annotated_case_name,
244 case.expected_output);
245 run_and_cmp_output.step.dependOn(&exe.step);
246
247 self.step.dependOn(&run_and_cmp_output.step);
248 } else {
249 for ([]bool{false, true}) |release| {
250 const annotated_case_name = %%fmt.allocPrint(self.b.allocator, "{} ({})",
251 case.name, if (release) "release" else "debug");
252 if (const filter ?= self.test_filter) {
253 if (mem.indexOf(u8, annotated_case_name, filter) == null)
254 continue;
255 }
256
257 const exe = b.addExecutable("test", root_src);
258 exe.setOutputPath(exe_path);
259 exe.setRelease(release);
260 if (case.link_libc) {
261 exe.linkLibrary("c");
262 }
263
264 for (case.sources.toSliceConst()) |src_file| {
265 const expanded_src_path = %%os.path.join(b.allocator, "test_artifacts", src_file.filename);
266 const write_src = b.addWriteFile(expanded_src_path, src_file.source);
267 exe.step.dependOn(&write_src.step);
268 }
269
270 const run_and_cmp_output = RunCompareOutputStep.create(self, exe_path, annotated_case_name,
271 case.expected_output);
272 run_and_cmp_output.step.dependOn(&exe.step);
273
274 self.step.dependOn(&run_and_cmp_output.step);
275 }
276 };
277
278 }
279};
280
281pub const CompileErrorContext = struct {
282 b: &build.Builder,
283 step: &build.Step,
284 test_index: usize,
285 test_filter: ?[]const u8,
286
287 const TestCase = struct {
288 name: []const u8,
289 sources: List(SourceFile),
290 expected_errors: List([]const u8),
291 link_libc: bool,
292 is_exe: bool,
293
294 const SourceFile = struct {
295 filename: []const u8,
296 source: []const u8,
297 };
298
299 pub fn addSourceFile(self: &TestCase, filename: []const u8, source: []const u8) {
300 %%self.sources.append(SourceFile {
301 .filename = filename,
302 .source = source,
303 });
304 }
305
306 pub fn addExpectedError(self: &TestCase, text: []const u8) {
307 %%self.expected_errors.append(text);
308 }
309 };
310
311 const CompileCmpOutputStep = struct {
312 step: build.Step,
313 context: &CompileErrorContext,
314 name: []const u8,
315 test_index: usize,
316 case: &const TestCase,
317 release: bool,
318
319 pub fn create(context: &CompileErrorContext, name: []const u8,
320 case: &const TestCase, release: bool) -> &CompileCmpOutputStep
321 {
322 const allocator = context.b.allocator;
323 const ptr = %%allocator.create(CompileCmpOutputStep);
324 *ptr = CompileCmpOutputStep {
325 .step = build.Step.init("CompileCmpOutput", allocator, make),
326 .context = context,
327 .name = name,
328 .test_index = context.test_index,
329 .case = case,
330 .release = release,
331 };
332 context.test_index += 1;
333 return ptr;
334 }
335
336 fn make(step: &build.Step) -> %void {
337 const self = @fieldParentPtr(CompileCmpOutputStep, "step", step);
338 const b = self.context.b;
339
340 const root_src = %%os.path.join(b.allocator, "test_artifacts", self.case.sources.items[0].filename);
341 const obj_path = %%os.path.join(b.allocator, "test_artifacts", "test.o");
342
343 var zig_args = List([]const u8).init(b.allocator);
344 %%zig_args.append(if (self.case.is_exe) "build_exe" else "build_obj");
345 %%zig_args.append(b.pathFromRoot(root_src));
346
347 %%zig_args.append("--name");
348 %%zig_args.append("test");
349
350 %%zig_args.append("--output");
351 %%zig_args.append(b.pathFromRoot(obj_path));
352
353 if (self.release) {
354 %%zig_args.append("--release");
355 }
356
357 %%io.stderr.printf("Test {}/{} {}...", self.test_index+1, self.context.test_index, self.name);
358
359 if (b.verbose) {
360 printInvocation(b.zig_exe, zig_args.toSliceConst());
361 }
362
363 var child = os.ChildProcess.spawn(b.zig_exe, zig_args.toSliceConst(), &b.env_map,
364 StdIo.Ignore, StdIo.Pipe, StdIo.Pipe, b.allocator) %% |err|
365 {
366 debug.panic("Unable to spawn {}: {}\n", b.zig_exe, @errorName(err));
367 };
368
369 const term = child.wait() %% |err| {
370 debug.panic("Unable to spawn {}: {}\n", b.zig_exe, @errorName(err));
371 };
372 switch (term) {
373 Term.Clean => |code| {
374 if (code == 0) {
375 %%io.stderr.printf("Compilation incorrectly succeeded\n");
376 return error.TestFailed;
377 }
378 },
379 else => {
380 %%io.stderr.printf("Process {} terminated unexpectedly\n", b.zig_exe);
381 return error.TestFailed;
382 },
383 };
384
385 var stdout_buf = %%Buffer0.initEmpty(b.allocator);
386 var stderr_buf = %%Buffer0.initEmpty(b.allocator);
387
388 %%(??child.stdout).readAll(&stdout_buf);
389 %%(??child.stderr).readAll(&stderr_buf);
390
391 const stdout = stdout_buf.toSliceConst();
392 const stderr = stderr_buf.toSliceConst();
393
394 if (stdout.len != 0) {
395 %%io.stderr.printf(
396 \\
397 \\Expected empty stdout, instead found:
398 \\================================================
399 \\{}
400 \\================================================
401 \\
402 , stdout);
403 return error.TestFailed;
404 }
405
406 for (self.case.expected_errors.toSliceConst()) |expected_error| {
407 if (mem.indexOf(u8, stderr, expected_error) == null) {
408 %%io.stderr.printf(
409 \\
410 \\========= Expected this compile error: =========
411 \\{}
412 \\================================================
413 \\{}
414 \\
415 , expected_error, stderr);
416 return error.TestFailed;
417 }
418 }
419 %%io.stderr.printf("OK\n");
420 }
421 };
422
423 fn printInvocation(exe_path: []const u8, args: []const []const u8) {
424 %%io.stderr.printf("{}", exe_path);
425 for (args) |arg| {
426 %%io.stderr.printf(" {}", arg);
427 }
428 %%io.stderr.printf("\n");
429 }
430
431 pub fn create(self: &CompileErrorContext, name: []const u8, source: []const u8,
432 expected_lines: ...) -> &TestCase
433 {
434 const tc = %%self.b.allocator.create(TestCase);
435 *tc = TestCase {
436 .name = name,
437 .sources = List(TestCase.SourceFile).init(self.b.allocator),
438 .expected_errors = List([]const u8).init(self.b.allocator),
439 .link_libc = false,
440 .is_exe = false,
441 };
442 tc.addSourceFile(".tmp_source.zig", source);
443 comptime var arg_i = 0;
444 inline while (arg_i < expected_lines.len; arg_i += 1) {
445 // TODO mem.dupe is because of issue #336
446 tc.addExpectedError(%%mem.dupe(self.b.allocator, u8, expected_lines[arg_i]));
447 }
448 return tc;
449 }
450
451 pub fn addC(self: &CompileErrorContext, name: []const u8, source: []const u8, expected_lines: ...) {
452 var tc = self.create(name, source, expected_lines);
453 tc.link_libc = true;
454 self.addCase(tc);
455 }
456
457 pub fn addExe(self: &CompileErrorContext, name: []const u8, source: []const u8, expected_lines: ...) {
458 var tc = self.create(name, source, expected_lines);
459 tc.is_exe = true;
460 self.addCase(tc);
461 }
462
463 pub fn add(self: &CompileErrorContext, name: []const u8, source: []const u8, expected_lines: ...) {
464 const tc = self.create(name, source, expected_lines);
465 self.addCase(tc);
466 }
467
468 pub fn addCase(self: &CompileErrorContext, case: &const TestCase) {
469 const b = self.b;
470
471 for ([]bool{false, true}) |release| {
472 const annotated_case_name = %%fmt.allocPrint(self.b.allocator, "compile-error {} ({})",
473 case.name, if (release) "release" else "debug");
474 if (const filter ?= self.test_filter) {
475 if (mem.indexOf(u8, annotated_case_name, filter) == null)
476 continue;
477 }
478
479 const compile_and_cmp_errors = CompileCmpOutputStep.create(self, annotated_case_name, case, release);
480 self.step.dependOn(&compile_and_cmp_errors.step);
481
482 for (case.sources.toSliceConst()) |src_file| {
483 const expanded_src_path = %%os.path.join(b.allocator, "test_artifacts", src_file.filename);
484 const write_src = b.addWriteFile(expanded_src_path, src_file.source);
485 compile_and_cmp_errors.step.dependOn(&write_src.step);
486 }
487 }
488 }
489};
490
491pub const BuildExamplesContext = struct {
492 b: &build.Builder,
493 step: &build.Step,
494 test_index: usize,
495 test_filter: ?[]const u8,
496
497 pub fn addC(self: &BuildExamplesContext, root_src: []const u8) {
498 self.addAllArgs(root_src, true);
499 }
500
501 pub fn add(self: &BuildExamplesContext, root_src: []const u8) {
502 self.addAllArgs(root_src, false);
503 }
504
505 pub fn addAllArgs(self: &BuildExamplesContext, root_src: []const u8, link_libc: bool) {
506 const b = self.b;
507
508 for ([]bool{false, true}) |release| {
509 const annotated_case_name = %%fmt.allocPrint(self.b.allocator, "build {} ({})",
510 root_src, if (release) "release" else "debug");
511 if (const filter ?= self.test_filter) {
512 if (mem.indexOf(u8, annotated_case_name, filter) == null)
513 continue;
514 }
515
516 const exe = b.addExecutable("test", root_src);
517 exe.setRelease(release);
518 if (link_libc) {
519 exe.linkLibrary("c");
520 }
521
522 const log_step = b.addLog("PASS {}\n", annotated_case_name);
523 log_step.step.dependOn(&exe.step);
524
525 self.step.dependOn(&log_step.step);
526 }
527 }
528};