| ... | @@ -4,9 +4,11 @@ const Step = std.Build.Step; | ... | @@ -4,9 +4,11 @@ const Step = std.Build.Step; |
| 4 | const Allocator = std.mem.Allocator; | 4 | const Allocator = std.mem.Allocator; |
| 5 | | 5 | |
| 6 | pub const Style = union(enum) { | 6 | pub const Style = union(enum) { |
| 7 | /// The configure format supported by autotools. It uses `#undef foo` to | 7 | /// A configure format supported by autotools that uses `#undef foo` to |
| 8 | /// mark lines that can be substituted with different values. | 8 | /// mark lines that can be substituted with different values. |
| 9 | autoconf: std.Build.LazyPath, | 9 | autoconf_undef: std.Build.LazyPath, |
| | 10 | /// A configure format supported by autotools that uses `@FOO@` output variables. |
| | 11 | autoconf_at: std.Build.LazyPath, |
| 10 | /// The configure format supported by CMake. It uses `@FOO@`, `${}` and | 12 | /// The configure format supported by CMake. It uses `@FOO@`, `${}` and |
| 11 | /// `#cmakedefine` for template substitution. | 13 | /// `#cmakedefine` for template substitution. |
| 12 | cmake: std.Build.LazyPath, | 14 | cmake: std.Build.LazyPath, |
| ... | @@ -17,7 +19,7 @@ pub const Style = union(enum) { | ... | @@ -17,7 +19,7 @@ pub const Style = union(enum) { |
| 17 | | 19 | |
| 18 | pub fn getPath(style: Style) ?std.Build.LazyPath { | 20 | pub fn getPath(style: Style) ?std.Build.LazyPath { |
| 19 | switch (style) { | 21 | switch (style) { |
| 20 | .autoconf, .cmake => |s| return s, | 22 | .autoconf_undef, .autoconf_at, .cmake => |s| return s, |
| 21 | .blank, .nasm => return null, | 23 | .blank, .nasm => return null, |
| 22 | } | 24 | } |
| 23 | } | 25 | } |
| ... | @@ -191,7 +193,7 @@ fn make(step: *Step, options: Step.MakeOptions) !void { | ... | @@ -191,7 +193,7 @@ fn make(step: *Step, options: Step.MakeOptions) !void { |
| 191 | const asm_generated_line = "; " ++ header_text ++ "\n"; | 193 | const asm_generated_line = "; " ++ header_text ++ "\n"; |
| 192 | | 194 | |
| 193 | switch (config_header.style) { | 195 | switch (config_header.style) { |
| 194 | .autoconf => |file_source| { | 196 | .autoconf_undef, .autoconf_at => |file_source| { |
| 195 | try output.appendSlice(c_generated_line); | 197 | try output.appendSlice(c_generated_line); |
| 196 | const src_path = file_source.getPath2(b, step); | 198 | const src_path = file_source.getPath2(b, step); |
| 197 | const contents = std.fs.cwd().readFileAlloc(arena, src_path, config_header.max_bytes) catch |err| { | 199 | const contents = std.fs.cwd().readFileAlloc(arena, src_path, config_header.max_bytes) catch |err| { |
| ... | @@ -199,7 +201,11 @@ fn make(step: *Step, options: Step.MakeOptions) !void { | ... | @@ -199,7 +201,11 @@ fn make(step: *Step, options: Step.MakeOptions) !void { |
| 199 | src_path, @errorName(err), | 201 | src_path, @errorName(err), |
| 200 | }); | 202 | }); |
| 201 | }; | 203 | }; |
| 202 | try render_autoconf(step, contents, &output, config_header.values, src_path); | 204 | switch (config_header.style) { |
| | 205 | .autoconf_undef => try render_autoconf_undef(step, contents, &output, config_header.values, src_path), |
| | 206 | .autoconf_at => try render_autoconf_at(step, contents, &output, config_header.values, src_path), |
| | 207 | else => unreachable, |
| | 208 | } |
| 203 | }, | 209 | }, |
| 204 | .cmake => |file_source| { | 210 | .cmake => |file_source| { |
| 205 | try output.appendSlice(c_generated_line); | 211 | try output.appendSlice(c_generated_line); |
| ... | @@ -257,7 +263,7 @@ fn make(step: *Step, options: Step.MakeOptions) !void { | ... | @@ -257,7 +263,7 @@ fn make(step: *Step, options: Step.MakeOptions) !void { |
| 257 | try man.writeManifest(); | 263 | try man.writeManifest(); |
| 258 | } | 264 | } |
| 259 | | 265 | |
| 260 | fn render_autoconf( | 266 | fn render_autoconf_undef( |
| 261 | step: *Step, | 267 | step: *Step, |
| 262 | contents: []const u8, | 268 | contents: []const u8, |
| 263 | output: *std.ArrayList(u8), | 269 | output: *std.ArrayList(u8), |
| ... | @@ -309,6 +315,62 @@ fn render_autoconf( | ... | @@ -309,6 +315,62 @@ fn render_autoconf( |
| 309 | } | 315 | } |
| 310 | } | 316 | } |
| 311 | | 317 | |
| | 318 | fn render_autoconf_at( |
| | 319 | step: *Step, |
| | 320 | contents: []const u8, |
| | 321 | output: *std.ArrayList(u8), |
| | 322 | values: std.StringArrayHashMap(Value), |
| | 323 | src_path: []const u8, |
| | 324 | ) !void { |
| | 325 | const build = step.owner; |
| | 326 | const allocator = build.allocator; |
| | 327 | |
| | 328 | const used = allocator.alloc(bool, values.count()) catch @panic("OOM"); |
| | 329 | for (used) |*u| u.* = false; |
| | 330 | defer allocator.free(used); |
| | 331 | |
| | 332 | var any_errors = false; |
| | 333 | var line_index: u32 = 0; |
| | 334 | var line_it = std.mem.splitScalar(u8, contents, '\n'); |
| | 335 | while (line_it.next()) |line| : (line_index += 1) { |
| | 336 | const last_line = line_it.index == line_it.buffer.len; |
| | 337 | |
| | 338 | const old_len = output.items.len; |
| | 339 | expand_variables_autoconf_at(output, line, values, used) catch |err| switch (err) { |
| | 340 | error.MissingValue => { |
| | 341 | const name = output.items[old_len..]; |
| | 342 | defer output.shrinkRetainingCapacity(old_len); |
| | 343 | try step.addError("{s}:{d}: error: unspecified config header value: '{s}'", .{ |
| | 344 | src_path, line_index + 1, name, |
| | 345 | }); |
| | 346 | any_errors = true; |
| | 347 | continue; |
| | 348 | }, |
| | 349 | else => { |
| | 350 | try step.addError("{s}:{d}: unable to substitute variable: error: {s}", .{ |
| | 351 | src_path, line_index + 1, @errorName(err), |
| | 352 | }); |
| | 353 | any_errors = true; |
| | 354 | continue; |
| | 355 | }, |
| | 356 | }; |
| | 357 | if (!last_line) { |
| | 358 | try output.append('\n'); |
| | 359 | } |
| | 360 | } |
| | 361 | |
| | 362 | for (values.unmanaged.entries.slice().items(.key), used) |name, u| { |
| | 363 | if (!u) { |
| | 364 | try step.addError("{s}: error: config header value unused: '{s}'", .{ src_path, name }); |
| | 365 | any_errors = true; |
| | 366 | } |
| | 367 | } |
| | 368 | |
| | 369 | if (any_errors) { |
| | 370 | return error.MakeFailed; |
| | 371 | } |
| | 372 | } |
| | 373 | |
| 312 | fn render_cmake( | 374 | fn render_cmake( |
| 313 | step: *Step, | 375 | step: *Step, |
| 314 | contents: []const u8, | 376 | contents: []const u8, |
| ... | @@ -541,6 +603,59 @@ fn renderValueNasm(output: *std.ArrayList(u8), name: []const u8, value: Value) ! | ... | @@ -541,6 +603,59 @@ fn renderValueNasm(output: *std.ArrayList(u8), name: []const u8, value: Value) ! |
| 541 | } | 603 | } |
| 542 | } | 604 | } |
| 543 | | 605 | |
| | 606 | fn expand_variables_autoconf_at( |
| | 607 | output: *std.ArrayList(u8), |
| | 608 | contents: []const u8, |
| | 609 | values: std.StringArrayHashMap(Value), |
| | 610 | used: []bool, |
| | 611 | ) !void { |
| | 612 | const valid_varname_chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_"; |
| | 613 | |
| | 614 | var curr: usize = 0; |
| | 615 | var source_offset: usize = 0; |
| | 616 | while (curr < contents.len) : (curr += 1) { |
| | 617 | if (contents[curr] != '@') continue; |
| | 618 | if (std.mem.indexOfScalarPos(u8, contents, curr + 1, '@')) |close_pos| { |
| | 619 | if (close_pos == curr + 1) { |
| | 620 | // closed immediately, preserve as a literal |
| | 621 | continue; |
| | 622 | } |
| | 623 | const valid_varname_end = std.mem.indexOfNonePos(u8, contents, curr + 1, valid_varname_chars) orelse 0; |
| | 624 | if (valid_varname_end != close_pos) { |
| | 625 | // contains invalid characters, preserve as a literal |
| | 626 | continue; |
| | 627 | } |
| | 628 | |
| | 629 | const key = contents[curr + 1 .. close_pos]; |
| | 630 | const index = values.getIndex(key) orelse { |
| | 631 | // Report the missing key to the caller. |
| | 632 | try output.appendSlice(key); |
| | 633 | return error.MissingValue; |
| | 634 | }; |
| | 635 | const value = values.unmanaged.entries.slice().items(.value)[index]; |
| | 636 | used[index] = true; |
| | 637 | try output.appendSlice(contents[source_offset..curr]); |
| | 638 | switch (value) { |
| | 639 | .undef, .defined => {}, |
| | 640 | .boolean => |b| { |
| | 641 | try output.append(if (b) '1' else '0'); |
| | 642 | }, |
| | 643 | .int => |i| { |
| | 644 | try output.writer().print("{d}", .{i}); |
| | 645 | }, |
| | 646 | .ident, .string => |s| { |
| | 647 | try output.appendSlice(s); |
| | 648 | }, |
| | 649 | } |
| | 650 | |
| | 651 | curr = close_pos; |
| | 652 | source_offset = close_pos + 1; |
| | 653 | } |
| | 654 | } |
| | 655 | |
| | 656 | try output.appendSlice(contents[source_offset..]); |
| | 657 | } |
| | 658 | |
| 544 | fn expand_variables_cmake( | 659 | fn expand_variables_cmake( |
| 545 | allocator: Allocator, | 660 | allocator: Allocator, |
| 546 | contents: []const u8, | 661 | contents: []const u8, |
| ... | @@ -672,7 +787,26 @@ fn expand_variables_cmake( | ... | @@ -672,7 +787,26 @@ fn expand_variables_cmake( |
| 672 | return result.toOwnedSlice(); | 787 | return result.toOwnedSlice(); |
| 673 | } | 788 | } |
| 674 | | 789 | |
| 675 | fn testReplaceVariables( | 790 | fn testReplaceVariablesAutoconfAt( |
| | 791 | allocator: Allocator, |
| | 792 | contents: []const u8, |
| | 793 | expected: []const u8, |
| | 794 | values: std.StringArrayHashMap(Value), |
| | 795 | ) !void { |
| | 796 | var output = std.ArrayList(u8).init(allocator); |
| | 797 | defer output.deinit(); |
| | 798 | |
| | 799 | const used = try allocator.alloc(bool, values.count()); |
| | 800 | for (used) |*u| u.* = false; |
| | 801 | defer allocator.free(used); |
| | 802 | |
| | 803 | try expand_variables_autoconf_at(&output, contents, values, used); |
| | 804 | |
| | 805 | for (used) |u| if (!u) return error.UnusedValue; |
| | 806 | try std.testing.expectEqualStrings(expected, output.items); |
| | 807 | } |
| | 808 | |
| | 809 | fn testReplaceVariablesCMake( |
| 676 | allocator: Allocator, | 810 | allocator: Allocator, |
| 677 | contents: []const u8, | 811 | contents: []const u8, |
| 678 | expected: []const u8, | 812 | expected: []const u8, |
| ... | @@ -684,6 +818,118 @@ fn testReplaceVariables( | ... | @@ -684,6 +818,118 @@ fn testReplaceVariables( |
| 684 | try std.testing.expectEqualStrings(expected, actual); | 818 | try std.testing.expectEqualStrings(expected, actual); |
| 685 | } | 819 | } |
| 686 | | 820 | |
| | 821 | test "expand_variables_autoconf_at simple cases" { |
| | 822 | const allocator = std.testing.allocator; |
| | 823 | var values = std.StringArrayHashMap(Value).init(allocator); |
| | 824 | defer values.deinit(); |
| | 825 | |
| | 826 | // empty strings are preserved |
| | 827 | try testReplaceVariablesAutoconfAt(allocator, "", "", values); |
| | 828 | |
| | 829 | // line with misc content is preserved |
| | 830 | try testReplaceVariablesAutoconfAt(allocator, "no substitution", "no substitution", values); |
| | 831 | |
| | 832 | // empty @ sigils are preserved |
| | 833 | try testReplaceVariablesAutoconfAt(allocator, "@", "@", values); |
| | 834 | try testReplaceVariablesAutoconfAt(allocator, "@@", "@@", values); |
| | 835 | try testReplaceVariablesAutoconfAt(allocator, "@@@", "@@@", values); |
| | 836 | try testReplaceVariablesAutoconfAt(allocator, "@@@@", "@@@@", values); |
| | 837 | |
| | 838 | // simple substitution |
| | 839 | try values.putNoClobber("undef", .undef); |
| | 840 | try testReplaceVariablesAutoconfAt(allocator, "@undef@", "", values); |
| | 841 | values.clearRetainingCapacity(); |
| | 842 | |
| | 843 | try values.putNoClobber("defined", .defined); |
| | 844 | try testReplaceVariablesAutoconfAt(allocator, "@defined@", "", values); |
| | 845 | values.clearRetainingCapacity(); |
| | 846 | |
| | 847 | try values.putNoClobber("true", Value{ .boolean = true }); |
| | 848 | try testReplaceVariablesAutoconfAt(allocator, "@true@", "1", values); |
| | 849 | values.clearRetainingCapacity(); |
| | 850 | |
| | 851 | try values.putNoClobber("false", Value{ .boolean = false }); |
| | 852 | try testReplaceVariablesAutoconfAt(allocator, "@false@", "0", values); |
| | 853 | values.clearRetainingCapacity(); |
| | 854 | |
| | 855 | try values.putNoClobber("int", Value{ .int = 42 }); |
| | 856 | try testReplaceVariablesAutoconfAt(allocator, "@int@", "42", values); |
| | 857 | values.clearRetainingCapacity(); |
| | 858 | |
| | 859 | try values.putNoClobber("ident", Value{ .string = "value" }); |
| | 860 | try testReplaceVariablesAutoconfAt(allocator, "@ident@", "value", values); |
| | 861 | values.clearRetainingCapacity(); |
| | 862 | |
| | 863 | try values.putNoClobber("string", Value{ .string = "text" }); |
| | 864 | try testReplaceVariablesAutoconfAt(allocator, "@string@", "text", values); |
| | 865 | values.clearRetainingCapacity(); |
| | 866 | |
| | 867 | // double packed substitution |
| | 868 | try values.putNoClobber("string", Value{ .string = "text" }); |
| | 869 | try testReplaceVariablesAutoconfAt(allocator, "@string@@string@", "texttext", values); |
| | 870 | values.clearRetainingCapacity(); |
| | 871 | |
| | 872 | // triple packed substitution |
| | 873 | try values.putNoClobber("int", Value{ .int = 42 }); |
| | 874 | try values.putNoClobber("string", Value{ .string = "text" }); |
| | 875 | try testReplaceVariablesAutoconfAt(allocator, "@string@@int@@string@", "text42text", values); |
| | 876 | values.clearRetainingCapacity(); |
| | 877 | |
| | 878 | // double separated substitution |
| | 879 | try values.putNoClobber("int", Value{ .int = 42 }); |
| | 880 | try testReplaceVariablesAutoconfAt(allocator, "@int@.@int@", "42.42", values); |
| | 881 | values.clearRetainingCapacity(); |
| | 882 | |
| | 883 | // triple separated substitution |
| | 884 | try values.putNoClobber("true", Value{ .boolean = true }); |
| | 885 | try values.putNoClobber("int", Value{ .int = 42 }); |
| | 886 | try testReplaceVariablesAutoconfAt(allocator, "@int@.@true@.@int@", "42.1.42", values); |
| | 887 | values.clearRetainingCapacity(); |
| | 888 | |
| | 889 | // misc prefix is preserved |
| | 890 | try values.putNoClobber("false", Value{ .boolean = false }); |
| | 891 | try testReplaceVariablesAutoconfAt(allocator, "false is @false@", "false is 0", values); |
| | 892 | values.clearRetainingCapacity(); |
| | 893 | |
| | 894 | // misc suffix is preserved |
| | 895 | try values.putNoClobber("true", Value{ .boolean = true }); |
| | 896 | try testReplaceVariablesAutoconfAt(allocator, "@true@ is true", "1 is true", values); |
| | 897 | values.clearRetainingCapacity(); |
| | 898 | |
| | 899 | // surrounding content is preserved |
| | 900 | try values.putNoClobber("int", Value{ .int = 42 }); |
| | 901 | try testReplaceVariablesAutoconfAt(allocator, "what is 6*7? @int@!", "what is 6*7? 42!", values); |
| | 902 | values.clearRetainingCapacity(); |
| | 903 | |
| | 904 | // incomplete key is preserved |
| | 905 | try testReplaceVariablesAutoconfAt(allocator, "@undef", "@undef", values); |
| | 906 | |
| | 907 | // unknown key leads to an error |
| | 908 | try std.testing.expectError(error.MissingValue, testReplaceVariablesAutoconfAt(allocator, "@bad@", "", values)); |
| | 909 | |
| | 910 | // unused key leads to an error |
| | 911 | try values.putNoClobber("int", Value{ .int = 42 }); |
| | 912 | try values.putNoClobber("false", Value{ .boolean = false }); |
| | 913 | try std.testing.expectError(error.UnusedValue, testReplaceVariablesAutoconfAt(allocator, "@int", "", values)); |
| | 914 | values.clearRetainingCapacity(); |
| | 915 | } |
| | 916 | |
| | 917 | test "expand_variables_autoconf_at edge cases" { |
| | 918 | const allocator = std.testing.allocator; |
| | 919 | var values = std.StringArrayHashMap(Value).init(allocator); |
| | 920 | defer values.deinit(); |
| | 921 | |
| | 922 | // @-vars resolved only when they wrap valid characters, otherwise considered literals |
| | 923 | try values.putNoClobber("string", Value{ .string = "text" }); |
| | 924 | try testReplaceVariablesAutoconfAt(allocator, "@@string@@", "@text@", values); |
| | 925 | values.clearRetainingCapacity(); |
| | 926 | |
| | 927 | // expanded variables are considered strings after expansion |
| | 928 | try values.putNoClobber("string_at", Value{ .string = "@string@" }); |
| | 929 | try testReplaceVariablesAutoconfAt(allocator, "@string_at@", "@string@", values); |
| | 930 | values.clearRetainingCapacity(); |
| | 931 | } |
| | 932 | |
| 687 | test "expand_variables_cmake simple cases" { | 933 | test "expand_variables_cmake simple cases" { |
| 688 | const allocator = std.testing.allocator; | 934 | const allocator = std.testing.allocator; |
| 689 | var values = std.StringArrayHashMap(Value).init(allocator); | 935 | var values = std.StringArrayHashMap(Value).init(allocator); |
| ... | @@ -698,78 +944,78 @@ test "expand_variables_cmake simple cases" { | ... | @@ -698,78 +944,78 @@ test "expand_variables_cmake simple cases" { |
| 698 | try values.putNoClobber("string", Value{ .string = "text" }); | 944 | try values.putNoClobber("string", Value{ .string = "text" }); |
| 699 | | 945 | |
| 700 | // empty strings are preserved | 946 | // empty strings are preserved |
| 701 | try testReplaceVariables(allocator, "", "", values); | 947 | try testReplaceVariablesCMake(allocator, "", "", values); |
| 702 | | 948 | |
| 703 | // line with misc content is preserved | 949 | // line with misc content is preserved |
| 704 | try testReplaceVariables(allocator, "no substitution", "no substitution", values); | 950 | try testReplaceVariablesCMake(allocator, "no substitution", "no substitution", values); |
| 705 | | 951 | |
| 706 | // empty ${} wrapper leads to an error | 952 | // empty ${} wrapper leads to an error |
| 707 | try std.testing.expectError(error.MissingKey, testReplaceVariables(allocator, "${}", "", values)); | 953 | try std.testing.expectError(error.MissingKey, testReplaceVariablesCMake(allocator, "${}", "", values)); |
| 708 | | 954 | |
| 709 | // empty @ sigils are preserved | 955 | // empty @ sigils are preserved |
| 710 | try testReplaceVariables(allocator, "@", "@", values); | 956 | try testReplaceVariablesCMake(allocator, "@", "@", values); |
| 711 | try testReplaceVariables(allocator, "@@", "@@", values); | 957 | try testReplaceVariablesCMake(allocator, "@@", "@@", values); |
| 712 | try testReplaceVariables(allocator, "@@@", "@@@", values); | 958 | try testReplaceVariablesCMake(allocator, "@@@", "@@@", values); |
| 713 | try testReplaceVariables(allocator, "@@@@", "@@@@", values); | 959 | try testReplaceVariablesCMake(allocator, "@@@@", "@@@@", values); |
| 714 | | 960 | |
| 715 | // simple substitution | 961 | // simple substitution |
| 716 | try testReplaceVariables(allocator, "@undef@", "", values); | 962 | try testReplaceVariablesCMake(allocator, "@undef@", "", values); |
| 717 | try testReplaceVariables(allocator, "${undef}", "", values); | 963 | try testReplaceVariablesCMake(allocator, "${undef}", "", values); |
| 718 | try testReplaceVariables(allocator, "@defined@", "", values); | 964 | try testReplaceVariablesCMake(allocator, "@defined@", "", values); |
| 719 | try testReplaceVariables(allocator, "${defined}", "", values); | 965 | try testReplaceVariablesCMake(allocator, "${defined}", "", values); |
| 720 | try testReplaceVariables(allocator, "@true@", "1", values); | 966 | try testReplaceVariablesCMake(allocator, "@true@", "1", values); |
| 721 | try testReplaceVariables(allocator, "${true}", "1", values); | 967 | try testReplaceVariablesCMake(allocator, "${true}", "1", values); |
| 722 | try testReplaceVariables(allocator, "@false@", "0", values); | 968 | try testReplaceVariablesCMake(allocator, "@false@", "0", values); |
| 723 | try testReplaceVariables(allocator, "${false}", "0", values); | 969 | try testReplaceVariablesCMake(allocator, "${false}", "0", values); |
| 724 | try testReplaceVariables(allocator, "@int@", "42", values); | 970 | try testReplaceVariablesCMake(allocator, "@int@", "42", values); |
| 725 | try testReplaceVariables(allocator, "${int}", "42", values); | 971 | try testReplaceVariablesCMake(allocator, "${int}", "42", values); |
| 726 | try testReplaceVariables(allocator, "@ident@", "value", values); | 972 | try testReplaceVariablesCMake(allocator, "@ident@", "value", values); |
| 727 | try testReplaceVariables(allocator, "${ident}", "value", values); | 973 | try testReplaceVariablesCMake(allocator, "${ident}", "value", values); |
| 728 | try testReplaceVariables(allocator, "@string@", "text", values); | 974 | try testReplaceVariablesCMake(allocator, "@string@", "text", values); |
| 729 | try testReplaceVariables(allocator, "${string}", "text", values); | 975 | try testReplaceVariablesCMake(allocator, "${string}", "text", values); |
| 730 | | 976 | |
| 731 | // double packed substitution | 977 | // double packed substitution |
| 732 | try testReplaceVariables(allocator, "@string@@string@", "texttext", values); | 978 | try testReplaceVariablesCMake(allocator, "@string@@string@", "texttext", values); |
| 733 | try testReplaceVariables(allocator, "${string}${string}", "texttext", values); | 979 | try testReplaceVariablesCMake(allocator, "${string}${string}", "texttext", values); |
| 734 | | 980 | |
| 735 | // triple packed substitution | 981 | // triple packed substitution |
| 736 | try testReplaceVariables(allocator, "@string@@int@@string@", "text42text", values); | 982 | try testReplaceVariablesCMake(allocator, "@string@@int@@string@", "text42text", values); |
| 737 | try testReplaceVariables(allocator, "@string@${int}@string@", "text42text", values); | 983 | try testReplaceVariablesCMake(allocator, "@string@${int}@string@", "text42text", values); |
| 738 | try testReplaceVariables(allocator, "${string}@int@${string}", "text42text", values); | 984 | try testReplaceVariablesCMake(allocator, "${string}@int@${string}", "text42text", values); |
| 739 | try testReplaceVariables(allocator, "${string}${int}${string}", "text42text", values); | 985 | try testReplaceVariablesCMake(allocator, "${string}${int}${string}", "text42text", values); |
| 740 | | 986 | |
| 741 | // double separated substitution | 987 | // double separated substitution |
| 742 | try testReplaceVariables(allocator, "@int@.@int@", "42.42", values); | 988 | try testReplaceVariablesCMake(allocator, "@int@.@int@", "42.42", values); |
| 743 | try testReplaceVariables(allocator, "${int}.${int}", "42.42", values); | 989 | try testReplaceVariablesCMake(allocator, "${int}.${int}", "42.42", values); |
| 744 | | 990 | |
| 745 | // triple separated substitution | 991 | // triple separated substitution |
| 746 | try testReplaceVariables(allocator, "@int@.@true@.@int@", "42.1.42", values); | 992 | try testReplaceVariablesCMake(allocator, "@int@.@true@.@int@", "42.1.42", values); |
| 747 | try testReplaceVariables(allocator, "@int@.${true}.@int@", "42.1.42", values); | 993 | try testReplaceVariablesCMake(allocator, "@int@.${true}.@int@", "42.1.42", values); |
| 748 | try testReplaceVariables(allocator, "${int}.@true@.${int}", "42.1.42", values); | 994 | try testReplaceVariablesCMake(allocator, "${int}.@true@.${int}", "42.1.42", values); |
| 749 | try testReplaceVariables(allocator, "${int}.${true}.${int}", "42.1.42", values); | 995 | try testReplaceVariablesCMake(allocator, "${int}.${true}.${int}", "42.1.42", values); |
| 750 | | 996 | |
| 751 | // misc prefix is preserved | 997 | // misc prefix is preserved |
| 752 | try testReplaceVariables(allocator, "false is @false@", "false is 0", values); | 998 | try testReplaceVariablesCMake(allocator, "false is @false@", "false is 0", values); |
| 753 | try testReplaceVariables(allocator, "false is ${false}", "false is 0", values); | 999 | try testReplaceVariablesCMake(allocator, "false is ${false}", "false is 0", values); |
| 754 | | 1000 | |
| 755 | // misc suffix is preserved | 1001 | // misc suffix is preserved |
| 756 | try testReplaceVariables(allocator, "@true@ is true", "1 is true", values); | 1002 | try testReplaceVariablesCMake(allocator, "@true@ is true", "1 is true", values); |
| 757 | try testReplaceVariables(allocator, "${true} is true", "1 is true", values); | 1003 | try testReplaceVariablesCMake(allocator, "${true} is true", "1 is true", values); |
| 758 | | 1004 | |
| 759 | // surrounding content is preserved | 1005 | // surrounding content is preserved |
| 760 | try testReplaceVariables(allocator, "what is 6*7? @int@!", "what is 6*7? 42!", values); | 1006 | try testReplaceVariablesCMake(allocator, "what is 6*7? @int@!", "what is 6*7? 42!", values); |
| 761 | try testReplaceVariables(allocator, "what is 6*7? ${int}!", "what is 6*7? 42!", values); | 1007 | try testReplaceVariablesCMake(allocator, "what is 6*7? ${int}!", "what is 6*7? 42!", values); |
| 762 | | 1008 | |
| 763 | // incomplete key is preserved | 1009 | // incomplete key is preserved |
| 764 | try testReplaceVariables(allocator, "@undef", "@undef", values); | 1010 | try testReplaceVariablesCMake(allocator, "@undef", "@undef", values); |
| 765 | try testReplaceVariables(allocator, "${undef", "${undef", values); | 1011 | try testReplaceVariablesCMake(allocator, "${undef", "${undef", values); |
| 766 | try testReplaceVariables(allocator, "{undef}", "{undef}", values); | 1012 | try testReplaceVariablesCMake(allocator, "{undef}", "{undef}", values); |
| 767 | try testReplaceVariables(allocator, "undef@", "undef@", values); | 1013 | try testReplaceVariablesCMake(allocator, "undef@", "undef@", values); |
| 768 | try testReplaceVariables(allocator, "undef}", "undef}", values); | 1014 | try testReplaceVariablesCMake(allocator, "undef}", "undef}", values); |
| 769 | | 1015 | |
| 770 | // unknown key leads to an error | 1016 | // unknown key leads to an error |
| 771 | try std.testing.expectError(error.MissingValue, testReplaceVariables(allocator, "@bad@", "", values)); | 1017 | try std.testing.expectError(error.MissingValue, testReplaceVariablesCMake(allocator, "@bad@", "", values)); |
| 772 | try std.testing.expectError(error.MissingValue, testReplaceVariables(allocator, "${bad}", "", values)); | 1018 | try std.testing.expectError(error.MissingValue, testReplaceVariablesCMake(allocator, "${bad}", "", values)); |
| 773 | } | 1019 | } |
| 774 | | 1020 | |
| 775 | test "expand_variables_cmake edge cases" { | 1021 | test "expand_variables_cmake edge cases" { |
| ... | @@ -796,41 +1042,41 @@ test "expand_variables_cmake edge cases" { | ... | @@ -796,41 +1042,41 @@ test "expand_variables_cmake edge cases" { |
| 796 | try values.putNoClobber("nest_proxy", Value{ .string = "nest_underscore_proxy" }); | 1042 | try values.putNoClobber("nest_proxy", Value{ .string = "nest_underscore_proxy" }); |
| 797 | | 1043 | |
| 798 | // @-vars resolved only when they wrap valid characters, otherwise considered literals | 1044 | // @-vars resolved only when they wrap valid characters, otherwise considered literals |
| 799 | try testReplaceVariables(allocator, "@@string@@", "@text@", values); | 1045 | try testReplaceVariablesCMake(allocator, "@@string@@", "@text@", values); |
| 800 | try testReplaceVariables(allocator, "@${string}@", "@text@", values); | 1046 | try testReplaceVariablesCMake(allocator, "@${string}@", "@text@", values); |
| 801 | | 1047 | |
| 802 | // @-vars are resolved inside ${}-vars | 1048 | // @-vars are resolved inside ${}-vars |
| 803 | try testReplaceVariables(allocator, "${@string_proxy@}", "text", values); | 1049 | try testReplaceVariablesCMake(allocator, "${@string_proxy@}", "text", values); |
| 804 | | 1050 | |
| 805 | // expanded variables are considered strings after expansion | 1051 | // expanded variables are considered strings after expansion |
| 806 | try testReplaceVariables(allocator, "@string_at@", "@string@", values); | 1052 | try testReplaceVariablesCMake(allocator, "@string_at@", "@string@", values); |
| 807 | try testReplaceVariables(allocator, "${string_at}", "@string@", values); | 1053 | try testReplaceVariablesCMake(allocator, "${string_at}", "@string@", values); |
| 808 | try testReplaceVariables(allocator, "$@string_curly@", "${string}", values); | 1054 | try testReplaceVariablesCMake(allocator, "$@string_curly@", "${string}", values); |
| 809 | try testReplaceVariables(allocator, "$${string_curly}", "${string}", values); | 1055 | try testReplaceVariablesCMake(allocator, "$${string_curly}", "${string}", values); |
| 810 | try testReplaceVariables(allocator, "${string_var}", "${string}", values); | 1056 | try testReplaceVariablesCMake(allocator, "${string_var}", "${string}", values); |
| 811 | try testReplaceVariables(allocator, "@string_var@", "${string}", values); | 1057 | try testReplaceVariablesCMake(allocator, "@string_var@", "${string}", values); |
| 812 | try testReplaceVariables(allocator, "${dollar}{${string}}", "${text}", values); | 1058 | try testReplaceVariablesCMake(allocator, "${dollar}{${string}}", "${text}", values); |
| 813 | try testReplaceVariables(allocator, "@dollar@{${string}}", "${text}", values); | 1059 | try testReplaceVariablesCMake(allocator, "@dollar@{${string}}", "${text}", values); |
| 814 | try testReplaceVariables(allocator, "@dollar@{@string@}", "${text}", values); | 1060 | try testReplaceVariablesCMake(allocator, "@dollar@{@string@}", "${text}", values); |
| 815 | | 1061 | |
| 816 | // when expanded variables contain invalid characters, they prevent further expansion | 1062 | // when expanded variables contain invalid characters, they prevent further expansion |
| 817 | try std.testing.expectError(error.MissingValue, testReplaceVariables(allocator, "${${string_var}}", "", values)); | 1063 | try std.testing.expectError(error.MissingValue, testReplaceVariablesCMake(allocator, "${${string_var}}", "", values)); |
| 818 | try std.testing.expectError(error.MissingValue, testReplaceVariables(allocator, "${@string_var@}", "", values)); | 1064 | try std.testing.expectError(error.MissingValue, testReplaceVariablesCMake(allocator, "${@string_var@}", "", values)); |
| 819 | | 1065 | |
| 820 | // nested expanded variables are expanded from the inside out | 1066 | // nested expanded variables are expanded from the inside out |
| 821 | try testReplaceVariables(allocator, "${string${underscore}proxy}", "string", values); | 1067 | try testReplaceVariablesCMake(allocator, "${string${underscore}proxy}", "string", values); |
| 822 | try testReplaceVariables(allocator, "${string@underscore@proxy}", "string", values); | 1068 | try testReplaceVariablesCMake(allocator, "${string@underscore@proxy}", "string", values); |
| 823 | | 1069 | |
| 824 | // nested vars are only expanded when ${} is closed | 1070 | // nested vars are only expanded when ${} is closed |
| 825 | try std.testing.expectError(error.MissingValue, testReplaceVariables(allocator, "@nest@underscore@proxy@", "", values)); | 1071 | try std.testing.expectError(error.MissingValue, testReplaceVariablesCMake(allocator, "@nest@underscore@proxy@", "", values)); |
| 826 | try testReplaceVariables(allocator, "${nest${underscore}proxy}", "nest_underscore_proxy", values); | 1072 | try testReplaceVariablesCMake(allocator, "${nest${underscore}proxy}", "nest_underscore_proxy", values); |
| 827 | try std.testing.expectError(error.MissingValue, testReplaceVariables(allocator, "@nest@@nest_underscore@underscore@proxy@@proxy@", "", values)); | 1073 | try std.testing.expectError(error.MissingValue, testReplaceVariablesCMake(allocator, "@nest@@nest_underscore@underscore@proxy@@proxy@", "", values)); |
| 828 | try testReplaceVariables(allocator, "${nest${${nest_underscore${underscore}proxy}}proxy}", "nest_underscore_proxy", values); | 1074 | try testReplaceVariablesCMake(allocator, "${nest${${nest_underscore${underscore}proxy}}proxy}", "nest_underscore_proxy", values); |
| 829 | | 1075 | |
| 830 | // invalid characters lead to an error | 1076 | // invalid characters lead to an error |
| 831 | try std.testing.expectError(error.InvalidCharacter, testReplaceVariables(allocator, "${str*ing}", "", values)); | 1077 | try std.testing.expectError(error.InvalidCharacter, testReplaceVariablesCMake(allocator, "${str*ing}", "", values)); |
| 832 | try std.testing.expectError(error.InvalidCharacter, testReplaceVariables(allocator, "${str$ing}", "", values)); | 1078 | try std.testing.expectError(error.InvalidCharacter, testReplaceVariablesCMake(allocator, "${str$ing}", "", values)); |
| 833 | try std.testing.expectError(error.InvalidCharacter, testReplaceVariables(allocator, "${str@ing}", "", values)); | 1079 | try std.testing.expectError(error.InvalidCharacter, testReplaceVariablesCMake(allocator, "${str@ing}", "", values)); |
| 834 | } | 1080 | } |
| 835 | | 1081 | |
| 836 | test "expand_variables_cmake escaped characters" { | 1082 | test "expand_variables_cmake escaped characters" { |
| ... | @@ -841,14 +1087,14 @@ test "expand_variables_cmake escaped characters" { | ... | @@ -841,14 +1087,14 @@ test "expand_variables_cmake escaped characters" { |
| 841 | try values.putNoClobber("string", Value{ .string = "text" }); | 1087 | try values.putNoClobber("string", Value{ .string = "text" }); |
| 842 | | 1088 | |
| 843 | // backslash is an invalid character for @ lookup | 1089 | // backslash is an invalid character for @ lookup |
| 844 | try testReplaceVariables(allocator, "\\@string\\@", "\\@string\\@", values); | 1090 | try testReplaceVariablesCMake(allocator, "\\@string\\@", "\\@string\\@", values); |
| 845 | | 1091 | |
| 846 | // backslash is preserved, but doesn't affect ${} variable expansion | 1092 | // backslash is preserved, but doesn't affect ${} variable expansion |
| 847 | try testReplaceVariables(allocator, "\\${string}", "\\text", values); | 1093 | try testReplaceVariablesCMake(allocator, "\\${string}", "\\text", values); |
| 848 | | 1094 | |
| 849 | // backslash breaks ${} opening bracket identification | 1095 | // backslash breaks ${} opening bracket identification |
| 850 | try testReplaceVariables(allocator, "$\\{string}", "$\\{string}", values); | 1096 | try testReplaceVariablesCMake(allocator, "$\\{string}", "$\\{string}", values); |
| 851 | | 1097 | |
| 852 | // backslash is skipped when checking for invalid characters, yet it mangles the key | 1098 | // backslash is skipped when checking for invalid characters, yet it mangles the key |
| 853 | try std.testing.expectError(error.MissingValue, testReplaceVariables(allocator, "${string\\}", "", values)); | 1099 | try std.testing.expectError(error.MissingValue, testReplaceVariablesCMake(allocator, "${string\\}", "", values)); |
| 854 | } | 1100 | } |