Commit 695c8f756b
Changed files (5)
src/main.cpp
@@ -43,7 +43,6 @@ static int print_full_usage(const char *arg0, FILE *file, int return_code) {
" libc [paths_file] Display native libc paths file or validate one\n"
" run [source] [-- [args]] create executable and run immediately\n"
" translate-c [source] convert c code to zig code\n"
- " translate-c-2 [source] experimental self-hosted translate-c\n"
" targets list available compilation targets\n"
" test [source] create and run a test build\n"
" version print version number and exit\n"
test/src/run_translated_c.zig
@@ -0,0 +1,180 @@
+// This is the implementation of the test harness for running translated
+// C code. For the actual test cases, see test/run_translated_c.zig.
+const std = @import("std");
+const build = std.build;
+const ArrayList = std.ArrayList;
+const fmt = std.fmt;
+const mem = std.mem;
+const fs = std.fs;
+const warn = std.debug.warn;
+
+pub const RunTranslatedCContext = struct {
+ b: *build.Builder,
+ step: *build.Step,
+ test_index: usize,
+ test_filter: ?[]const u8,
+
+ const TestCase = struct {
+ name: []const u8,
+ sources: ArrayList(SourceFile),
+ expected_stdout: []const u8,
+ allow_warnings: bool,
+
+ const SourceFile = struct {
+ filename: []const u8,
+ source: []const u8,
+ };
+
+ pub fn addSourceFile(self: *TestCase, filename: []const u8, source: []const u8) void {
+ self.sources.append(SourceFile{
+ .filename = filename,
+ .source = source,
+ }) catch unreachable;
+ }
+ };
+
+ const DoEverythingStep = struct {
+ step: build.Step,
+ context: *RunTranslatedCContext,
+ name: []const u8,
+ case: *const TestCase,
+ test_index: usize,
+
+ pub fn create(
+ context: *RunTranslatedCContext,
+ name: []const u8,
+ case: *const TestCase,
+ ) *DoEverythingStep {
+ const allocator = context.b.allocator;
+ const ptr = allocator.create(DoEverythingStep) catch unreachable;
+ ptr.* = DoEverythingStep{
+ .context = context,
+ .name = name,
+ .case = case,
+ .test_index = context.test_index,
+ .step = build.Step.init("RunTranslatedC", allocator, make),
+ };
+ context.test_index += 1;
+ return ptr;
+ }
+
+ fn make(step: *build.Step) !void {
+ const self = @fieldParentPtr(DoEverythingStep, "step", step);
+ const b = self.context.b;
+
+ warn("Test {}/{} {}...", .{ self.test_index + 1, self.context.test_index, self.name });
+ // translate from c to zig
+ const translated_c_code = blk: {
+ var zig_args = ArrayList([]const u8).init(b.allocator);
+ defer zig_args.deinit();
+
+ const rel_c_filename = try fs.path.join(b.allocator, &[_][]const u8{
+ b.cache_root,
+ self.case.sources.toSliceConst()[0].filename,
+ });
+
+ try zig_args.append(b.zig_exe);
+ try zig_args.append("translate-c");
+ try zig_args.append("-lc");
+ try zig_args.append(b.pathFromRoot(rel_c_filename));
+
+ break :blk try b.exec(zig_args.toSliceConst());
+ };
+
+ // write stdout to a file
+
+ const translated_c_path = try fs.path.join(b.allocator,
+ &[_][]const u8{ b.cache_root, "translated_c.zig" });
+ try fs.cwd().writeFile(translated_c_path, translated_c_code);
+
+ // zig run the result
+ const run_stdout = blk: {
+ var zig_args = ArrayList([]const u8).init(b.allocator);
+ defer zig_args.deinit();
+
+ try zig_args.append(b.zig_exe);
+ try zig_args.append("-lc");
+ try zig_args.append("run");
+ try zig_args.append(translated_c_path);
+
+ break :blk try b.exec(zig_args.toSliceConst());
+ };
+ // compare stdout
+ if (!mem.eql(u8, self.case.expected_stdout, run_stdout)) {
+ warn(
+ \\
+ \\========= Expected this output: =========
+ \\{}
+ \\========= But found: ====================
+ \\{}
+ \\
+ , .{ self.case.expected_stdout, run_stdout });
+ return error.TestFailed;
+ }
+
+ warn("OK\n", .{});
+ }
+ };
+
+ pub fn create(
+ self: *RunTranslatedCContext,
+ allow_warnings: bool,
+ filename: []const u8,
+ name: []const u8,
+ source: []const u8,
+ expected_stdout: []const u8,
+ ) *TestCase {
+ const tc = self.b.allocator.create(TestCase) catch unreachable;
+ tc.* = TestCase{
+ .name = name,
+ .sources = ArrayList(TestCase.SourceFile).init(self.b.allocator),
+ .expected_stdout = expected_stdout,
+ .allow_warnings = allow_warnings,
+ };
+
+ tc.addSourceFile(filename, source);
+ return tc;
+ }
+
+ pub fn add(
+ self: *RunTranslatedCContext,
+ name: []const u8,
+ source: []const u8,
+ expected_stdout: []const u8,
+ ) void {
+ const tc = self.create(false, "source.c", name, source, expected_stdout);
+ self.addCase(tc);
+ }
+
+ pub fn addAllowWarnings(
+ self: *RunTranslatedCContext,
+ name: []const u8,
+ source: []const u8,
+ expected_stdout: []const u8,
+ ) void {
+ const tc = self.create(true, "source.c", name, source, expected_stdout);
+ self.addCase(tc);
+ }
+
+ pub fn addCase(self: *RunTranslatedCContext, case: *const TestCase) void {
+ const b = self.b;
+
+ const annotated_case_name = fmt.allocPrint(self.b.allocator, "run-translated-c {}", .{ case.name }) catch unreachable;
+ if (self.test_filter) |filter| {
+ if (mem.indexOf(u8, annotated_case_name, filter) == null) return;
+ }
+
+ const do_everything_step = DoEverythingStep.create(self, annotated_case_name, case);
+ self.step.dependOn(&do_everything_step.step);
+
+ for (case.sources.toSliceConst()) |src_file| {
+ const expanded_src_path = fs.path.join(
+ b.allocator,
+ &[_][]const u8{ b.cache_root, src_file.filename },
+ ) catch unreachable;
+ const write_src = b.addWriteFile(expanded_src_path, src_file.source);
+ do_everything_step.step.dependOn(&write_src.step);
+ }
+ }
+};
+
test/run_translated_c.zig
@@ -0,0 +1,24 @@
+const tests = @import("tests.zig");
+
+pub fn addCases(cases: *tests.RunTranslatedCContext) void {
+ cases.add("hello world",
+ \\#define _NO_CRT_STDIO_INLINE 1
+ \\#include <stdio.h>
+ \\int main(int argc, char **argv) {
+ \\ printf("hello, world!\n");
+ \\ return 0;
+ \\}
+ , "hello, world!\n");
+
+ cases.add("anon struct init",
+ \\#include <stdlib.h>
+ \\struct {int a; int b;} x = {1, 2};
+ \\int main(int argc, char **argv) {
+ \\ x.a += 2;
+ \\ x.b += 1;
+ \\ if (x.a != 3) abort();
+ \\ if (x.b != 3) abort();
+ \\ return 0;
+ \\}
+ , "");
+}
test/tests.zig
@@ -14,6 +14,7 @@ const builtin = @import("builtin");
const Mode = builtin.Mode;
const LibExeObjStep = build.LibExeObjStep;
+// Cases
const compare_output = @import("compare_output.zig");
const standalone = @import("standalone.zig");
const stack_traces = @import("stack_traces.zig");
@@ -21,8 +22,12 @@ const compile_errors = @import("compile_errors.zig");
const assemble_and_link = @import("assemble_and_link.zig");
const runtime_safety = @import("runtime_safety.zig");
const translate_c = @import("translate_c.zig");
+const run_translated_c = @import("run_translated_c.zig");
const gen_h = @import("gen_h.zig");
+// Implementations
+pub const RunTranslatedCContext = @import("src/run_translated_c.zig").RunTranslatedCContext;
+
const TestTarget = struct {
target: Target = .Native,
mode: builtin.Mode = .Debug,
@@ -383,6 +388,20 @@ pub fn addTranslateCTests(b: *build.Builder, test_filter: ?[]const u8) *build.St
return cases.step;
}
+pub fn addRunTranslatedCTests(b: *build.Builder, test_filter: ?[]const u8) *build.Step {
+ const cases = b.allocator.create(RunTranslatedCContext) catch unreachable;
+ cases.* = .{
+ .b = b,
+ .step = b.step("test-run-translated-c", "Run the Run-Translated-C tests"),
+ .test_index = 0,
+ .test_filter = test_filter,
+ };
+
+ run_translated_c.addCases(cases);
+
+ return cases.step;
+}
+
pub fn addGenHTests(b: *build.Builder, test_filter: ?[]const u8) *build.Step {
const cases = b.allocator.create(GenHContext) catch unreachable;
cases.* = GenHContext{
build.zig
@@ -137,6 +137,7 @@ pub fn build(b: *Builder) !void {
test_step.dependOn(tests.addAssembleAndLinkTests(b, test_filter, modes));
test_step.dependOn(tests.addRuntimeSafetyTests(b, test_filter, modes));
test_step.dependOn(tests.addTranslateCTests(b, test_filter));
+ test_step.dependOn(tests.addRunTranslatedCTests(b, test_filter));
test_step.dependOn(tests.addGenHTests(b, test_filter));
test_step.dependOn(tests.addCompileErrorTests(b, test_filter, modes));
test_step.dependOn(docs_step);