authorgravatar for git@vexu.euVeikka Tuominen <git@vexu.eu> 2023-10-14 22:02:32+03:00
committergravatar for git@vexu.euVeikka Tuominen <git@vexu.eu> 2023-10-17 11:55:17+03:00
loge765495b114af3417aea4cffcd7c37f400aab3fe
tree89e1f9a8e856e565d192d08ec8db165fd5d2b6f4
parent58b07ea14fcf5212cfcc14e5ad3366287894618e

tests: translate-c and run-translated-c to the test harness


17 files changed, 338 insertions(+), 71 deletions(-)

lib/std/Build/Step/TranslateC.zig+6
...@@ -17,12 +17,14 @@ target: CrossTarget,...@@ -17,12 +17,14 @@ target: CrossTarget,
17optimize: std.builtin.OptimizeMode,17optimize: std.builtin.OptimizeMode,
18output_file: std.Build.GeneratedFile,18output_file: std.Build.GeneratedFile,
19link_libc: bool,19link_libc: bool,
20use_clang: bool,
2021
21pub const Options = struct {22pub const Options = struct {
22 source_file: std.Build.LazyPath,23 source_file: std.Build.LazyPath,
23 target: CrossTarget,24 target: CrossTarget,
24 optimize: std.builtin.OptimizeMode,25 optimize: std.builtin.OptimizeMode,
25 link_libc: bool = true,26 link_libc: bool = true,
27 use_clang: bool = true,
26};28};
2729
28pub fn create(owner: *std.Build, options: Options) *TranslateC {30pub fn create(owner: *std.Build, options: Options) *TranslateC {
...@@ -43,6 +45,7 @@ pub fn create(owner: *std.Build, options: Options) *TranslateC {...@@ -43,6 +45,7 @@ pub fn create(owner: *std.Build, options: Options) *TranslateC {
43 .optimize = options.optimize,45 .optimize = options.optimize,
44 .output_file = std.Build.GeneratedFile{ .step = &self.step },46 .output_file = std.Build.GeneratedFile{ .step = &self.step },
45 .link_libc = options.link_libc,47 .link_libc = options.link_libc,
48 .use_clang = options.use_clang,
46 };49 };
47 source.addStepDependencies(&self.step);50 source.addStepDependencies(&self.step);
48 return self;51 return self;
...@@ -130,6 +133,9 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {...@@ -130,6 +133,9 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void {
130 if (self.link_libc) {133 if (self.link_libc) {
131 try argv_list.append("-lc");134 try argv_list.append("-lc");
132 }135 }
136 if (!self.use_clang) {
137 try argv_list.append("-fno-clang");
138 }
133139
134 try argv_list.append("--listen=-");140 try argv_list.append("--listen=-");
135141
test/cases/README.md+28-1
...@@ -9,7 +9,7 @@ If you want it to be run with `zig test` and match expected error messages:...@@ -9,7 +9,7 @@ If you want it to be run with `zig test` and match expected error messages:
99
10```zig10```zig
11// error11// error
12// is_test=112// is_test=true
13//13//
14// :4:13: error: 'try' outside function scope14// :4:13: error: 'try' outside function scope
15```15```
...@@ -22,6 +22,33 @@ This will do `zig run` on the code and expect exit code 0....@@ -22,6 +22,33 @@ This will do `zig run` on the code and expect exit code 0.
22// run22// run
23```23```
2424
25## Translate-c
26
27If you want to test translating C code to Zig use `translate-c`:
28
29```c
30// translate-c
31// c_frontend=aro,clang
32// target=x86_64-linux
33//
34// pub const foo = 1;
35// pub const immediately_after_foo = 2;
36//
37// pub const somewhere_else_in_the_file = 3:
38```
39
40## Run Translated C
41
42If you want to test translating C code to Zig and then executing it use `run-translated-c`:
43
44```c
45// run-translated-c
46// c_frontend=aro,clang
47// target=x86_64-linux
48//
49// Hello world!
50```
51
25## Incremental Compilation52## Incremental Compilation
2653
27Make multiple files that have ".", and then an integer, before the ".zig"54Make multiple files that have ".", and then an integer, before the ".zig"
test/cases/compile_errors/access_invalid_typeInfo_decl.zig+1-1
...@@ -6,6 +6,6 @@ test "Crash" {...@@ -6,6 +6,6 @@ test "Crash" {
6// error6// error
7// backend=stage27// backend=stage2
8// target=native8// target=native
9// is_test=19// is_test=true
10//10//
11// :1:11: error: use of undeclared identifier 'B'11// :1:11: error: use of undeclared identifier 'B'
test/cases/compile_errors/invalid_duplicate_test_decl_name.zig+1-1
...@@ -4,7 +4,7 @@ test "thingy" {}...@@ -4,7 +4,7 @@ test "thingy" {}
4// error4// error
5// backend=stage25// backend=stage2
6// target=native6// target=native
7// is_test=17// is_test=true
8//8//
9// :1:6: error: duplicate test name: test.thingy9// :1:6: error: duplicate test name: test.thingy
10// :2:6: note: other test here10// :2:6: note: other test here
test/cases/compile_errors/repeated_invalid_field_access_to_generic_function_returning_type_crashes_compiler_2655.zig+1-1
...@@ -9,6 +9,6 @@ test "1" {...@@ -9,6 +9,6 @@ test "1" {
9// error9// error
10// backend=stage210// backend=stage2
11// target=native11// target=native
12// is_test=112// is_test=true
13//13//
14// :2:12: error: use of undeclared identifier 'Q'14// :2:12: error: use of undeclared identifier 'Q'
test/cases/compile_errors/return_invalid_type_from_test.zig+1-1
...@@ -5,6 +5,6 @@ test "example" {...@@ -5,6 +5,6 @@ test "example" {
5// error5// error
6// backend=stage26// backend=stage2
7// target=native7// target=native
8// is_test=18// is_test=true
9//9//
10// :2:12: error: expected type 'anyerror!void', found 'comptime_int'10// :2:12: error: expected type 'anyerror!void', found 'comptime_int'
test/cases/compile_errors/tagName_on_invalid_value_of_non-exhaustive_enum.zig+1-1
...@@ -6,7 +6,7 @@ test "enum" {...@@ -6,7 +6,7 @@ test "enum" {
6// error6// error
7// backend=stage27// backend=stage2
8// target=native8// target=native
9// is_test=19// is_test=true
10//10//
11// :3:9: error: no field with value '@enumFromInt(5)' in enum 'test.enum.E'11// :3:9: error: no field with value '@enumFromInt(5)' in enum 'test.enum.E'
12// :2:15: note: declared here12// :2:15: note: declared here
test/cases/f32_passed_to_variadic_fn.zig+1-1
...@@ -9,7 +9,7 @@ pub fn main() void {...@@ -9,7 +9,7 @@ pub fn main() void {
9// run9// run
10// backend=llvm10// backend=llvm
11// target=x86_64-linux-gnu11// target=x86_64-linux-gnu
12// link_libc=112// link_libc=true
13//13//
14// f64: 2.00000014// f64: 2.000000
15// f32: 10.00000015// f32: 10.000000
test/cases/fn_typeinfo_passed_to_comptime_fn.zig+1-1
...@@ -13,6 +13,6 @@ fn foo(comptime info: std.builtin.Type) !void {...@@ -13,6 +13,6 @@ fn foo(comptime info: std.builtin.Type) !void {
13}13}
1414
15// run15// run
16// is_test=116// is_test=true
17// backend=llvm17// backend=llvm
18//18//
test/cases/llvm/hello_world.zig+1-1
...@@ -7,7 +7,7 @@ pub fn main() void {...@@ -7,7 +7,7 @@ pub fn main() void {
7// run7// run
8// backend=llvm8// backend=llvm
9// target=x86_64-linux,x86_64-macos9// target=x86_64-linux,x86_64-macos
10// link_libc=110// link_libc=true
11//11//
12// hello world!12// hello world!
13//13//
test/cases/run_translated_c/dereference address of.c created+11
...@@ -0,0 +1,11 @@
1#include <stdlib.h>
2int main(void) {
3 int i = 0;
4 *&i = 42;
5 if (i != 42) abort();
6 return 0;
7}
8
9// run-translated-c
10// c_frontend=clang
11// link_libc=true
test/cases/translate_c/enums msvc.c created+16
...@@ -0,0 +1,16 @@
1enum Foo {
2 FooA = 2,
3 FooB = 5,
4 Foo1,
5};
6
7// translate-c
8// target=x86_64-windows-msvc
9// c_frontend=clang
10//
11// pub const FooA: c_int = 2;
12// pub const FooB: c_int = 5;
13// pub const Foo1: c_int = 6;
14// pub const enum_Foo = c_int;
15//
16// pub const Foo = enum_Foo;
test/cases/translate_c/enums.c created+16
...@@ -0,0 +1,16 @@
1enum Foo {
2 FooA = 2,
3 FooB = 5,
4 Foo1,
5};
6
7// translate-c
8// target=x86_64-linux
9// c_frontend=clang,aro
10//
11// pub const FooA: c_int = 2;
12// pub const FooB: c_int = 5;
13// pub const Foo1: c_int = 6;
14// pub const enum_Foo = c_uint;
15//
16// pub const Foo = enum_Foo;
test/cases/try_in_comptime_in_struct_in_test.zig+1-1
...@@ -8,6 +8,6 @@ test "@unionInit on union w/ tag but no fields" {...@@ -8,6 +8,6 @@ test "@unionInit on union w/ tag but no fields" {
8}8}
99
10// error10// error
11// is_test=111// is_test=true
12//12//
13// :4:13: error: 'try' outside function scope13// :4:13: error: 'try' outside function scope
test/run_translated_c.zig+7-10
...@@ -2,17 +2,14 @@ const std = @import("std");...@@ -2,17 +2,14 @@ const std = @import("std");
2const tests = @import("tests.zig");2const tests = @import("tests.zig");
3const nl = if (@import("builtin").os.tag == .windows) "\r\n" else "\n";3const nl = if (@import("builtin").os.tag == .windows) "\r\n" else "\n";
44
5pub fn addCases(cases: *tests.RunTranslatedCContext) void {5// *********************************************************
6 cases.add("dereference address of",6// * *
7 \\#include <stdlib.h>7// * DO NOT ADD NEW CASES HERE *
8 \\int main(void) {8// * instead add a file to test/cases/run_translated_c *
9 \\ int i = 0;9// * *
10 \\ *&i = 42;10// *********************************************************
11 \\ if (i != 42) abort();
12 \\ return 0;
13 \\}
14 , "");
1511
12pub fn addCases(cases: *tests.RunTranslatedCContext) void {
16 cases.add("division of floating literals",13 cases.add("division of floating literals",
17 \\#define _NO_CRT_STDIO_INLINE 114 \\#define _NO_CRT_STDIO_INLINE 1
18 \\#include <stdio.h>15 \\#include <stdio.h>
test/src/Cases.zig+238-34
...@@ -1,6 +1,7 @@...@@ -1,6 +1,7 @@
1gpa: Allocator,1gpa: Allocator,
2arena: Allocator,2arena: Allocator,
3cases: std.ArrayList(Case),3cases: std.ArrayList(Case),
4translate: std.ArrayList(Translate),
4incremental_cases: std.ArrayList(IncrementalCase),5incremental_cases: std.ArrayList(IncrementalCase),
56
6pub const IncrementalCase = struct {7pub const IncrementalCase = struct {
...@@ -36,7 +37,7 @@ pub const Update = struct {...@@ -36,7 +37,7 @@ pub const Update = struct {
36 Execution: []const u8,37 Execution: []const u8,
37 /// A header update compiles the input with the equivalent of38 /// A header update compiles the input with the equivalent of
38 /// `-femit-h` and tests the produced header against the39 /// `-femit-h` and tests the produced header against the
39 /// expected result40 /// expected result.
40 Header: []const u8,41 Header: []const u8,
41 },42 },
4243
...@@ -61,6 +62,11 @@ pub const Backend = enum {...@@ -61,6 +62,11 @@ pub const Backend = enum {
61 llvm,62 llvm,
62};63};
6364
65pub const CFrontend = enum {
66 clang,
67 aro,
68};
69
64/// A `Case` consists of a list of `Update`. The same `Compilation` is used for each70/// A `Case` consists of a list of `Update`. The same `Compilation` is used for each
65/// update, so each update's source is treated as a single file being71/// update, so each update's source is treated as a single file being
66/// updated by the test harness and incrementally compiled.72/// updated by the test harness and incrementally compiled.
...@@ -143,6 +149,25 @@ pub const Case = struct {...@@ -143,6 +149,25 @@ pub const Case = struct {
143 }149 }
144};150};
145151
152pub const Translate = struct {
153 /// The name of the test case. This is shown if a test fails, and
154 /// otherwise ignored.
155 name: []const u8,
156
157 input: [:0]const u8,
158 target: CrossTarget,
159 link_libc: bool,
160 c_frontend: CFrontend,
161 kind: union(enum) {
162 /// Translate the input, run it and check that it
163 /// outputs the expected text.
164 run: []const u8,
165 /// Translate the input and check that it contains
166 /// the expected lines of code.
167 translate: []const []const u8,
168 },
169};
170
146pub fn addExe(171pub fn addExe(
147 ctx: *Cases,172 ctx: *Cases,
148 name: []const u8,173 name: []const u8,
...@@ -346,9 +371,12 @@ pub fn addCompile(...@@ -346,9 +371,12 @@ pub fn addCompile(
346pub fn addFromDir(ctx: *Cases, dir: std.fs.IterableDir) void {371pub fn addFromDir(ctx: *Cases, dir: std.fs.IterableDir) void {
347 var current_file: []const u8 = "none";372 var current_file: []const u8 = "none";
348 ctx.addFromDirInner(dir, &current_file) catch |err| {373 ctx.addFromDirInner(dir, &current_file) catch |err| {
349 std.debug.panic("test harness failed to process file '{s}': {s}\n", .{374 std.debug.panicExtra(
350 current_file, @errorName(err),375 @errorReturnTrace(),
351 });376 @returnAddress(),
377 "test harness failed to process file '{s}': {s}\n",
378 .{ current_file, @errorName(err) },
379 );
352 };380 };
353}381}
354382
...@@ -395,10 +423,44 @@ fn addFromDirInner(...@@ -395,10 +423,44 @@ fn addFromDirInner(
395423
396 const backends = try manifest.getConfigForKeyAlloc(ctx.arena, "backend", Backend);424 const backends = try manifest.getConfigForKeyAlloc(ctx.arena, "backend", Backend);
397 const targets = try manifest.getConfigForKeyAlloc(ctx.arena, "target", CrossTarget);425 const targets = try manifest.getConfigForKeyAlloc(ctx.arena, "target", CrossTarget);
426 const c_frontends = try manifest.getConfigForKeyAlloc(ctx.arena, "c_frontend", CFrontend);
398 const is_test = try manifest.getConfigForKeyAssertSingle("is_test", bool);427 const is_test = try manifest.getConfigForKeyAssertSingle("is_test", bool);
399 const link_libc = try manifest.getConfigForKeyAssertSingle("link_libc", bool);428 const link_libc = try manifest.getConfigForKeyAssertSingle("link_libc", bool);
400 const output_mode = try manifest.getConfigForKeyAssertSingle("output_mode", std.builtin.OutputMode);429 const output_mode = try manifest.getConfigForKeyAssertSingle("output_mode", std.builtin.OutputMode);
401430
431 if (manifest.type == .translate_c) {
432 for (c_frontends) |c_frontend| {
433 for (targets) |target| {
434 const output = try manifest.trailingLinesSplit(ctx.arena);
435 try ctx.translate.append(.{
436 .name = std.fs.path.stem(filename),
437 .c_frontend = c_frontend,
438 .target = target,
439 .link_libc = link_libc,
440 .input = src,
441 .kind = .{ .translate = output },
442 });
443 }
444 }
445 continue;
446 }
447 if (manifest.type == .run_translated_c) {
448 for (c_frontends) |c_frontend| {
449 for (targets) |target| {
450 const output = try manifest.trailingSplit(ctx.arena);
451 try ctx.translate.append(.{
452 .name = std.fs.path.stem(filename),
453 .c_frontend = c_frontend,
454 .target = target,
455 .link_libc = link_libc,
456 .input = src,
457 .kind = .{ .run = output },
458 });
459 }
460 }
461 continue;
462 }
463
402 var cases = std.ArrayList(usize).init(ctx.arena);464 var cases = std.ArrayList(usize).init(ctx.arena);
403465
404 // Cross-product to get all possible test combinations466 // Cross-product to get all possible test combinations
...@@ -439,21 +501,15 @@ fn addFromDirInner(...@@ -439,21 +501,15 @@ fn addFromDirInner(
439 case.addCompile(src);501 case.addCompile(src);
440 },502 },
441 .@"error" => {503 .@"error" => {
442 const errors = try manifest.trailingAlloc(ctx.arena);504 const errors = try manifest.trailingLines(ctx.arena);
443 case.addError(src, errors);505 case.addError(src, errors);
444 },506 },
445 .run => {507 .run => {
446 var output = std.ArrayList(u8).init(ctx.arena);508 const output = try manifest.trailingSplit(ctx.arena);
447 var trailing_it = manifest.trailing();509 case.addCompareOutput(src, output);
448 while (trailing_it.next()) |line| {
449 try output.appendSlice(line);
450 try output.append('\n');
451 }
452 if (output.items.len > 0) {
453 try output.resize(output.items.len - 1);
454 }
455 case.addCompareOutput(src, try output.toOwnedSlice());
456 },510 },
511 .translate_c => @panic("c_frontend specified for compile case"),
512 .run_translated_c => @panic("c_frontend specified for compile case"),
457 .cli => @panic("TODO cli tests"),513 .cli => @panic("TODO cli tests"),
458 }514 }
459 }515 }
...@@ -468,6 +524,7 @@ pub fn init(gpa: Allocator, arena: Allocator) Cases {...@@ -468,6 +524,7 @@ pub fn init(gpa: Allocator, arena: Allocator) Cases {
468 return .{524 return .{
469 .gpa = gpa,525 .gpa = gpa,
470 .cases = std.ArrayList(Case).init(gpa),526 .cases = std.ArrayList(Case).init(gpa),
527 .translate = std.ArrayList(Translate).init(gpa),
471 .incremental_cases = std.ArrayList(IncrementalCase).init(gpa),528 .incremental_cases = std.ArrayList(IncrementalCase).init(gpa),
472 .arena = arena,529 .arena = arena,
473 };530 };
...@@ -482,7 +539,7 @@ pub fn lowerToBuildSteps(...@@ -482,7 +539,7 @@ pub fn lowerToBuildSteps(
482 incremental_exe: *std.Build.Step.Compile,539 incremental_exe: *std.Build.Step.Compile,
483) void {540) void {
484 const host = std.zig.system.NativeTargetInfo.detect(.{}) catch |err|541 const host = std.zig.system.NativeTargetInfo.detect(.{}) catch |err|
485 std.debug.panic("unable to detect notive host: {s}\n", .{@errorName(err)});542 std.debug.panic("unable to detect native host: {s}\n", .{@errorName(err)});
486543
487 for (self.incremental_cases.items) |incr_case| {544 for (self.incremental_cases.items) |incr_case| {
488 if (true) {545 if (true) {
...@@ -589,7 +646,7 @@ pub fn lowerToBuildSteps(...@@ -589,7 +646,7 @@ pub fn lowerToBuildSteps(
589 .Execution => |expected_stdout| no_exec: {646 .Execution => |expected_stdout| no_exec: {
590 const run = if (case.target.ofmt == .c) run_step: {647 const run = if (case.target.ofmt == .c) run_step: {
591 const target_info = std.zig.system.NativeTargetInfo.detect(case.target) catch |err|648 const target_info = std.zig.system.NativeTargetInfo.detect(case.target) catch |err|
592 std.debug.panic("unable to detect notive host: {s}\n", .{@errorName(err)});649 std.debug.panic("unable to detect target host: {s}\n", .{@errorName(err)});
593 if (host.getExternalExecutor(&target_info, .{ .link_libc = true }) != .native) {650 if (host.getExternalExecutor(&target_info, .{ .link_libc = true }) != .native) {
594 // We wouldn't be able to run the compiled C code.651 // We wouldn't be able to run the compiled C code.
595 break :no_exec;652 break :no_exec;
...@@ -623,6 +680,68 @@ pub fn lowerToBuildSteps(...@@ -623,6 +680,68 @@ pub fn lowerToBuildSteps(
623 .Header => @panic("TODO"),680 .Header => @panic("TODO"),
624 }681 }
625 }682 }
683
684 for (self.translate.items) |*case| switch (case.kind) {
685 .run => |output| {
686 const annotated_case_name = b.fmt("run-translated-c {s}", .{case.name});
687 if (opt_test_filter) |filter| {
688 if (std.mem.indexOf(u8, annotated_case_name, filter) == null) return;
689 }
690 if (!std.process.can_spawn) {
691 std.debug.print("Unable to spawn child processes on {s}, skipping test.\n", .{@tagName(builtin.os.tag)});
692 continue; // Pass test.
693 }
694
695 const target_info = std.zig.system.NativeTargetInfo.detect(case.target) catch |err|
696 std.debug.panic("unable to detect target host: {s}\n", .{@errorName(err)});
697 if (host.getExternalExecutor(&target_info, .{ .link_libc = true }) != .native) {
698 // We wouldn't be able to run the compiled C code.
699 continue; // Pass test.
700 }
701
702 const write_src = b.addWriteFiles();
703 const file_source = write_src.add("tmp.c", case.input);
704
705 const translate_c = b.addTranslateC(.{
706 .source_file = file_source,
707 .optimize = .Debug,
708 .target = case.target,
709 .link_libc = case.link_libc,
710 .use_clang = case.c_frontend == .clang,
711 });
712 translate_c.step.name = b.fmt("{s} translate-c", .{annotated_case_name});
713
714 const run_exe = translate_c.addExecutable(.{});
715 run_exe.step.name = b.fmt("{s} build-exe", .{annotated_case_name});
716 run_exe.linkLibC();
717 const run = b.addRunArtifact(run_exe);
718 run.step.name = b.fmt("{s} run", .{annotated_case_name});
719 run.expectStdOutEqual(output);
720
721 parent_step.dependOn(&run.step);
722 },
723 .translate => |output| {
724 const annotated_case_name = b.fmt("zig translate-c {s}", .{case.name});
725 if (opt_test_filter) |filter| {
726 if (std.mem.indexOf(u8, annotated_case_name, filter) == null) return;
727 }
728
729 const write_src = b.addWriteFiles();
730 const file_source = write_src.add("tmp.c", case.input);
731
732 const translate_c = b.addTranslateC(.{
733 .source_file = file_source,
734 .optimize = .Debug,
735 .target = case.target,
736 .link_libc = case.link_libc,
737 .use_clang = case.c_frontend == .clang,
738 });
739 translate_c.step.name = annotated_case_name;
740
741 const check_file = translate_c.addCheckFile(output);
742 parent_step.dependOn(&check_file.step);
743 },
744 };
626}745}
627746
628/// Sort test filenames in-place, so that incremental test cases ("foo.0.zig",747/// Sort test filenames in-place, so that incremental test cases ("foo.0.zig",
...@@ -780,7 +899,7 @@ const TestManifestConfigDefaults = struct {...@@ -780,7 +899,7 @@ const TestManifestConfigDefaults = struct {
780 if (std.mem.eql(u8, key, "backend")) {899 if (std.mem.eql(u8, key, "backend")) {
781 return "stage2";900 return "stage2";
782 } else if (std.mem.eql(u8, key, "target")) {901 } else if (std.mem.eql(u8, key, "target")) {
783 if (@"type" == .@"error") {902 if (@"type" == .@"error" or @"type" == .translate_c or @"type" == .run_translated_c) {
784 return "native";903 return "native";
785 }904 }
786 return comptime blk: {905 return comptime blk: {
...@@ -807,12 +926,16 @@ const TestManifestConfigDefaults = struct {...@@ -807,12 +926,16 @@ const TestManifestConfigDefaults = struct {
807 .@"error" => "Obj",926 .@"error" => "Obj",
808 .run => "Exe",927 .run => "Exe",
809 .compile => "Obj",928 .compile => "Obj",
929 .translate_c => "Obj",
930 .run_translated_c => "Obj",
810 .cli => @panic("TODO test harness for CLI tests"),931 .cli => @panic("TODO test harness for CLI tests"),
811 };932 };
812 } else if (std.mem.eql(u8, key, "is_test")) {933 } else if (std.mem.eql(u8, key, "is_test")) {
813 return "0";934 return "false";
814 } else if (std.mem.eql(u8, key, "link_libc")) {935 } else if (std.mem.eql(u8, key, "link_libc")) {
815 return "0";936 return "false";
937 } else if (std.mem.eql(u8, key, "c_frontend")) {
938 return "clang";
816 } else unreachable;939 } else unreachable;
817 }940 }
818};941};
...@@ -844,6 +967,8 @@ const TestManifest = struct {...@@ -844,6 +967,8 @@ const TestManifest = struct {
844 run,967 run,
845 cli,968 cli,
846 compile,969 compile,
970 translate_c,
971 run_translated_c,
847 };972 };
848973
849 const TrailingIterator = struct {974 const TrailingIterator = struct {
...@@ -912,6 +1037,10 @@ const TestManifest = struct {...@@ -912,6 +1037,10 @@ const TestManifest = struct {
912 break :blk .cli;1037 break :blk .cli;
913 } else if (std.mem.eql(u8, raw, "compile")) {1038 } else if (std.mem.eql(u8, raw, "compile")) {
914 break :blk .compile;1039 break :blk .compile;
1040 } else if (std.mem.eql(u8, raw, "translate-c")) {
1041 break :blk .translate_c;
1042 } else if (std.mem.eql(u8, raw, "run-translated-c")) {
1043 break :blk .run_translated_c;
915 } else {1044 } else {
916 std.log.warn("unknown test case type requested: {s}", .{raw});1045 std.log.warn("unknown test case type requested: {s}", .{raw});
917 return error.UnknownTestCaseType;1046 return error.UnknownTestCaseType;
...@@ -979,7 +1108,21 @@ const TestManifest = struct {...@@ -979,7 +1108,21 @@ const TestManifest = struct {
979 };1108 };
980 }1109 }
9811110
982 fn trailingAlloc(self: TestManifest, allocator: Allocator) error{OutOfMemory}![]const []const u8 {1111 fn trailingSplit(self: TestManifest, allocator: Allocator) error{OutOfMemory}![]const u8 {
1112 var out = std.ArrayList(u8).init(allocator);
1113 defer out.deinit();
1114 var trailing_it = self.trailing();
1115 while (trailing_it.next()) |line| {
1116 try out.appendSlice(line);
1117 try out.append('\n');
1118 }
1119 if (out.items.len > 0) {
1120 try out.resize(out.items.len - 1);
1121 }
1122 return try out.toOwnedSlice();
1123 }
1124
1125 fn trailingLines(self: TestManifest, allocator: Allocator) error{OutOfMemory}![]const []const u8 {
983 var out = std.ArrayList([]const u8).init(allocator);1126 var out = std.ArrayList([]const u8).init(allocator);
984 defer out.deinit();1127 defer out.deinit();
985 var it = self.trailing();1128 var it = self.trailing();
...@@ -989,6 +1132,28 @@ const TestManifest = struct {...@@ -989,6 +1132,28 @@ const TestManifest = struct {
989 return try out.toOwnedSlice();1132 return try out.toOwnedSlice();
990 }1133 }
9911134
1135 fn trailingLinesSplit(self: TestManifest, allocator: Allocator) error{OutOfMemory}![]const []const u8 {
1136 // Collect output lines split by empty lines
1137 var out = std.ArrayList([]const u8).init(allocator);
1138 defer out.deinit();
1139 var buf = std.ArrayList(u8).init(allocator);
1140 defer buf.deinit();
1141 var it = self.trailing();
1142 while (it.next()) |line| {
1143 if (line.len == 0) {
1144 if (buf.items.len != 0) {
1145 try out.append(try buf.toOwnedSlice());
1146 buf.items.len = 0;
1147 }
1148 continue;
1149 }
1150 try buf.appendSlice(line);
1151 try buf.append('\n');
1152 }
1153 try out.append(try buf.toOwnedSlice());
1154 return try out.toOwnedSlice();
1155 }
1156
992 fn ParseFn(comptime T: type) type {1157 fn ParseFn(comptime T: type) type {
993 return fn ([]const u8) anyerror!T;1158 return fn ([]const u8) anyerror!T;
994 }1159 }
...@@ -1011,8 +1176,10 @@ const TestManifest = struct {...@@ -1011,8 +1176,10 @@ const TestManifest = struct {
1011 }.parse,1176 }.parse,
1012 .Bool => return struct {1177 .Bool => return struct {
1013 fn parse(str: []const u8) anyerror!T {1178 fn parse(str: []const u8) anyerror!T {
1014 const as_int = try std.fmt.parseInt(u1, str, 0);1179 if (std.mem.eql(u8, str, "true")) return true;
1015 return as_int > 0;1180 if (std.mem.eql(u8, str, "false")) return false;
1181 std.debug.print("{s}\n", .{str});
1182 return error.InvalidBool;
1016 }1183 }
1017 }.parse,1184 }.parse,
1018 .Enum => return struct {1185 .Enum => return struct {
...@@ -1124,9 +1291,47 @@ pub fn main() !void {...@@ -1124,9 +1291,47 @@ pub fn main() !void {
1124 if (cases.items.len == 0) {1291 if (cases.items.len == 0) {
1125 const backends = try manifest.getConfigForKeyAlloc(arena, "backend", Backend);1292 const backends = try manifest.getConfigForKeyAlloc(arena, "backend", Backend);
1126 const targets = try manifest.getConfigForKeyAlloc(arena, "target", CrossTarget);1293 const targets = try manifest.getConfigForKeyAlloc(arena, "target", CrossTarget);
1294 const c_frontends = try manifest.getConfigForKeyAlloc(ctx.arena, "c_frontend", CFrontend);
1127 const is_test = try manifest.getConfigForKeyAssertSingle("is_test", bool);1295 const is_test = try manifest.getConfigForKeyAssertSingle("is_test", bool);
1296 const link_libc = try manifest.getConfigForKeyAssertSingle("link_libc", bool);
1128 const output_mode = try manifest.getConfigForKeyAssertSingle("output_mode", std.builtin.OutputMode);1297 const output_mode = try manifest.getConfigForKeyAssertSingle("output_mode", std.builtin.OutputMode);
11291298
1299 if (manifest.type == .translate_c) {
1300 for (c_frontends) |c_frontend| {
1301 for (targets) |target| {
1302 const output = try manifest.trailingLinesSplit(ctx.arena);
1303 try ctx.translate.append(.{
1304 .name = std.fs.path.stem(filename),
1305 .c_frontend = c_frontend,
1306 .target = target,
1307 .is_test = is_test,
1308 .link_libc = link_libc,
1309 .input = src,
1310 .kind = .{ .translate = output },
1311 });
1312 }
1313 }
1314 continue;
1315 }
1316 if (manifest.type == .run_translated_c) {
1317 for (c_frontends) |c_frontend| {
1318 for (targets) |target| {
1319 const output = try manifest.trailingSplit(ctx.arena);
1320 try ctx.translate.append(.{
1321 .name = std.fs.path.stem(filename),
1322 .c_frontend = c_frontend,
1323 .target = target,
1324 .is_test = is_test,
1325 .link_libc = link_libc,
1326 .output = output,
1327 .input = src,
1328 .kind = .{ .run = output },
1329 });
1330 }
1331 }
1332 continue;
1333 }
1334
1130 // Cross-product to get all possible test combinations1335 // Cross-product to get all possible test combinations
1131 for (backends) |backend| {1336 for (backends) |backend| {
1132 for (targets) |target| {1337 for (targets) |target| {
...@@ -1158,7 +1363,7 @@ pub fn main() !void {...@@ -1158,7 +1363,7 @@ pub fn main() !void {
1158 case.addCompile(src);1363 case.addCompile(src);
1159 },1364 },
1160 .@"error" => {1365 .@"error" => {
1161 const errors = try manifest.trailingAlloc(arena);1366 const errors = try manifest.trailingLines(arena);
1162 switch (strategy) {1367 switch (strategy) {
1163 .independent => {1368 .independent => {
1164 case.addError(src, errors);1369 case.addError(src, errors);
...@@ -1169,17 +1374,11 @@ pub fn main() !void {...@@ -1169,17 +1374,11 @@ pub fn main() !void {
1169 }1374 }
1170 },1375 },
1171 .run => {1376 .run => {
1172 var output = std.ArrayList(u8).init(arena);1377 const output = try manifest.trailingSplit(ctx.arena);
1173 var trailing_it = manifest.trailing();1378 case.addCompareOutput(src, output);
1174 while (trailing_it.next()) |line| {
1175 try output.appendSlice(line);
1176 try output.append('\n');
1177 }
1178 if (output.items.len > 0) {
1179 try output.resize(output.items.len - 1);
1180 }
1181 case.addCompareOutput(src, try output.toOwnedSlice());
1182 },1379 },
1380 .translate_c => @panic("c_frontend specified for compile case"),
1381 .run_translated_c => @panic("c_frontend specified for compile case"),
1183 .cli => @panic("TODO cli tests"),1382 .cli => @panic("TODO cli tests"),
1184 }1383 }
1185 }1384 }
...@@ -1255,6 +1454,11 @@ fn runCases(self: *Cases, zig_exe_path: []const u8) !void {...@@ -1255,6 +1454,11 @@ fn runCases(self: *Cases, zig_exe_path: []const u8) !void {
1255 host,1454 host,
1256 );1455 );
1257 }1456 }
1457
1458 for (self.translate.items) |*case| {
1459 _ = case;
1460 @panic("TODO is this even used?");
1461 }
1258 }1462 }
1259}1463}
12601464
test/translate_c.zig+7-17
...@@ -3,6 +3,13 @@ const builtin = @import("builtin");...@@ -3,6 +3,13 @@ const builtin = @import("builtin");
3const tests = @import("tests.zig");3const tests = @import("tests.zig");
4const CrossTarget = std.zig.CrossTarget;4const CrossTarget = std.zig.CrossTarget;
55
6// ********************************************************
7// * *
8// * DO NOT ADD NEW CASES HERE *
9// * instead add a file to test/cases/translate_c *
10// * *
11// ********************************************************
12
6pub fn addCases(cases: *tests.TranslateCContext) void {13pub fn addCases(cases: *tests.TranslateCContext) void {
7 const default_enum_type = if (builtin.abi == .msvc) "c_int" else "c_uint";14 const default_enum_type = if (builtin.abi == .msvc) "c_int" else "c_uint";
815
...@@ -3315,23 +3322,6 @@ pub fn addCases(cases: *tests.TranslateCContext) void {...@@ -3315,23 +3322,6 @@ pub fn addCases(cases: *tests.TranslateCContext) void {
3315 \\pub const FOO_CHAR = '\x3f';3322 \\pub const FOO_CHAR = '\x3f';
3316 });3323 });
33173324
3318 cases.add("enums",
3319 \\enum Foo {
3320 \\ FooA = 2,
3321 \\ FooB = 5,
3322 \\ Foo1,
3323 \\};
3324 , &[_][]const u8{
3325 \\pub const FooA: c_int = 2;
3326 \\pub const FooB: c_int = 5;
3327 \\pub const Foo1: c_int = 6;
3328 \\pub const enum_Foo =
3329 ++ " " ++ default_enum_type ++
3330 \\;
3331 ,
3332 \\pub const Foo = enum_Foo;
3333 });
3334
3335 cases.add("macro cast",3325 cases.add("macro cast",
3336 \\#include <stdint.h>3326 \\#include <stdint.h>
3337 \\int baz(void *arg) { return 0; }3327 \\int baz(void *arg) { return 0; }