authorgravatar for evan@lagerdata.comEvan Haas <evan@lagerdata.com> 2022-05-06 10:14:31-07:00
committergravatar for git@vexu.euVeikka Tuominen <git@vexu.eu> 2022-05-09 18:42:42+03:00
log94b9bcd034960f0467ca789217064b17f2ca7a49
treefb9bc675814214ad303f41bce4c8b3ca75f2c3c8
parentd7f8368da86241f47e97257e737b6fb14bf5f773

stdlib: escape backslashes and double quotes in Builder response file

Fixes #11595

5 files changed, 94 insertions(+), 1 deletions(-)

lib/std/build.zig+26-1
...@@ -3009,6 +3009,7 @@ pub const LibExeObjStep = struct {...@@ -3009,6 +3009,7 @@ pub const LibExeObjStep = struct {
3009 // Windows has an argument length limit of 32,766 characters, macOS 262,144 and Linux3009 // Windows has an argument length limit of 32,766 characters, macOS 262,144 and Linux
3010 // 2,097,152. If our args exceed 30 KiB, we instead write them to a "response file" and3010 // 2,097,152. If our args exceed 30 KiB, we instead write them to a "response file" and
3011 // pass that to zig, e.g. via 'zig build-lib @args.rsp'3011 // pass that to zig, e.g. via 'zig build-lib @args.rsp'
3012 // See @file syntax here: https://gcc.gnu.org/onlinedocs/gcc/Overall-Options.html
3012 var args_length: usize = 0;3013 var args_length: usize = 0;
3013 for (zig_args.items) |arg| {3014 for (zig_args.items) |arg| {
3014 args_length += arg.len + 1; // +1 to account for null terminator3015 args_length += arg.len + 1; // +1 to account for null terminator
...@@ -3020,9 +3021,33 @@ pub const LibExeObjStep = struct {...@@ -3020,9 +3021,33 @@ pub const LibExeObjStep = struct {
3020 );3021 );
3021 try std.fs.cwd().makePath(args_dir);3022 try std.fs.cwd().makePath(args_dir);
30223023
3024 var args_arena = std.heap.ArenaAllocator.init(builder.allocator);
3025 defer args_arena.deinit();
3026
3027 const args_to_escape = zig_args.items[2..];
3028 var escaped_args = try ArrayList([]const u8).initCapacity(args_arena.allocator(), args_to_escape.len);
3029
3030 arg_blk: for (args_to_escape) |arg| {
3031 for (arg) |c, arg_idx| {
3032 if (c == '\\' or c == '"') {
3033 // Slow path for arguments that need to be escaped. We'll need to allocate and copy
3034 var escaped = try ArrayList(u8).initCapacity(args_arena.allocator(), arg.len + 1);
3035 const writer = escaped.writer();
3036 writer.writeAll(arg[0..arg_idx]) catch unreachable;
3037 for (arg[arg_idx..]) |to_escape| {
3038 if (to_escape == '\\' or to_escape == '"') try writer.writeByte('\\');
3039 try writer.writeByte(to_escape);
3040 }
3041 escaped_args.appendAssumeCapacity(escaped.items);
3042 continue :arg_blk;
3043 }
3044 }
3045 escaped_args.appendAssumeCapacity(arg); // no escaping needed so just use original argument
3046 }
3047
3023 // Write the args to zig-cache/args/<SHA256 hash of args> to avoid conflicts with3048 // Write the args to zig-cache/args/<SHA256 hash of args> to avoid conflicts with
3024 // other zig build commands running in parallel.3049 // other zig build commands running in parallel.
3025 const partially_quoted = try std.mem.join(builder.allocator, "\" \"", zig_args.items[2..]);3050 const partially_quoted = try std.mem.join(builder.allocator, "\" \"", escaped_args.items);
3026 const args = try std.mem.concat(builder.allocator, u8, &[_][]const u8{ "\"", partially_quoted, "\"" });3051 const args = try std.mem.concat(builder.allocator, u8, &[_][]const u8{ "\"", partially_quoted, "\"" });
30273052
3028 var args_hash: [Sha256.digest_length]u8 = undefined;3053 var args_hash: [Sha256.digest_length]u8 = undefined;
test/standalone.zig+1
...@@ -49,6 +49,7 @@ pub fn addCases(cases: *tests.StandaloneContext) void {...@@ -49,6 +49,7 @@ pub fn addCases(cases: *tests.StandaloneContext) void {
49 cases.addBuildFile("test/standalone/issue_7030/build.zig", .{});49 cases.addBuildFile("test/standalone/issue_7030/build.zig", .{});
50 cases.addBuildFile("test/standalone/install_raw_hex/build.zig", .{});50 cases.addBuildFile("test/standalone/install_raw_hex/build.zig", .{});
51 cases.addBuildFile("test/standalone/issue_9812/build.zig", .{});51 cases.addBuildFile("test/standalone/issue_9812/build.zig", .{});
52 cases.addBuildFile("test/standalone/issue_11595/build.zig", .{});
52 if (builtin.os.tag != .wasi) {53 if (builtin.os.tag != .wasi) {
53 cases.addBuildFile("test/standalone/load_dynamic_library/build.zig", .{});54 cases.addBuildFile("test/standalone/load_dynamic_library/build.zig", .{});
54 }55 }
test/standalone/issue_11595/build.zig created+52
...@@ -0,0 +1,52 @@
1const std = @import("std");
2const builtin = @import("builtin");
3const Builder = std.build.Builder;
4const CrossTarget = std.zig.CrossTarget;
5
6// TODO integrate this with the std.build executor API
7fn isRunnableTarget(t: CrossTarget) bool {
8 if (t.isNative()) return true;
9
10 return (t.getOsTag() == builtin.os.tag and
11 t.getCpuArch() == builtin.cpu.arch);
12}
13
14pub fn build(b: *Builder) void {
15 const mode = b.standardReleaseOptions();
16 const target = b.standardTargetOptions(.{});
17
18 const exe = b.addExecutable("zigtest", "main.zig");
19 exe.setBuildMode(mode);
20 exe.install();
21
22 const c_sources = [_][]const u8{
23 "test.c",
24 };
25
26 exe.addCSourceFiles(&c_sources, &.{});
27 exe.linkLibC();
28
29 var i: i32 = 0;
30 while (i < 1000) : (i += 1) {
31 exe.defineCMacro("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA");
32 }
33
34 exe.defineCMacro("FOO", "42");
35 exe.defineCMacro("BAR", "\"BAR\"");
36 exe.defineCMacro("BAZ",
37 \\"\"BAZ\""
38 );
39 exe.defineCMacro("QUX", "\"Q\" \"UX\"");
40 exe.defineCMacro("QUUX", "\"QU\\\"UX\"");
41
42 exe.setTarget(target);
43 b.default_step.dependOn(&exe.step);
44
45 const test_step = b.step("test", "Test the program");
46 if (isRunnableTarget(target)) {
47 const run_cmd = exe.run();
48 test_step.dependOn(&run_cmd.step);
49 } else {
50 test_step.dependOn(&exe.step);
51 }
52}
test/standalone/issue_11595/main.zig created+5
...@@ -0,0 +1,5 @@
1extern fn check() c_int;
2
3pub fn main() u8 {
4 return @intCast(u8, check());
5}
test/standalone/issue_11595/test.c created+10
...@@ -0,0 +1,10 @@
1 #include <string.h>
2
3int check(void) {
4 if (FOO != 42) return 1;
5 if (strcmp(BAR, "BAR")) return 2;
6 if (strcmp(BAZ, "\"BAZ\"")) return 3;
7 if (strcmp(QUX, "QUX")) return 4;
8 if (strcmp(QUUX, "QU\"UX")) return 5;
9 return 0;
10}