| ... | @@ -204,10 +204,10 @@ use_llvm: ?bool, | ... | @@ -204,10 +204,10 @@ use_llvm: ?bool, |
| 204 | use_lld: ?bool, | 204 | use_lld: ?bool, |
| 205 | | 205 | |
| 206 | /// This is an advanced setting that can change the intent of this Compile step. | 206 | /// This is an advanced setting that can change the intent of this Compile step. |
| 207 | /// If this slice has nonzero length, it means that this Compile step exists to | 207 | /// If this value is non-null, it means that this Compile step exists to |
| 208 | /// check for compile errors and return *success* if they match, and failure | 208 | /// check for compile errors and return *success* if they match, and failure |
| 209 | /// otherwise. | 209 | /// otherwise. |
| 210 | expect_errors: []const []const u8 = &.{}, | 210 | expect_errors: ?ExpectedCompileErrors = null, |
| 211 | | 211 | |
| 212 | emit_directory: ?*GeneratedFile, | 212 | emit_directory: ?*GeneratedFile, |
| 213 | | 213 | |
| ... | @@ -220,6 +220,11 @@ generated_llvm_bc: ?*GeneratedFile, | ... | @@ -220,6 +220,11 @@ generated_llvm_bc: ?*GeneratedFile, |
| 220 | generated_llvm_ir: ?*GeneratedFile, | 220 | generated_llvm_ir: ?*GeneratedFile, |
| 221 | generated_h: ?*GeneratedFile, | 221 | generated_h: ?*GeneratedFile, |
| 222 | | 222 | |
| | 223 | pub const ExpectedCompileErrors = union(enum) { |
| | 224 | contains: []const u8, |
| | 225 | exact: []const []const u8, |
| | 226 | }; |
| | 227 | |
| 223 | pub const CSourceFiles = struct { | 228 | pub const CSourceFiles = struct { |
| 224 | dependency: ?*std.Build.Dependency, | 229 | dependency: ?*std.Build.Dependency, |
| 225 | /// If `dependency` is not null relative to it, | 230 | /// If `dependency` is not null relative to it, |
| ... | @@ -2131,7 +2136,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { | ... | @@ -2131,7 +2136,7 @@ fn make(step: *Step, prog_node: *std.Progress.Node) !void { |
| 2131 | | 2136 | |
| 2132 | const maybe_output_bin_path = step.evalZigProcess(zig_args.items, prog_node) catch |err| switch (err) { | 2137 | const maybe_output_bin_path = step.evalZigProcess(zig_args.items, prog_node) catch |err| switch (err) { |
| 2133 | error.NeedCompileErrorCheck => { | 2138 | error.NeedCompileErrorCheck => { |
| 2134 | assert(self.expect_errors.len != 0); | 2139 | assert(self.expect_errors != null); |
| 2135 | try checkCompileErrors(self); | 2140 | try checkCompileErrors(self); |
| 2136 | return; | 2141 | return; |
| 2137 | }, | 2142 | }, |
| ... | @@ -2390,52 +2395,70 @@ fn checkCompileErrors(self: *Compile) !void { | ... | @@ -2390,52 +2395,70 @@ fn checkCompileErrors(self: *Compile) !void { |
| 2390 | | 2395 | |
| 2391 | // Render the expected lines into a string that we can compare verbatim. | 2396 | // Render the expected lines into a string that we can compare verbatim. |
| 2392 | var expected_generated = std.ArrayList(u8).init(arena); | 2397 | var expected_generated = std.ArrayList(u8).init(arena); |
| | 2398 | const expect_errors = self.expect_errors.?; |
| 2393 | | 2399 | |
| 2394 | var actual_line_it = mem.splitScalar(u8, actual_stderr, '\n'); | 2400 | var actual_line_it = mem.splitScalar(u8, actual_stderr, '\n'); |
| 2395 | for (self.expect_errors) |expect_line| { | 2401 | |
| 2396 | const actual_line = actual_line_it.next() orelse { | 2402 | // TODO merge this with the testing.expectEqualStrings logic, and also CheckFile |
| 2397 | try expected_generated.appendSlice(expect_line); | 2403 | switch (expect_errors) { |
| 2398 | try expected_generated.append('\n'); | 2404 | .contains => |expect_line| { |
| 2399 | continue; | 2405 | while (actual_line_it.next()) |actual_line| { |
| 2400 | }; | 2406 | if (!matchCompileError(actual_line, expect_line)) continue; |
| 2401 | if (mem.endsWith(u8, actual_line, expect_line)) { | 2407 | return; |
| 2402 | try expected_generated.appendSlice(actual_line); | | |
| 2403 | try expected_generated.append('\n'); | | |
| 2404 | continue; | | |
| 2405 | } | | |
| 2406 | if (mem.startsWith(u8, expect_line, ":?:?: ")) { | | |
| 2407 | if (mem.endsWith(u8, actual_line, expect_line[":?:?: ".len..])) { | | |
| 2408 | try expected_generated.appendSlice(actual_line); | | |
| 2409 | try expected_generated.append('\n'); | | |
| 2410 | continue; | | |
| 2411 | } | 2408 | } |
| 2412 | } | 2409 | |
| 2413 | // We scan for /?/ in expected line and if there is a match, we match everything | 2410 | return self.step.fail( |
| 2414 | // up to and after /?/. | 2411 | \\ |
| 2415 | const expect_line_trim = mem.trim(u8, expect_line, " "); | 2412 | \\========= should contain: =============== |
| 2416 | if (mem.indexOf(u8, expect_line_trim, "/?/")) |exp_index| { | 2413 | \\{s} |
| 2417 | const actual_line_trim = mem.trim(u8, actual_line, " "); | 2414 | \\========= but not found: ================ |
| 2418 | const exp_lhs = expect_line_trim[0..exp_index]; | 2415 | \\{s} |
| 2419 | const exp_rhs = expect_line_trim[exp_index + "/?/".len ..]; | 2416 | \\========================================= |
| 2420 | if (mem.startsWith(u8, actual_line_trim, exp_lhs) and mem.endsWith(u8, actual_line_trim, exp_rhs)) { | 2417 | , .{ expect_line, actual_stderr }); |
| 2421 | try expected_generated.appendSlice(actual_line); | 2418 | }, |
| | 2419 | .exact => |expect_lines| { |
| | 2420 | for (expect_lines) |expect_line| { |
| | 2421 | const actual_line = actual_line_it.next() orelse { |
| | 2422 | try expected_generated.appendSlice(expect_line); |
| | 2423 | try expected_generated.append('\n'); |
| | 2424 | continue; |
| | 2425 | }; |
| | 2426 | if (matchCompileError(actual_line, expect_line)) { |
| | 2427 | try expected_generated.appendSlice(actual_line); |
| | 2428 | try expected_generated.append('\n'); |
| | 2429 | continue; |
| | 2430 | } |
| | 2431 | try expected_generated.appendSlice(expect_line); |
| 2422 | try expected_generated.append('\n'); | 2432 | try expected_generated.append('\n'); |
| 2423 | continue; | | |
| 2424 | } | 2433 | } |
| 2425 | } | | |
| 2426 | try expected_generated.appendSlice(expect_line); | | |
| 2427 | try expected_generated.append('\n'); | | |
| 2428 | } | | |
| 2429 | | 2434 | |
| 2430 | if (mem.eql(u8, expected_generated.items, actual_stderr)) return; | 2435 | if (mem.eql(u8, expected_generated.items, actual_stderr)) return; |
| 2431 | | 2436 | |
| 2432 | // TODO merge this with the testing.expectEqualStrings logic, and also CheckFile | 2437 | return self.step.fail( |
| 2433 | return self.step.fail( | 2438 | \\ |
| 2434 | \\ | 2439 | \\========= expected: ===================== |
| 2435 | \\========= expected: ===================== | 2440 | \\{s} |
| 2436 | \\{s} | 2441 | \\========= but found: ==================== |
| 2437 | \\========= but found: ==================== | 2442 | \\{s} |
| 2438 | \\{s} | 2443 | \\========================================= |
| 2439 | \\========================================= | 2444 | , .{ expected_generated.items, actual_stderr }); |
| 2440 | , .{ expected_generated.items, actual_stderr }); | 2445 | }, |
| | 2446 | } |
| | 2447 | } |
| | 2448 | |
| | 2449 | fn matchCompileError(actual: []const u8, expected: []const u8) bool { |
| | 2450 | if (mem.endsWith(u8, actual, expected)) return true; |
| | 2451 | if (mem.startsWith(u8, expected, ":?:?: ")) { |
| | 2452 | if (mem.endsWith(u8, actual, expected[":?:?: ".len..])) return true; |
| | 2453 | } |
| | 2454 | // We scan for /?/ in expected line and if there is a match, we match everything |
| | 2455 | // up to and after /?/. |
| | 2456 | const expected_trim = mem.trim(u8, expected, " "); |
| | 2457 | if (mem.indexOf(u8, expected_trim, "/?/")) |index| { |
| | 2458 | const actual_trim = mem.trim(u8, actual, " "); |
| | 2459 | const lhs = expected_trim[0..index]; |
| | 2460 | const rhs = expected_trim[index + "/?/".len ..]; |
| | 2461 | if (mem.startsWith(u8, actual_trim, lhs) and mem.endsWith(u8, actual_trim, rhs)) return true; |
| | 2462 | } |
| | 2463 | return false; |
| 2441 | } | 2464 | } |