Commit d1e01e43d3
Changed files (11)
src/link.cpp
@@ -770,6 +770,14 @@ void codegen_link(CodeGen *g, const char *out_file) {
if (g->want_h_file) {
codegen_generate_h_file(g);
}
+ if (override_out_file) {
+ assert(g->link_objects.length == 1);
+ Buf *o_file_path = g->link_objects.at(0);
+ int err;
+ if ((err = os_rename(o_file_path, &lj.out_file))) {
+ zig_panic("unable to rename object file into final output: %s", err_str(err));
+ }
+ }
if (g->verbose) {
fprintf(stderr, "OK\n");
}
src/os.cpp
@@ -683,3 +683,10 @@ int os_delete_file(Buf *path) {
void os_init(void) {
srand((unsigned)time(NULL));
}
+
+int os_rename(Buf *src_path, Buf *dest_path) {
+ if (rename(buf_ptr(src_path), buf_ptr(dest_path)) == -1) {
+ return ErrorFileSystem;
+ }
+ return 0;
+}
src/os.hpp
@@ -55,6 +55,8 @@ int os_delete_file(Buf *path);
int os_file_exists(Buf *full_path, bool *result);
+int os_rename(Buf *src_path, Buf *dest_path);
+
#if defined(__APPLE__)
#define ZIG_OS_DARWIN
#elif defined(_WIN32)
std/build.zig
@@ -17,6 +17,7 @@ error UncleanExit;
error InvalidStepName;
error DependencyLoopDetected;
error NoCompilerFound;
+error NeedAnObject;
pub const Builder = struct {
uninstall_tls: TopLevelStep,
@@ -131,6 +132,30 @@ pub const Builder = struct {
return test_step;
}
+ pub fn addAssemble(self: &Builder, name: []const u8, src: []const u8) -> &AsmStep {
+ const asm_step = %%self.allocator.create(AsmStep);
+ *asm_step = AsmStep.init(self, name, src);
+ return asm_step;
+ }
+
+ pub fn addLinkExecutable(self: &Builder, name: []const u8) -> &LinkStep {
+ const exe = %%self.allocator.create(LinkStep);
+ *exe = LinkStep.initExecutable(self, name);
+ return exe;
+ }
+
+ pub fn addLinkStaticLibrary(self: &Builder, name: []const u8) -> &LinkStep {
+ const exe = %%self.allocator.create(LinkStep);
+ *exe = LinkStep.initStaticLibrary(self, name);
+ return exe;
+ }
+
+ pub fn addLinkSharedLibrary(self: &Builder, name: []const u8, ver: &const Version) -> &LinkStep {
+ const exe = %%self.allocator.create(LinkStep);
+ *exe = LinkStep.initSharedLibrary(self, name, ver);
+ return exe;
+ }
+
pub fn addCStaticLibrary(self: &Builder, name: []const u8) -> &CLibrary {
const lib = %%self.allocator.create(CLibrary);
*lib = CLibrary.initStatic(self, name);
@@ -546,6 +571,17 @@ const Target = enum {
else => ".o",
};
}
+
+ pub fn exeFileExt(self: &const Target) -> []const u8 {
+ const target_os = switch (*self) {
+ Target.Native => @compileVar("os"),
+ Target.Cross => |t| t.os,
+ };
+ return switch (target_os) {
+ Os.windows => ".exe",
+ else => "",
+ };
+ }
};
const LinkerScript = enum {
@@ -704,6 +740,310 @@ pub const Exe = struct {
}
};
+pub const AsmStep = struct {
+ step: Step,
+ builder: &Builder,
+ name: []const u8,
+ target: Target,
+ verbose: bool,
+ release: bool,
+ output_path: ?[]const u8,
+ src_path: []const u8,
+
+ pub fn init(builder: &Builder, name: []const u8, src_path: []const u8) -> AsmStep {
+ var self = AsmStep {
+ .step = Step.init(name, builder.allocator, make),
+ .builder = builder,
+ .name = name,
+ .target = Target.Native,
+ .verbose = false,
+ .release = false,
+ .output_path = null,
+ .src_path = src_path,
+ };
+ return self;
+ }
+
+ pub fn setTarget(self: &AsmStep, target_arch: Arch, target_os: Os, target_environ: Environ) {
+ self.target = Target.Cross {
+ CrossTarget {
+ .arch = target_arch,
+ .os = target_os,
+ .environ = target_environ,
+ }
+ };
+ }
+
+ pub fn setVerbose(self: &AsmStep, value: bool) {
+ self.verbose = value;
+ }
+
+ pub fn setRelease(self: &AsmStep, value: bool) {
+ self.release = value;
+ }
+
+ pub fn setOutputPath(self: &AsmStep, value: []const u8) {
+ self.output_path = value;
+ }
+
+ fn make(step: &Step) -> %void {
+ const self = @fieldParentPtr(AsmStep, "step", step);
+ const builder = self.builder;
+
+ var zig_args = List([]const u8).init(builder.allocator);
+ defer zig_args.deinit();
+
+ %%zig_args.append("asm");
+ %%zig_args.append(builder.pathFromRoot(self.src_path));
+
+ if (self.verbose) {
+ %%zig_args.append("--verbose");
+ }
+
+ if (self.release) {
+ %%zig_args.append("--release");
+ }
+
+ if (const output_path ?= self.output_path) {
+ %%zig_args.append("--output");
+ %%zig_args.append(builder.pathFromRoot(output_path));
+ }
+
+ %%zig_args.append("--name");
+ %%zig_args.append(self.name);
+
+ switch (self.target) {
+ Target.Native => {},
+ Target.Cross => |cross_target| {
+ %%zig_args.append("--target-arch");
+ %%zig_args.append(@enumTagName(cross_target.arch));
+
+ %%zig_args.append("--target-os");
+ %%zig_args.append(@enumTagName(cross_target.os));
+
+ %%zig_args.append("--target-environ");
+ %%zig_args.append(@enumTagName(cross_target.environ));
+ },
+ }
+
+ builder.spawnChild(builder.zig_exe, zig_args.toSliceConst());
+ }
+};
+
+pub const LinkStep = struct {
+ step: Step,
+ builder: &Builder,
+ name: []const u8,
+ target: Target,
+ linker_script: LinkerScript,
+ link_libs: BufSet,
+ verbose: bool,
+ release: bool,
+ output_path: ?[]const u8,
+ object_files: List([]const u8),
+ static: bool,
+ out_filename: []const u8,
+ out_type: OutType,
+ version: Version,
+ major_only_filename: []const u8,
+ name_only_filename: []const u8,
+
+ const OutType = enum {
+ Exe,
+ Lib,
+ };
+
+ pub fn initExecutable(builder: &Builder, name: []const u8) -> LinkStep {
+ return init(builder, name, OutType.Exe, builder.version(0, 0, 0), false)
+ }
+
+ pub fn initSharedLibrary(builder: &Builder, name: []const u8, version: &const Version) -> LinkStep {
+ return init(builder, name, OutType.Lib, version, false)
+ }
+
+ pub fn initStaticLibrary(builder: &Builder, name: []const u8) -> LinkStep {
+ return init(builder, name, OutType.Lib, builder.version(0, 0, 0), true)
+ }
+
+ fn init(builder: &Builder, name: []const u8, out_type: OutType, version: &const Version, static: bool) -> LinkStep {
+ var self = LinkStep {
+ .builder = builder,
+ .verbose = false,
+ .release = false,
+ .name = name,
+ .target = Target.Native,
+ .linker_script = LinkerScript.None,
+ .link_libs = BufSet.init(builder.allocator),
+ .step = Step.init(name, builder.allocator, make),
+ .output_path = null,
+ .object_files = List([]const u8).init(builder.allocator),
+ .out_type = out_type,
+ .version = *version,
+ .static = static,
+ .out_filename = undefined,
+ .major_only_filename = undefined,
+ .name_only_filename = undefined,
+ };
+ self.computeOutFileName();
+ return self;
+ }
+
+ fn computeOutFileName(self: &LinkStep) {
+ switch (self.out_type) {
+ OutType.Exe => {
+ self.out_filename = %%fmt.allocPrint(self.builder.allocator, "{}{}",
+ self.name, self.target.exeFileExt());
+ },
+ OutType.Lib => {
+ if (self.static) {
+ self.out_filename = %%fmt.allocPrint(self.builder.allocator, "lib{}.a", self.name);
+ } else {
+ self.out_filename = %%fmt.allocPrint(self.builder.allocator, "lib{}.so.{d}.{d}.{d}",
+ self.name, self.version.major, self.version.minor, self.version.patch);
+ self.major_only_filename = %%fmt.allocPrint(self.builder.allocator,
+ "lib{}.so.{d}", self.name, self.version.major);
+ self.name_only_filename = %%fmt.allocPrint(self.builder.allocator,
+ "lib{}.so", self.name);
+ }
+ },
+ }
+ }
+
+ pub fn addObjectFile(self: &LinkStep, file: []const u8) {
+ %%self.object_files.append(file);
+ }
+
+ pub fn setTarget(self: &LinkStep, target_arch: Arch, target_os: Os, target_environ: Environ) {
+ self.target = Target.Cross {
+ CrossTarget {
+ .arch = target_arch,
+ .os = target_os,
+ .environ = target_environ,
+ }
+ };
+ self.computeOutFileName();
+ }
+
+ /// LinkStep keeps a reference to script for its lifetime or until this function
+ /// is called again.
+ pub fn setLinkerScriptContents(self: &LinkStep, script: []const u8) {
+ self.linker_script = LinkerScript.Embed { script };
+ }
+
+ pub fn setLinkerScriptPath(self: &LinkStep, path: []const u8) {
+ self.linker_script = LinkerScript.Path { path };
+ }
+
+ pub fn linkLibrary(self: &LinkStep, name: []const u8) {
+ %%self.link_libs.put(name);
+ }
+
+ pub fn setVerbose(self: &LinkStep, value: bool) {
+ self.verbose = value;
+ }
+
+ pub fn setRelease(self: &LinkStep, value: bool) {
+ self.release = value;
+ }
+
+ pub fn setOutputPath(self: &LinkStep, value: []const u8) {
+ self.output_path = value;
+ }
+
+ fn make(step: &Step) -> %void {
+ const self = @fieldParentPtr(LinkStep, "step", step);
+ const builder = self.builder;
+
+ if (self.object_files.len == 0) {
+ %%io.stderr.printf("{}: linker needs 1 or more objects to link\n", step.name);
+ return error.NeedAnObject;
+ }
+
+ var zig_args = List([]const u8).init(builder.allocator);
+ defer zig_args.deinit();
+
+ const cmd = switch (self.out_type) {
+ OutType.Exe => "link_exe",
+ OutType.Lib => "link_lib",
+ };
+ %%zig_args.append(cmd);
+
+ for (self.object_files.toSliceConst()) |object_file| {
+ %%zig_args.append(builder.pathFromRoot(object_file));
+ }
+
+ if (self.verbose) {
+ %%zig_args.append("--verbose");
+ }
+
+ if (self.release) {
+ %%zig_args.append("--release");
+ }
+
+ if (self.static) {
+ %%zig_args.append("--static");
+ }
+
+ if (const output_path ?= self.output_path) {
+ %%zig_args.append("--output");
+ %%zig_args.append(builder.pathFromRoot(output_path));
+ }
+
+ %%zig_args.append("--name");
+ %%zig_args.append(self.name);
+
+ switch (self.target) {
+ Target.Native => {},
+ Target.Cross => |cross_target| {
+ %%zig_args.append("--target-arch");
+ %%zig_args.append(@enumTagName(cross_target.arch));
+
+ %%zig_args.append("--target-os");
+ %%zig_args.append(@enumTagName(cross_target.os));
+
+ %%zig_args.append("--target-environ");
+ %%zig_args.append(@enumTagName(cross_target.environ));
+ },
+ }
+
+ switch (self.linker_script) {
+ LinkerScript.None => {},
+ LinkerScript.Embed => |script| {
+ const tmp_file_name = "linker.ld.tmp"; // TODO issue #298
+ io.writeFile(tmp_file_name, script, builder.allocator)
+ %% |err| debug.panic("unable to write linker script: {}\n", @errorName(err));
+ %%zig_args.append("--linker-script");
+ %%zig_args.append(tmp_file_name);
+ },
+ LinkerScript.Path => |path| {
+ %%zig_args.append("--linker-script");
+ %%zig_args.append(path);
+ },
+ }
+
+ {
+ var it = self.link_libs.iterator();
+ while (true) {
+ const entry = it.next() ?? break;
+ %%zig_args.append("--library");
+ %%zig_args.append(entry.key);
+ }
+ }
+
+ for (builder.rpaths.toSliceConst()) |rpath| {
+ %%zig_args.append("-rpath");
+ %%zig_args.append(rpath);
+ }
+
+ for (builder.lib_paths.toSliceConst()) |lib_path| {
+ %%zig_args.append("--library-path");
+ %%zig_args.append(lib_path);
+ }
+
+ builder.spawnChild(builder.zig_exe, zig_args.toSliceConst());
+ }
+};
+
pub const TestStep = struct {
step: Step,
builder: &Builder,
@@ -803,7 +1143,7 @@ pub const CLibrary = struct {
}
pub fn initStatic(builder: &Builder, name: []const u8) -> CLibrary {
- return init(builder, name, undefined, true);
+ return init(builder, name, builder.version(0, 0, 0), true);
}
fn init(builder: &Builder, name: []const u8, version: &const Version, static: bool) -> CLibrary {
@@ -931,7 +1271,7 @@ pub const CLibrary = struct {
%%cc_args.append(self.out_filename);
for (self.object_files.toSliceConst()) |object_file| {
- %%cc_args.append(object_file);
+ %%cc_args.append(builder.pathFromRoot(object_file));
}
builder.spawnChild(cc, cc_args.toSliceConst());
test/assemble_and_link.zig
@@ -0,0 +1,26 @@
+const tests = @import("tests.zig");
+
+pub fn addCases(cases: &tests.CompareOutputContext) {
+ if (@compileVar("os") == Os.linux and @compileVar("arch") == Arch.x86_64) {
+ cases.addAsm("hello world linux x86_64",
+ \\.text
+ \\.globl _start
+ \\
+ \\_start:
+ \\ mov rax, 1
+ \\ mov rdi, 1
+ \\ lea rsi, msg
+ \\ mov rdx, 14
+ \\ syscall
+ \\
+ \\ mov rax, 60
+ \\ mov rdi, 0
+ \\ syscall
+ \\
+ \\.data
+ \\
+ \\msg:
+ \\ .ascii "Hello, world!\n"
+ , "Hello, world!\n");
+ }
+}
test/build_examples.zig
@@ -1,60 +1,8 @@
-const std = @import("std");
-const build = std.build;
-const mem = std.mem;
-const fmt = std.fmt;
-
-pub fn addBuildExampleTests(b: &build.Builder, test_filter: ?[]const u8) -> &build.Step {
- const cases = %%b.allocator.create(BuildExamplesContext);
- *cases = BuildExamplesContext {
- .b = b,
- .step = b.step("test-build-examples", "Build the examples"),
- .test_index = 0,
- .test_filter = test_filter,
- };
+const tests = @import("tests.zig");
+pub fn addCases(cases: &tests.BuildExamplesContext) {
cases.add("example/hello_world/hello.zig");
cases.addC("example/hello_world/hello_libc.zig");
cases.add("example/cat/main.zig");
cases.add("example/guess_number/main.zig");
-
- return cases.step;
}
-
-const BuildExamplesContext = struct {
- b: &build.Builder,
- step: &build.Step,
- test_index: usize,
- test_filter: ?[]const u8,
-
- pub fn addC(self: &BuildExamplesContext, root_src: []const u8) {
- self.addAllArgs(root_src, true);
- }
-
- pub fn add(self: &BuildExamplesContext, root_src: []const u8) {
- self.addAllArgs(root_src, false);
- }
-
- pub fn addAllArgs(self: &BuildExamplesContext, root_src: []const u8, link_libc: bool) {
- const b = self.b;
-
- for ([]bool{false, true}) |release| {
- const annotated_case_name = %%fmt.allocPrint(self.b.allocator, "build {} ({})",
- root_src, if (release) "release" else "debug");
- if (const filter ?= self.test_filter) {
- if (mem.indexOf(u8, annotated_case_name, filter) == null)
- continue;
- }
-
- const exe = b.addExecutable("test", root_src);
- exe.setRelease(release);
- if (link_libc) {
- exe.linkLibrary("c");
- }
-
- const log_step = b.addLog("PASS {}\n", annotated_case_name);
- log_step.step.dependOn(&exe.step);
-
- self.step.dependOn(&log_step.step);
- }
- }
-};
test/compare_output.zig
@@ -1,26 +1,7 @@
-const std = @import("std");
-const debug = std.debug;
-const build = std.build;
-const os = std.os;
-const StdIo = os.ChildProcess.StdIo;
-const Term = os.ChildProcess.Term;
-const Buffer0 = std.cstr.Buffer0;
-const io = std.io;
-const mem = std.mem;
-const fmt = std.fmt;
-const List = std.list.List;
-
-error TestFailed;
-
-pub fn addCompareOutputTests(b: &build.Builder, test_filter: ?[]const u8) -> &build.Step {
- const cases = %%b.allocator.create(CompareOutputContext);
- *cases = CompareOutputContext {
- .b = b,
- .compare_output_tests = b.step("test-compare-output", "Run the compare output tests"),
- .test_index = 0,
- .test_filter = test_filter,
- };
+const os = @import("std").os;
+const tests = @import("tests.zig");
+pub fn addCases(cases: &tests.CompareOutputContext) {
cases.addC("hello world with libc",
\\const c = @cImport(@cInclude("stdio.h"));
\\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
tc
});
-
- return cases.compare_output_tests;
}
-
-const CompareOutputContext = struct {
- b: &build.Builder,
- compare_output_tests: &build.Step,
- test_index: usize,
- test_filter: ?[]const u8,
-
- const TestCase = struct {
- name: []const u8,
- sources: List(SourceFile),
- expected_output: []const u8,
- link_libc: bool,
-
- const SourceFile = struct {
- filename: []const u8,
- source: []const u8,
- };
-
- pub fn addSourceFile(self: &TestCase, filename: []const u8, source: []const u8) {
- %%self.sources.append(SourceFile {
- .filename = filename,
- .source = source,
- });
- }
- };
-
- pub fn create(self: &CompareOutputContext, name: []const u8, source: []const u8,
- expected_output: []const u8) -> TestCase
- {
- var tc = TestCase {
- .name = name,
- .sources = List(TestCase.SourceFile).init(self.b.allocator),
- .expected_output = expected_output,
- .link_libc = false,
- };
- tc.addSourceFile("source.zig", source);
- return tc;
- }
-
- pub fn addC(self: &CompareOutputContext, name: []const u8, source: []const u8, expected_output: []const u8) {
- var tc = self.create(name, source, expected_output);
- tc.link_libc = true;
- self.addCase(tc);
- }
-
- pub fn add(self: &CompareOutputContext, name: []const u8, source: []const u8, expected_output: []const u8) {
- const tc = self.create(name, source, expected_output);
- self.addCase(tc);
- }
-
- pub fn addCase(self: &CompareOutputContext, case: &const TestCase) {
- const b = self.b;
-
- const root_src = %%os.path.join(b.allocator, "test_artifacts", case.sources.items[0].filename);
- const exe_path = %%os.path.join(b.allocator, "test_artifacts", "test");
-
- for ([]bool{false, true}) |release| {
- const annotated_case_name = %%fmt.allocPrint(self.b.allocator, "{} ({})",
- case.name, if (release) "release" else "debug");
- if (const filter ?= self.test_filter) {
- if (mem.indexOf(u8, annotated_case_name, filter) == null)
- continue;
- }
-
- const exe = b.addExecutable("test", root_src);
- exe.setOutputPath(exe_path);
- exe.setRelease(release);
- if (case.link_libc) {
- exe.linkLibrary("c");
- }
-
- for (case.sources.toSliceConst()) |src_file| {
- const expanded_src_path = %%os.path.join(b.allocator, "test_artifacts", src_file.filename);
- const write_src = b.addWriteFile(expanded_src_path, src_file.source);
- exe.step.dependOn(&write_src.step);
- }
-
- const run_and_cmp_output = RunCompareOutputStep.create(self, exe_path, annotated_case_name,
- case.expected_output);
- run_and_cmp_output.step.dependOn(&exe.step);
-
- self.compare_output_tests.dependOn(&run_and_cmp_output.step);
- }
- }
-};
-
-const RunCompareOutputStep = struct {
- step: build.Step,
- context: &CompareOutputContext,
- exe_path: []const u8,
- name: []const u8,
- expected_output: []const u8,
- test_index: usize,
-
- pub fn create(context: &CompareOutputContext, exe_path: []const u8,
- name: []const u8, expected_output: []const u8) -> &RunCompareOutputStep
- {
- const allocator = context.b.allocator;
- const ptr = %%allocator.create(RunCompareOutputStep);
- *ptr = RunCompareOutputStep {
- .context = context,
- .exe_path = exe_path,
- .name = name,
- .expected_output = expected_output,
- .test_index = context.test_index,
- .step = build.Step.init("RunCompareOutput", allocator, make),
- };
- context.test_index += 1;
- return ptr;
- }
-
- fn make(step: &build.Step) -> %void {
- const self = @fieldParentPtr(RunCompareOutputStep, "step", step);
- const b = self.context.b;
-
- const full_exe_path = b.pathFromRoot(self.exe_path);
-
- %%io.stderr.printf("Test {}/{} {}...", self.test_index+1, self.context.test_index, self.name);
-
- var child = os.ChildProcess.spawn(full_exe_path, [][]u8{}, &b.env_map,
- StdIo.Ignore, StdIo.Pipe, StdIo.Pipe, b.allocator) %% |err|
- {
- debug.panic("Unable to spawn {}: {}\n", full_exe_path, @errorName(err));
- };
-
- const term = child.wait() %% |err| {
- debug.panic("Unable to spawn {}: {}\n", full_exe_path, @errorName(err));
- };
- switch (term) {
- Term.Clean => |code| {
- if (code != 0) {
- %%io.stderr.printf("Process {} exited with error code {}\n", full_exe_path, code);
- return error.TestFailed;
- }
- },
- else => {
- %%io.stderr.printf("Process {} terminated unexpectedly\n", full_exe_path);
- return error.TestFailed;
- },
- };
-
- var stdout = %%Buffer0.initEmpty(b.allocator);
- var stderr = %%Buffer0.initEmpty(b.allocator);
-
- %%(??child.stdout).readAll(&stdout);
- %%(??child.stderr).readAll(&stderr);
-
- if (!mem.eql(u8, self.expected_output, stdout.toSliceConst())) {
- %%io.stderr.printf(
- \\
- \\========= Expected this output: =========
- \\{}
- \\================================================
- \\{}
- \\
- , self.expected_output, stdout.toSliceConst());
- return error.TestFailed;
- }
- %%io.stderr.printf("OK\n");
- }
-};
-
test/compile_errors.zig
@@ -1,26 +1,6 @@
-const std = @import("std");
-const debug = std.debug;
-const build = std.build;
-const os = std.os;
-const StdIo = os.ChildProcess.StdIo;
-const Term = os.ChildProcess.Term;
-const Buffer0 = std.cstr.Buffer0;
-const io = std.io;
-const mem = std.mem;
-const fmt = std.fmt;
-const List = std.list.List;
-
-error TestFailed;
-
-pub fn addCompileErrorTests(b: &build.Builder, test_filter: ?[]const u8) -> &build.Step {
- const cases = %%b.allocator.create(CompileErrorContext);
- *cases = CompileErrorContext {
- .b = b,
- .step = b.step("test-compile-errors", "Run the compile error tests"),
- .test_index = 0,
- .test_filter = test_filter,
- };
+const tests = @import("tests.zig");
+pub fn addCases(cases: &tests.CompileErrorContext) {
cases.add("implicit semicolon - block statement",
\\export fn entry() {
\\ {}
@@ -1594,219 +1574,4 @@ pub fn addCompileErrorTests(b: &build.Builder, test_filter: ?[]const u8) -> &bui
,
"error: 'main' is private",
".tmp_source.zig:1:1: note: declared here");
-
-
-
-
- return cases.step;
}
-
-const CompileErrorContext = struct {
- b: &build.Builder,
- step: &build.Step,
- test_index: usize,
- test_filter: ?[]const u8,
-
- const TestCase = struct {
- name: []const u8,
- sources: List(SourceFile),
- expected_errors: List([]const u8),
- link_libc: bool,
- is_exe: bool,
-
- const SourceFile = struct {
- filename: []const u8,
- source: []const u8,
- };
-
- pub fn addSourceFile(self: &TestCase, filename: []const u8, source: []const u8) {
- %%self.sources.append(SourceFile {
- .filename = filename,
- .source = source,
- });
- }
-
- pub fn addExpectedError(self: &TestCase, text: []const u8) {
- %%self.expected_errors.append(text);
- }
- };
-
- const CompileCmpOutputStep = struct {
- step: build.Step,
- context: &CompileErrorContext,
- name: []const u8,
- test_index: usize,
- case: &const TestCase,
- release: bool,
-
- pub fn create(context: &CompileErrorContext, name: []const u8,
- case: &const TestCase, release: bool) -> &CompileCmpOutputStep
- {
- const allocator = context.b.allocator;
- const ptr = %%allocator.create(CompileCmpOutputStep);
- *ptr = CompileCmpOutputStep {
- .step = build.Step.init("CompileCmpOutput", allocator, make),
- .context = context,
- .name = name,
- .test_index = context.test_index,
- .case = case,
- .release = release,
- };
- context.test_index += 1;
- return ptr;
- }
-
- fn make(step: &build.Step) -> %void {
- const self = @fieldParentPtr(CompileCmpOutputStep, "step", step);
- const b = self.context.b;
-
- const root_src = %%os.path.join(b.allocator, "test_artifacts", self.case.sources.items[0].filename);
- const obj_path = %%os.path.join(b.allocator, "test_artifacts", "test.o");
-
- var zig_args = List([]const u8).init(b.allocator);
- %%zig_args.append(if (self.case.is_exe) "build_exe" else "build_obj");
- %%zig_args.append(b.pathFromRoot(root_src));
-
- %%zig_args.append("--name");
- %%zig_args.append("test");
-
- %%zig_args.append("--output");
- %%zig_args.append(b.pathFromRoot(obj_path));
-
- if (self.release) {
- %%zig_args.append("--release");
- }
-
- %%io.stderr.printf("Test {}/{} {}...", self.test_index+1, self.context.test_index, self.name);
-
- if (b.verbose) {
- printInvocation(b.zig_exe, zig_args.toSliceConst());
- }
-
- var child = os.ChildProcess.spawn(b.zig_exe, zig_args.toSliceConst(), &b.env_map,
- StdIo.Ignore, StdIo.Pipe, StdIo.Pipe, b.allocator) %% |err|
- {
- debug.panic("Unable to spawn {}: {}\n", b.zig_exe, @errorName(err));
- };
-
- const term = child.wait() %% |err| {
- debug.panic("Unable to spawn {}: {}\n", b.zig_exe, @errorName(err));
- };
- switch (term) {
- Term.Clean => |code| {
- if (code == 0) {
- %%io.stderr.printf("Compilation incorrectly succeeded\n");
- return error.TestFailed;
- }
- },
- else => {
- %%io.stderr.printf("Process {} terminated unexpectedly\n", b.zig_exe);
- return error.TestFailed;
- },
- };
-
- var stdout_buf = %%Buffer0.initEmpty(b.allocator);
- var stderr_buf = %%Buffer0.initEmpty(b.allocator);
-
- %%(??child.stdout).readAll(&stdout_buf);
- %%(??child.stderr).readAll(&stderr_buf);
-
- const stdout = stdout_buf.toSliceConst();
- const stderr = stderr_buf.toSliceConst();
-
- if (stdout.len != 0) {
- %%io.stderr.printf(
- \\
- \\Expected empty stdout, instead found:
- \\================================================
- \\{}
- \\================================================
- \\
- , stdout);
- return error.TestFailed;
- }
-
- for (self.case.expected_errors.toSliceConst()) |expected_error| {
- if (mem.indexOf(u8, stderr, expected_error) == null) {
- %%io.stderr.printf(
- \\
- \\========= Expected this compile error: =========
- \\{}
- \\================================================
- \\{}
- \\
- , expected_error, stderr);
- return error.TestFailed;
- }
- }
- %%io.stderr.printf("OK\n");
- }
- };
-
- fn printInvocation(exe_path: []const u8, args: []const []const u8) {
- %%io.stderr.printf("{}", exe_path);
- for (args) |arg| {
- %%io.stderr.printf(" {}", arg);
- }
- %%io.stderr.printf("\n");
- }
-
- pub fn create(self: &CompileErrorContext, name: []const u8, source: []const u8,
- expected_lines: ...) -> &TestCase
- {
- const tc = %%self.b.allocator.create(TestCase);
- *tc = TestCase {
- .name = name,
- .sources = List(TestCase.SourceFile).init(self.b.allocator),
- .expected_errors = List([]const u8).init(self.b.allocator),
- .link_libc = false,
- .is_exe = false,
- };
- tc.addSourceFile(".tmp_source.zig", source);
- comptime var arg_i = 0;
- inline while (arg_i < expected_lines.len; arg_i += 1) {
- // TODO mem.dupe is because of issue #336
- tc.addExpectedError(%%mem.dupe(self.b.allocator, u8, expected_lines[arg_i]));
- }
- return tc;
- }
-
- pub fn addC(self: &CompileErrorContext, name: []const u8, source: []const u8, expected_lines: ...) {
- var tc = self.create(name, source, expected_lines);
- tc.link_libc = true;
- self.addCase(tc);
- }
-
- pub fn addExe(self: &CompileErrorContext, name: []const u8, source: []const u8, expected_lines: ...) {
- var tc = self.create(name, source, expected_lines);
- tc.is_exe = true;
- self.addCase(tc);
- }
-
- pub fn add(self: &CompileErrorContext, name: []const u8, source: []const u8, expected_lines: ...) {
- const tc = self.create(name, source, expected_lines);
- self.addCase(tc);
- }
-
- pub fn addCase(self: &CompileErrorContext, case: &const TestCase) {
- const b = self.b;
-
- for ([]bool{false, true}) |release| {
- const annotated_case_name = %%fmt.allocPrint(self.b.allocator, "{} ({})",
- case.name, if (release) "release" else "debug");
- if (const filter ?= self.test_filter) {
- if (mem.indexOf(u8, annotated_case_name, filter) == null)
- continue;
- }
-
- const compile_and_cmp_errors = CompileCmpOutputStep.create(self, annotated_case_name, case, release);
- self.step.dependOn(&compile_and_cmp_errors.step);
-
- for (case.sources.toSliceConst()) |src_file| {
- const expanded_src_path = %%os.path.join(b.allocator, "test_artifacts", src_file.filename);
- const write_src = b.addWriteFile(expanded_src_path, src_file.source);
- compile_and_cmp_errors.step.dependOn(&write_src.step);
- }
- }
- }
-};
test/run_tests.cpp
@@ -58,37 +58,6 @@ static const char *zig_exe = "./zig";
#define NL "\n"
#endif
-static TestCase *add_asm_case(const char *case_name, const char *source, const char *output) {
- TestCase *test_case = allocate<TestCase>(1);
- test_case->case_name = case_name;
- test_case->output = output;
- test_case->special = TestSpecialLinkStep;
-
- test_case->source_files.resize(1);
- test_case->source_files.at(0).relative_path = ".tmp_source.s";
- test_case->source_files.at(0).source_code = source;
-
- test_case->compiler_args.append("asm");
- test_case->compiler_args.append(".tmp_source.s");
- test_case->compiler_args.append("--name");
- test_case->compiler_args.append("test");
- test_case->compiler_args.append("--color");
- test_case->compiler_args.append("on");
-
- test_case->linker_args.append("link_exe");
- test_case->linker_args.append("test.o");
- test_case->linker_args.append("--name");
- test_case->linker_args.append("test");
- test_case->linker_args.append("--output");
- test_case->linker_args.append(tmp_exe_path);
- test_case->linker_args.append("--color");
- test_case->linker_args.append("on");
-
- test_cases.append(test_case);
-
- return test_case;
-}
-
static void add_debug_safety_case(const char *case_name, const char *source) {
TestCase *test_case = allocate<TestCase>(1);
test_case->is_debug_safety = true;
@@ -566,33 +535,6 @@ struct comptime {
R"(pub const FOO_CHAR = 63;)");
}
-static void add_asm_tests(void) {
-#if defined(ZIG_OS_LINUX) && defined(ZIG_ARCH_X86_64)
- add_asm_case("assemble and link hello world linux x86_64", R"SOURCE(
-.text
-.globl _start
-
-_start:
- mov rax, 1
- mov rdi, 1
- lea rsi, msg
- mov rdx, 14
- syscall
-
- mov rax, 60
- mov rdi, 0
- syscall
-
-.data
-
-msg:
- .ascii "Hello, world!\n"
- )SOURCE", "Hello, world!\n");
-
-#endif
-}
-
-
static void print_compiler_invocation(TestCase *test_case) {
printf("%s", zig_exe);
for (size_t i = 0; i < test_case->compiler_args.length; i += 1) {
@@ -800,7 +742,6 @@ int main(int argc, char **argv) {
}
add_debug_safety_test_cases();
add_parseh_test_cases();
- add_asm_tests();
run_all_tests(grep_text);
cleanup();
}
test/tests.zig
@@ -1,3 +1,528 @@
-pub const addCompareOutputTests = @import("compare_output.zig").addCompareOutputTests;
-pub const addBuildExampleTests = @import("build_examples.zig").addBuildExampleTests;
-pub const addCompileErrorTests = @import("compile_errors.zig").addCompileErrorTests;
+const std = @import("std");
+const debug = std.debug;
+const build = std.build;
+const os = std.os;
+const StdIo = os.ChildProcess.StdIo;
+const Term = os.ChildProcess.Term;
+const Buffer0 = std.cstr.Buffer0;
+const io = std.io;
+const mem = std.mem;
+const fmt = std.fmt;
+const List = std.list.List;
+
+error TestFailed;
+
+pub const compare_output = @import("compare_output.zig");
+pub const build_examples = @import("build_examples.zig");
+pub const compile_errors = @import("compile_errors.zig");
+pub const assemble_and_link = @import("assemble_and_link.zig");
+
+pub fn addCompareOutputTests(b: &build.Builder, test_filter: ?[]const u8) -> &build.Step {
+ const cases = %%b.allocator.create(CompareOutputContext);
+ *cases = CompareOutputContext {
+ .b = b,
+ .step = b.step("test-compare-output", "Run the compare output tests"),
+ .test_index = 0,
+ .test_filter = test_filter,
+ };
+
+ compare_output.addCases(cases);
+
+ return cases.step;
+}
+
+pub fn addCompileErrorTests(b: &build.Builder, test_filter: ?[]const u8) -> &build.Step {
+ const cases = %%b.allocator.create(CompileErrorContext);
+ *cases = CompileErrorContext {
+ .b = b,
+ .step = b.step("test-compile-errors", "Run the compile error tests"),
+ .test_index = 0,
+ .test_filter = test_filter,
+ };
+
+ compile_errors.addCases(cases);
+
+ return cases.step;
+}
+
+pub fn addBuildExampleTests(b: &build.Builder, test_filter: ?[]const u8) -> &build.Step {
+ const cases = %%b.allocator.create(BuildExamplesContext);
+ *cases = BuildExamplesContext {
+ .b = b,
+ .step = b.step("test-build-examples", "Build the examples"),
+ .test_index = 0,
+ .test_filter = test_filter,
+ };
+
+ build_examples.addCases(cases);
+
+ return cases.step;
+}
+
+pub fn addAssembleAndLinkTests(b: &build.Builder, test_filter: ?[]const u8) -> &build.Step {
+ const cases = %%b.allocator.create(CompareOutputContext);
+ *cases = CompareOutputContext {
+ .b = b,
+ .step = b.step("test-asm-link", "Run the assemble and link tests"),
+ .test_index = 0,
+ .test_filter = test_filter,
+ };
+
+ assemble_and_link.addCases(cases);
+
+ return cases.step;
+}
+
+pub const CompareOutputContext = struct {
+ b: &build.Builder,
+ step: &build.Step,
+ test_index: usize,
+ test_filter: ?[]const u8,
+
+ const TestCase = struct {
+ name: []const u8,
+ sources: List(SourceFile),
+ expected_output: []const u8,
+ link_libc: bool,
+ is_asm: bool,
+
+ const SourceFile = struct {
+ filename: []const u8,
+ source: []const u8,
+ };
+
+ pub fn addSourceFile(self: &TestCase, filename: []const u8, source: []const u8) {
+ %%self.sources.append(SourceFile {
+ .filename = filename,
+ .source = source,
+ });
+ }
+ };
+
+ const RunCompareOutputStep = struct {
+ step: build.Step,
+ context: &CompareOutputContext,
+ exe_path: []const u8,
+ name: []const u8,
+ expected_output: []const u8,
+ test_index: usize,
+
+ pub fn create(context: &CompareOutputContext, exe_path: []const u8,
+ name: []const u8, expected_output: []const u8) -> &RunCompareOutputStep
+ {
+ const allocator = context.b.allocator;
+ const ptr = %%allocator.create(RunCompareOutputStep);
+ *ptr = RunCompareOutputStep {
+ .context = context,
+ .exe_path = exe_path,
+ .name = name,
+ .expected_output = expected_output,
+ .test_index = context.test_index,
+ .step = build.Step.init("RunCompareOutput", allocator, make),
+ };
+ context.test_index += 1;
+ return ptr;
+ }
+
+ fn make(step: &build.Step) -> %void {
+ const self = @fieldParentPtr(RunCompareOutputStep, "step", step);
+ const b = self.context.b;
+
+ const full_exe_path = b.pathFromRoot(self.exe_path);
+
+ %%io.stderr.printf("Test {}/{} {}...", self.test_index+1, self.context.test_index, self.name);
+
+ var child = os.ChildProcess.spawn(full_exe_path, [][]u8{}, &b.env_map,
+ StdIo.Ignore, StdIo.Pipe, StdIo.Pipe, b.allocator) %% |err|
+ {
+ debug.panic("Unable to spawn {}: {}\n", full_exe_path, @errorName(err));
+ };
+
+ const term = child.wait() %% |err| {
+ debug.panic("Unable to spawn {}: {}\n", full_exe_path, @errorName(err));
+ };
+ switch (term) {
+ Term.Clean => |code| {
+ if (code != 0) {
+ %%io.stderr.printf("Process {} exited with error code {}\n", full_exe_path, code);
+ return error.TestFailed;
+ }
+ },
+ else => {
+ %%io.stderr.printf("Process {} terminated unexpectedly\n", full_exe_path);
+ return error.TestFailed;
+ },
+ };
+
+ var stdout = %%Buffer0.initEmpty(b.allocator);
+ var stderr = %%Buffer0.initEmpty(b.allocator);
+
+ %%(??child.stdout).readAll(&stdout);
+ %%(??child.stderr).readAll(&stderr);
+
+ if (!mem.eql(u8, self.expected_output, stdout.toSliceConst())) {
+ %%io.stderr.printf(
+ \\
+ \\========= Expected this output: =========
+ \\{}
+ \\================================================
+ \\{}
+ \\
+ , self.expected_output, stdout.toSliceConst());
+ return error.TestFailed;
+ }
+ %%io.stderr.printf("OK\n");
+ }
+ };
+
+ pub fn createExtra(self: &CompareOutputContext, name: []const u8, source: []const u8,
+ expected_output: []const u8, is_asm: bool) -> TestCase
+ {
+ var tc = TestCase {
+ .name = name,
+ .sources = List(TestCase.SourceFile).init(self.b.allocator),
+ .expected_output = expected_output,
+ .link_libc = false,
+ .is_asm = is_asm,
+ };
+ const root_src_name = if (is_asm) "source.s" else "source.zig";
+ tc.addSourceFile(root_src_name, source);
+ return tc;
+ }
+
+ pub fn create(self: &CompareOutputContext, name: []const u8, source: []const u8,
+ expected_output: []const u8) -> TestCase
+ {
+ return createExtra(self, name, source, expected_output, false);
+ }
+
+ pub fn addC(self: &CompareOutputContext, name: []const u8, source: []const u8, expected_output: []const u8) {
+ var tc = self.create(name, source, expected_output);
+ tc.link_libc = true;
+ self.addCase(tc);
+ }
+
+ pub fn add(self: &CompareOutputContext, name: []const u8, source: []const u8, expected_output: []const u8) {
+ const tc = self.create(name, source, expected_output);
+ self.addCase(tc);
+ }
+
+ pub fn addAsm(self: &CompareOutputContext, name: []const u8, source: []const u8, expected_output: []const u8) {
+ const tc = self.createExtra(name, source, expected_output, true);
+ self.addCase(tc);
+ }
+
+ pub fn addCase(self: &CompareOutputContext, case: &const TestCase) {
+ const b = self.b;
+
+ const root_src = %%os.path.join(b.allocator, "test_artifacts", case.sources.items[0].filename);
+ const exe_path = %%os.path.join(b.allocator, "test_artifacts", "test");
+
+ if (case.is_asm) {
+ const obj_path = %%os.path.join(b.allocator, "test_artifacts", "test.o");
+ const annotated_case_name = %%fmt.allocPrint(self.b.allocator, "assemble-and-link {}", case.name);
+ if (const filter ?= self.test_filter) {
+ if (mem.indexOf(u8, annotated_case_name, filter) == null)
+ return;
+ }
+
+ const obj = b.addAssemble("test", root_src);
+ obj.setOutputPath(obj_path);
+
+ for (case.sources.toSliceConst()) |src_file| {
+ const expanded_src_path = %%os.path.join(b.allocator, "test_artifacts", src_file.filename);
+ const write_src = b.addWriteFile(expanded_src_path, src_file.source);
+ obj.step.dependOn(&write_src.step);
+ }
+
+ const exe = b.addLinkExecutable("test");
+ exe.step.dependOn(&obj.step);
+ exe.addObjectFile(obj_path);
+ exe.setOutputPath(exe_path);
+
+ const run_and_cmp_output = RunCompareOutputStep.create(self, exe_path, annotated_case_name,
+ case.expected_output);
+ run_and_cmp_output.step.dependOn(&exe.step);
+
+ self.step.dependOn(&run_and_cmp_output.step);
+ } else {
+ for ([]bool{false, true}) |release| {
+ const annotated_case_name = %%fmt.allocPrint(self.b.allocator, "{} ({})",
+ case.name, if (release) "release" else "debug");
+ if (const filter ?= self.test_filter) {
+ if (mem.indexOf(u8, annotated_case_name, filter) == null)
+ continue;
+ }
+
+ const exe = b.addExecutable("test", root_src);
+ exe.setOutputPath(exe_path);
+ exe.setRelease(release);
+ if (case.link_libc) {
+ exe.linkLibrary("c");
+ }
+
+ for (case.sources.toSliceConst()) |src_file| {
+ const expanded_src_path = %%os.path.join(b.allocator, "test_artifacts", src_file.filename);
+ const write_src = b.addWriteFile(expanded_src_path, src_file.source);
+ exe.step.dependOn(&write_src.step);
+ }
+
+ const run_and_cmp_output = RunCompareOutputStep.create(self, exe_path, annotated_case_name,
+ case.expected_output);
+ run_and_cmp_output.step.dependOn(&exe.step);
+
+ self.step.dependOn(&run_and_cmp_output.step);
+ }
+ };
+
+ }
+};
+
+pub const CompileErrorContext = struct {
+ b: &build.Builder,
+ step: &build.Step,
+ test_index: usize,
+ test_filter: ?[]const u8,
+
+ const TestCase = struct {
+ name: []const u8,
+ sources: List(SourceFile),
+ expected_errors: List([]const u8),
+ link_libc: bool,
+ is_exe: bool,
+
+ const SourceFile = struct {
+ filename: []const u8,
+ source: []const u8,
+ };
+
+ pub fn addSourceFile(self: &TestCase, filename: []const u8, source: []const u8) {
+ %%self.sources.append(SourceFile {
+ .filename = filename,
+ .source = source,
+ });
+ }
+
+ pub fn addExpectedError(self: &TestCase, text: []const u8) {
+ %%self.expected_errors.append(text);
+ }
+ };
+
+ const CompileCmpOutputStep = struct {
+ step: build.Step,
+ context: &CompileErrorContext,
+ name: []const u8,
+ test_index: usize,
+ case: &const TestCase,
+ release: bool,
+
+ pub fn create(context: &CompileErrorContext, name: []const u8,
+ case: &const TestCase, release: bool) -> &CompileCmpOutputStep
+ {
+ const allocator = context.b.allocator;
+ const ptr = %%allocator.create(CompileCmpOutputStep);
+ *ptr = CompileCmpOutputStep {
+ .step = build.Step.init("CompileCmpOutput", allocator, make),
+ .context = context,
+ .name = name,
+ .test_index = context.test_index,
+ .case = case,
+ .release = release,
+ };
+ context.test_index += 1;
+ return ptr;
+ }
+
+ fn make(step: &build.Step) -> %void {
+ const self = @fieldParentPtr(CompileCmpOutputStep, "step", step);
+ const b = self.context.b;
+
+ const root_src = %%os.path.join(b.allocator, "test_artifacts", self.case.sources.items[0].filename);
+ const obj_path = %%os.path.join(b.allocator, "test_artifacts", "test.o");
+
+ var zig_args = List([]const u8).init(b.allocator);
+ %%zig_args.append(if (self.case.is_exe) "build_exe" else "build_obj");
+ %%zig_args.append(b.pathFromRoot(root_src));
+
+ %%zig_args.append("--name");
+ %%zig_args.append("test");
+
+ %%zig_args.append("--output");
+ %%zig_args.append(b.pathFromRoot(obj_path));
+
+ if (self.release) {
+ %%zig_args.append("--release");
+ }
+
+ %%io.stderr.printf("Test {}/{} {}...", self.test_index+1, self.context.test_index, self.name);
+
+ if (b.verbose) {
+ printInvocation(b.zig_exe, zig_args.toSliceConst());
+ }
+
+ var child = os.ChildProcess.spawn(b.zig_exe, zig_args.toSliceConst(), &b.env_map,
+ StdIo.Ignore, StdIo.Pipe, StdIo.Pipe, b.allocator) %% |err|
+ {
+ debug.panic("Unable to spawn {}: {}\n", b.zig_exe, @errorName(err));
+ };
+
+ const term = child.wait() %% |err| {
+ debug.panic("Unable to spawn {}: {}\n", b.zig_exe, @errorName(err));
+ };
+ switch (term) {
+ Term.Clean => |code| {
+ if (code == 0) {
+ %%io.stderr.printf("Compilation incorrectly succeeded\n");
+ return error.TestFailed;
+ }
+ },
+ else => {
+ %%io.stderr.printf("Process {} terminated unexpectedly\n", b.zig_exe);
+ return error.TestFailed;
+ },
+ };
+
+ var stdout_buf = %%Buffer0.initEmpty(b.allocator);
+ var stderr_buf = %%Buffer0.initEmpty(b.allocator);
+
+ %%(??child.stdout).readAll(&stdout_buf);
+ %%(??child.stderr).readAll(&stderr_buf);
+
+ const stdout = stdout_buf.toSliceConst();
+ const stderr = stderr_buf.toSliceConst();
+
+ if (stdout.len != 0) {
+ %%io.stderr.printf(
+ \\
+ \\Expected empty stdout, instead found:
+ \\================================================
+ \\{}
+ \\================================================
+ \\
+ , stdout);
+ return error.TestFailed;
+ }
+
+ for (self.case.expected_errors.toSliceConst()) |expected_error| {
+ if (mem.indexOf(u8, stderr, expected_error) == null) {
+ %%io.stderr.printf(
+ \\
+ \\========= Expected this compile error: =========
+ \\{}
+ \\================================================
+ \\{}
+ \\
+ , expected_error, stderr);
+ return error.TestFailed;
+ }
+ }
+ %%io.stderr.printf("OK\n");
+ }
+ };
+
+ fn printInvocation(exe_path: []const u8, args: []const []const u8) {
+ %%io.stderr.printf("{}", exe_path);
+ for (args) |arg| {
+ %%io.stderr.printf(" {}", arg);
+ }
+ %%io.stderr.printf("\n");
+ }
+
+ pub fn create(self: &CompileErrorContext, name: []const u8, source: []const u8,
+ expected_lines: ...) -> &TestCase
+ {
+ const tc = %%self.b.allocator.create(TestCase);
+ *tc = TestCase {
+ .name = name,
+ .sources = List(TestCase.SourceFile).init(self.b.allocator),
+ .expected_errors = List([]const u8).init(self.b.allocator),
+ .link_libc = false,
+ .is_exe = false,
+ };
+ tc.addSourceFile(".tmp_source.zig", source);
+ comptime var arg_i = 0;
+ inline while (arg_i < expected_lines.len; arg_i += 1) {
+ // TODO mem.dupe is because of issue #336
+ tc.addExpectedError(%%mem.dupe(self.b.allocator, u8, expected_lines[arg_i]));
+ }
+ return tc;
+ }
+
+ pub fn addC(self: &CompileErrorContext, name: []const u8, source: []const u8, expected_lines: ...) {
+ var tc = self.create(name, source, expected_lines);
+ tc.link_libc = true;
+ self.addCase(tc);
+ }
+
+ pub fn addExe(self: &CompileErrorContext, name: []const u8, source: []const u8, expected_lines: ...) {
+ var tc = self.create(name, source, expected_lines);
+ tc.is_exe = true;
+ self.addCase(tc);
+ }
+
+ pub fn add(self: &CompileErrorContext, name: []const u8, source: []const u8, expected_lines: ...) {
+ const tc = self.create(name, source, expected_lines);
+ self.addCase(tc);
+ }
+
+ pub fn addCase(self: &CompileErrorContext, case: &const TestCase) {
+ const b = self.b;
+
+ for ([]bool{false, true}) |release| {
+ const annotated_case_name = %%fmt.allocPrint(self.b.allocator, "compile-error {} ({})",
+ case.name, if (release) "release" else "debug");
+ if (const filter ?= self.test_filter) {
+ if (mem.indexOf(u8, annotated_case_name, filter) == null)
+ continue;
+ }
+
+ const compile_and_cmp_errors = CompileCmpOutputStep.create(self, annotated_case_name, case, release);
+ self.step.dependOn(&compile_and_cmp_errors.step);
+
+ for (case.sources.toSliceConst()) |src_file| {
+ const expanded_src_path = %%os.path.join(b.allocator, "test_artifacts", src_file.filename);
+ const write_src = b.addWriteFile(expanded_src_path, src_file.source);
+ compile_and_cmp_errors.step.dependOn(&write_src.step);
+ }
+ }
+ }
+};
+
+pub const BuildExamplesContext = struct {
+ b: &build.Builder,
+ step: &build.Step,
+ test_index: usize,
+ test_filter: ?[]const u8,
+
+ pub fn addC(self: &BuildExamplesContext, root_src: []const u8) {
+ self.addAllArgs(root_src, true);
+ }
+
+ pub fn add(self: &BuildExamplesContext, root_src: []const u8) {
+ self.addAllArgs(root_src, false);
+ }
+
+ pub fn addAllArgs(self: &BuildExamplesContext, root_src: []const u8, link_libc: bool) {
+ const b = self.b;
+
+ for ([]bool{false, true}) |release| {
+ const annotated_case_name = %%fmt.allocPrint(self.b.allocator, "build {} ({})",
+ root_src, if (release) "release" else "debug");
+ if (const filter ?= self.test_filter) {
+ if (mem.indexOf(u8, annotated_case_name, filter) == null)
+ continue;
+ }
+
+ const exe = b.addExecutable("test", root_src);
+ exe.setRelease(release);
+ if (link_libc) {
+ exe.linkLibrary("c");
+ }
+
+ const log_step = b.addLog("PASS {}\n", annotated_case_name);
+ log_step.step.dependOn(&exe.step);
+
+ self.step.dependOn(&log_step.step);
+ }
+ }
+};
build.zig
@@ -38,4 +38,5 @@ pub fn build(b: &Builder) {
test_step.dependOn(tests.addCompareOutputTests(b, test_filter));
test_step.dependOn(tests.addBuildExampleTests(b, test_filter));
test_step.dependOn(tests.addCompileErrorTests(b, test_filter));
+ test_step.dependOn(tests.addAssembleAndLinkTests(b, test_filter));
}