| author | |
| committer | |
| log | d1e01e43d3b2078bfb07defb693d819e99eaa6c5 |
| tree | ee5a96ae18383a89b62533c9aada1d867a485959 |
| parent | 666435195fff867417f92e1b4f8ef7e0608470ee |
11 files changed, 921 insertions(+), 541 deletions(-)
build.zig+1| ... | ... | @@ -38,4 +38,5 @@ pub fn build(b: &Builder) { |
| 38 | 38 | test_step.dependOn(tests.addCompareOutputTests(b, test_filter)); |
| 39 | 39 | test_step.dependOn(tests.addBuildExampleTests(b, test_filter)); |
| 40 | 40 | test_step.dependOn(tests.addCompileErrorTests(b, test_filter)); |
| 41 | test_step.dependOn(tests.addAssembleAndLinkTests(b, test_filter)); | |
| 41 | 42 | } |
src/link.cpp+8| ... | ... | @@ -770,6 +770,14 @@ void codegen_link(CodeGen *g, const char *out_file) { |
| 770 | 770 | if (g->want_h_file) { |
| 771 | 771 | codegen_generate_h_file(g); |
| 772 | 772 | } |
| 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 | } | |
| 773 | 781 | if (g->verbose) { |
| 774 | 782 | fprintf(stderr, "OK\n"); |
| 775 | 783 | } |
src/os.cpp+7| ... | ... | @@ -683,3 +683,10 @@ int os_delete_file(Buf *path) { |
| 683 | 683 | void os_init(void) { |
| 684 | 684 | srand((unsigned)time(NULL)); |
| 685 | 685 | } |
| 686 | ||
| 687 | int 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); |
| 55 | 55 | |
| 56 | 56 | int os_file_exists(Buf *full_path, bool *result); |
| 57 | 57 | |
| 58 | int os_rename(Buf *src_path, Buf *dest_path); | |
| 59 | ||
| 58 | 60 | #if defined(__APPLE__) |
| 59 | 61 | #define ZIG_OS_DARWIN |
| 60 | 62 | #elif defined(_WIN32) |
std/build.zig+342-2| ... | ... | @@ -17,6 +17,7 @@ error UncleanExit; |
| 17 | 17 | error InvalidStepName; |
| 18 | 18 | error DependencyLoopDetected; |
| 19 | 19 | error NoCompilerFound; |
| 20 | error NeedAnObject; | |
| 20 | 21 | |
| 21 | 22 | pub const Builder = struct { |
| 22 | 23 | uninstall_tls: TopLevelStep, |
| ... | ... | @@ -131,6 +132,30 @@ pub const Builder = struct { |
| 131 | 132 | return test_step; |
| 132 | 133 | } |
| 133 | 134 | |
| 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 | ||
| 134 | 159 | pub fn addCStaticLibrary(self: &Builder, name: []const u8) -> &CLibrary { |
| 135 | 160 | const lib = %%self.allocator.create(CLibrary); |
| 136 | 161 | *lib = CLibrary.initStatic(self, name); |
| ... | ... | @@ -546,6 +571,17 @@ const Target = enum { |
| 546 | 571 | else => ".o", |
| 547 | 572 | }; |
| 548 | 573 | } |
| 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 | } | |
| 549 | 585 | }; |
| 550 | 586 | |
| 551 | 587 | const LinkerScript = enum { |
| ... | ... | @@ -704,6 +740,310 @@ pub const Exe = struct { |
| 704 | 740 | } |
| 705 | 741 | }; |
| 706 | 742 | |
| 743 | pub 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 | ||
| 833 | pub 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 | ||
| 707 | 1047 | pub const TestStep = struct { |
| 708 | 1048 | step: Step, |
| 709 | 1049 | builder: &Builder, |
| ... | ... | @@ -803,7 +1143,7 @@ pub const CLibrary = struct { |
| 803 | 1143 | } |
| 804 | 1144 | |
| 805 | 1145 | 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); | |
| 807 | 1147 | } |
| 808 | 1148 | |
| 809 | 1149 | fn init(builder: &Builder, name: []const u8, version: &const Version, static: bool) -> CLibrary { |
| ... | ... | @@ -931,7 +1271,7 @@ pub const CLibrary = struct { |
| 931 | 1271 | %%cc_args.append(self.out_filename); |
| 932 | 1272 | |
| 933 | 1273 | for (self.object_files.toSliceConst()) |object_file| { |
| 934 | %%cc_args.append(object_file); | |
| 1274 | %%cc_args.append(builder.pathFromRoot(object_file)); | |
| 935 | 1275 | } |
| 936 | 1276 | |
| 937 | 1277 | builder.spawnChild(cc, cc_args.toSliceConst()); |
test/assemble_and_link.zig created+26| ... | ... | @@ -0,0 +1,26 @@ |
| 1 | const tests = @import("tests.zig"); | |
| 2 | ||
| 3 | pub 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 @@ |
| 1 | const std = @import("std"); | |
| 2 | const build = std.build; | |
| 3 | const mem = std.mem; | |
| 4 | const fmt = std.fmt; | |
| 5 | ||
| 6 | pub 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 | }; | |
| 1 | const tests = @import("tests.zig"); | |
| 14 | 2 | |
| 3 | pub fn addCases(cases: &tests.BuildExamplesContext) { | |
| 15 | 4 | cases.add("example/hello_world/hello.zig"); |
| 16 | 5 | cases.addC("example/hello_world/hello_libc.zig"); |
| 17 | 6 | cases.add("example/cat/main.zig"); |
| 18 | 7 | cases.add("example/guess_number/main.zig"); |
| 19 | ||
| 20 | return cases.step; | |
| 21 | 8 | } |
| 22 | ||
| 23 | const 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 @@ |
| 1 | const std = @import("std"); | |
| 2 | const debug = std.debug; | |
| 3 | const build = std.build; | |
| 4 | const os = std.os; | |
| 5 | const StdIo = os.ChildProcess.StdIo; | |
| 6 | const Term = os.ChildProcess.Term; | |
| 7 | const Buffer0 = std.cstr.Buffer0; | |
| 8 | const io = std.io; | |
| 9 | const mem = std.mem; | |
| 10 | const fmt = std.fmt; | |
| 11 | const List = std.list.List; | |
| 12 | ||
| 13 | error TestFailed; | |
| 14 | ||
| 15 | pub 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 | }; | |
| 1 | const os = @import("std").os; | |
| 2 | const tests = @import("tests.zig"); | |
| 23 | 3 | |
| 4 | pub fn addCases(cases: &tests.CompareOutputContext) { | |
| 24 | 5 | cases.addC("hello world with libc", |
| 25 | 6 | \\const c = @cImport(@cInclude("stdio.h")); |
| 26 | 7 | \\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 |
| 420 | 401 | |
| 421 | 402 | tc |
| 422 | 403 | }); |
| 423 | ||
| 424 | return cases.compare_output_tests; | |
| 425 | 404 | } |
| 426 | ||
| 427 | const 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 | ||
| 512 | const 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 @@ |
| 1 | const std = @import("std"); | |
| 2 | const debug = std.debug; | |
| 3 | const build = std.build; | |
| 4 | const os = std.os; | |
| 5 | const StdIo = os.ChildProcess.StdIo; | |
| 6 | const Term = os.ChildProcess.Term; | |
| 7 | const Buffer0 = std.cstr.Buffer0; | |
| 8 | const io = std.io; | |
| 9 | const mem = std.mem; | |
| 10 | const fmt = std.fmt; | |
| 11 | const List = std.list.List; | |
| 12 | ||
| 13 | error TestFailed; | |
| 14 | ||
| 15 | pub 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 | }; | |
| 1 | const tests = @import("tests.zig"); | |
| 23 | 2 | |
| 3 | pub fn addCases(cases: &tests.CompileErrorContext) { | |
| 24 | 4 | cases.add("implicit semicolon - block statement", |
| 25 | 5 | \\export fn entry() { |
| 26 | 6 | \\ {} |
| ... | ... | @@ -1594,219 +1574,4 @@ pub fn addCompileErrorTests(b: &build.Builder, test_filter: ?[]const u8) -> &bui |
| 1594 | 1574 | , |
| 1595 | 1575 | "error: 'main' is private", |
| 1596 | 1576 | ".tmp_source.zig:1:1: note: declared here"); |
| 1597 | ||
| 1598 | ||
| 1599 | ||
| 1600 | ||
| 1601 | return cases.step; | |
| 1602 | 1577 | } |
| 1603 | ||
| 1604 | const 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"; |
| 58 | 58 | #define NL "\n" |
| 59 | 59 | #endif |
| 60 | 60 | |
| 61 | static 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 | ||
| 92 | 61 | static void add_debug_safety_case(const char *case_name, const char *source) { |
| 93 | 62 | TestCase *test_case = allocate<TestCase>(1); |
| 94 | 63 | test_case->is_debug_safety = true; |
| ... | ... | @@ -566,33 +535,6 @@ struct comptime { |
| 566 | 535 | R"(pub const FOO_CHAR = 63;)"); |
| 567 | 536 | } |
| 568 | 537 | |
| 569 | static 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 | ||
| 588 | msg: | |
| 589 | .ascii "Hello, world!\n" | |
| 590 | )SOURCE", "Hello, world!\n"); | |
| 591 | ||
| 592 | #endif | |
| 593 | } | |
| 594 | ||
| 595 | ||
| 596 | 538 | static void print_compiler_invocation(TestCase *test_case) { |
| 597 | 539 | printf("%s", zig_exe); |
| 598 | 540 | for (size_t i = 0; i < test_case->compiler_args.length; i += 1) { |
| ... | ... | @@ -800,7 +742,6 @@ int main(int argc, char **argv) { |
| 800 | 742 | } |
| 801 | 743 | add_debug_safety_test_cases(); |
| 802 | 744 | add_parseh_test_cases(); |
| 803 | add_asm_tests(); | |
| 804 | 745 | run_all_tests(grep_text); |
| 805 | 746 | cleanup(); |
| 806 | 747 | } |
test/tests.zig+528-3| ... | ... | @@ -1,3 +1,528 @@ |
| 1 | pub const addCompareOutputTests = @import("compare_output.zig").addCompareOutputTests; | |
| 2 | pub const addBuildExampleTests = @import("build_examples.zig").addBuildExampleTests; | |
| 3 | pub const addCompileErrorTests = @import("compile_errors.zig").addCompileErrorTests; | |
| 1 | const std = @import("std"); | |
| 2 | const debug = std.debug; | |
| 3 | const build = std.build; | |
| 4 | const os = std.os; | |
| 5 | const StdIo = os.ChildProcess.StdIo; | |
| 6 | const Term = os.ChildProcess.Term; | |
| 7 | const Buffer0 = std.cstr.Buffer0; | |
| 8 | const io = std.io; | |
| 9 | const mem = std.mem; | |
| 10 | const fmt = std.fmt; | |
| 11 | const List = std.list.List; | |
| 12 | ||
| 13 | error TestFailed; | |
| 14 | ||
| 15 | pub const compare_output = @import("compare_output.zig"); | |
| 16 | pub const build_examples = @import("build_examples.zig"); | |
| 17 | pub const compile_errors = @import("compile_errors.zig"); | |
| 18 | pub const assemble_and_link = @import("assemble_and_link.zig"); | |
| 19 | ||
| 20 | pub 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 | ||
| 34 | pub 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 | ||
| 48 | pub 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 | ||
| 62 | pub 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 | ||
| 76 | pub 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 | ||
| 281 | pub 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 | ||
| 491 | pub 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 | }; |