authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2023-05-16 16:29:20-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2023-05-16 16:41:22-07:00
logae119a9a8d5b8dec21bd314e91afcec122eb8631
treeb47079e19cee86e8f244be48dd29b0666336743d
parent2e692312f104e1e5533c9f389d73488ea4a1ef68

CLI: fix stdin dumping behavior

* no need to move `tmpFilePath` around * no need for calculating max length of `FileExt` tag name * provide a canonical file extension name for `FileExt` so that, e.g. the file will be named `stdin.S` instead of `stdin.assembly_with_cpp`. * move temp file cleanup to a function to reduce defer bloat in a large function. * fix bug caused by mixing relative and absolute paths in the cleanup logic. * remove commented out test and dead code

4 files changed, 62 insertions(+), 134 deletions(-)

lib/std/Build/Cache.zig-10
...@@ -31,16 +31,6 @@ pub const Directory = struct {...@@ -31,16 +31,6 @@ pub const Directory = struct {
31 }31 }
32 }32 }
3333
34 pub fn tmpFilePath(self: Directory, ally: Allocator, suffix: []const u8) error{OutOfMemory}![]const u8 {
35 const s = std.fs.path.sep_str;
36 const rand_int = std.crypto.random.int(u64);
37 if (self.path) |p| {
38 return std.fmt.allocPrint(ally, "{s}" ++ s ++ "tmp" ++ s ++ "{x}-{s}", .{ p, rand_int, suffix });
39 } else {
40 return std.fmt.allocPrint(ally, "tmp" ++ s ++ "{x}-{s}", .{ rand_int, suffix });
41 }
42 }
43
44 /// Whether or not the handle should be closed, or the path should be freed34 /// Whether or not the handle should be closed, or the path should be freed
45 /// is determined by usage, however this function is provided for convenience35 /// is determined by usage, however this function is provided for convenience
46 /// if it happens to be what the caller needs.36 /// if it happens to be what the caller needs.
src/Compilation.zig+31-8
...@@ -3981,7 +3981,7 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: *std.P...@@ -3981,7 +3981,7 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: *std.P
39813981
3982 // We can't know the digest until we do the C compiler invocation,3982 // We can't know the digest until we do the C compiler invocation,
3983 // so we need a temporary filename.3983 // so we need a temporary filename.
3984 const out_obj_path = try comp.local_cache_directory.tmpFilePath(arena, o_basename);3984 const out_obj_path = try comp.tmpFilePath(arena, o_basename);
3985 var zig_cache_tmp_dir = try comp.local_cache_directory.handle.makeOpenPath("tmp", .{});3985 var zig_cache_tmp_dir = try comp.local_cache_directory.handle.makeOpenPath("tmp", .{});
3986 defer zig_cache_tmp_dir.close();3986 defer zig_cache_tmp_dir.close();
39873987
...@@ -4129,6 +4129,16 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: *std.P...@@ -4129,6 +4129,16 @@ fn updateCObject(comp: *Compilation, c_object: *CObject, c_obj_prog_node: *std.P
4129 };4129 };
4130}4130}
41314131
4132pub fn tmpFilePath(comp: *Compilation, ally: Allocator, suffix: []const u8) error{OutOfMemory}![]const u8 {
4133 const s = std.fs.path.sep_str;
4134 const rand_int = std.crypto.random.int(u64);
4135 if (comp.local_cache_directory.path) |p| {
4136 return std.fmt.allocPrint(ally, "{s}" ++ s ++ "tmp" ++ s ++ "{x}-{s}", .{ p, rand_int, suffix });
4137 } else {
4138 return std.fmt.allocPrint(ally, "tmp" ++ s ++ "{x}-{s}", .{ rand_int, suffix });
4139 }
4140}
4141
4132pub fn addTranslateCCArgs(4142pub fn addTranslateCCArgs(
4133 comp: *Compilation,4143 comp: *Compilation,
4134 arena: Allocator,4144 arena: Allocator,
...@@ -4588,13 +4598,26 @@ pub const FileExt = enum {...@@ -4588,13 +4598,26 @@ pub const FileExt = enum {
4588 };4598 };
4589 }4599 }
45904600
4591 // maximum length of @tagName(ext: FileExt)4601 pub fn canonicalName(ext: FileExt, target: Target) [:0]const u8 {
4592 pub const max_len = blk: {4602 return switch (ext) {
4593 var max: u16 = 0;4603 .c => ".c",
4594 inline for (std.meta.tags(FileExt)) |ext|4604 .cpp => ".cpp",
4595 max = std.math.max(@tagName(ext).len, max);4605 .cu => ".cu",
4596 break :blk max;4606 .h => ".h",
4597 };4607 .m => ".m",
4608 .mm => ".mm",
4609 .ll => ".ll",
4610 .bc => ".bc",
4611 .assembly => ".s",
4612 .assembly_with_cpp => ".S",
4613 .shared_library => target.dynamicLibSuffix(),
4614 .object => target.ofmt.fileExt(target.cpu.arch),
4615 .static_library => target.staticLibSuffix(),
4616 .zig => ".zig",
4617 .def => ".def",
4618 .unknown => "",
4619 };
4620 }
4598};4621};
45994622
4600pub fn hasObjectExt(filename: []const u8) bool {4623pub fn hasObjectExt(filename: []const u8) bool {
src/main.zig+31-23
...@@ -705,6 +705,17 @@ const ArgsIterator = struct {...@@ -705,6 +705,17 @@ const ArgsIterator = struct {
705 }705 }
706};706};
707707
708fn cleanupTempStdinFile(
709 temp_stdin_file: ?[]const u8,
710 local_cache_directory: Compilation.Directory,
711) void {
712 if (temp_stdin_file) |file| {
713 // Some garbage may stay in the file system if removal fails; this
714 // is harmless so no warning is needed.
715 local_cache_directory.handle.deleteFile(file) catch {};
716 }
717}
718
708fn buildOutputType(719fn buildOutputType(
709 gpa: Allocator,720 gpa: Allocator,
710 arena: Allocator,721 arena: Allocator,
...@@ -3021,15 +3032,11 @@ fn buildOutputType(...@@ -3021,15 +3032,11 @@ fn buildOutputType(
3021 };3032 };
30223033
3023 var temp_stdin_file: ?[]const u8 = null;3034 var temp_stdin_file: ?[]const u8 = null;
3024 defer {3035 // Note that in one of the happy paths, execve() is used to switch to clang
3025 if (temp_stdin_file) |file| {3036 // in which case this cleanup logic does not run and this temp file is
3026 // some garbage may stay in the file system if removal fails.3037 // leaked. Oh well. It's a minor punishment for using `-x c` which nobody
3027 // Alternatively, we could tell the user that the removal failed,3038 // should be doing.
3028 // but it's not as much of a deal: it's a temporary cache directory3039 defer cleanupTempStdinFile(temp_stdin_file, local_cache_directory);
3029 // at all.
3030 local_cache_directory.handle.deleteFile(file) catch {};
3031 }
3032 }
30333040
3034 for (c_source_files.items) |*src| {3041 for (c_source_files.items) |*src| {
3035 if (!mem.eql(u8, src.src_path, "-")) continue;3042 if (!mem.eql(u8, src.src_path, "-")) continue;
...@@ -3038,21 +3045,22 @@ fn buildOutputType(...@@ -3038,21 +3045,22 @@ fn buildOutputType(
3038 fatal("-E or -x is required when reading from a non-regular file", .{});3045 fatal("-E or -x is required when reading from a non-regular file", .{});
30393046
3040 // "-" is stdin. Dump it to a real file.3047 // "-" is stdin. Dump it to a real file.
3041 const new_file = blk: {3048 const sub_path = blk: {
3042 var buf: ["stdin.".len + Compilation.FileExt.max_len]u8 = undefined;3049 const sep = fs.path.sep_str;
3043 const fname = try std.fmt.bufPrint(&buf, "stdin.{s}", .{@tagName(ext)});3050 const sub_path = try std.fmt.allocPrint(arena, "tmp" ++ sep ++ "{x}-stdin{s}", .{
3044 const new_name = try local_cache_directory.tmpFilePath(arena, fname);3051 std.crypto.random.int(u64), ext.canonicalName(target_info.target),
30453052 });
3046 try local_cache_directory.handle.makePath("tmp");3053 try local_cache_directory.handle.makePath("tmp");
3047 var outfile = try local_cache_directory.handle.createFile(new_name, .{});3054 var f = try local_cache_directory.handle.createFile(sub_path, .{});
3048 defer outfile.close();3055 defer f.close();
3049 errdefer local_cache_directory.handle.deleteFile(new_name) catch {};3056 errdefer local_cache_directory.handle.deleteFile(sub_path) catch {};
30503057 try f.writeFileAll(io.getStdIn(), .{});
3051 try outfile.writeFileAll(io.getStdIn(), .{});3058 break :blk sub_path;
3052 break :blk new_name;
3053 };3059 };
3054 temp_stdin_file = new_file;3060 // Relative to `local_cache_directory`.
3055 src.src_path = new_file;3061 temp_stdin_file = sub_path;
3062 // Relative to current working directory.
3063 src.src_path = try local_cache_directory.join(arena, &.{sub_path});
3056 }3064 }
30573065
3058 if (build_options.have_llvm and emit_asm != .no) {3066 if (build_options.have_llvm and emit_asm != .no) {
...@@ -3908,7 +3916,7 @@ fn cmdTranslateC(comp: *Compilation, arena: Allocator, fancy_output: ?*Translate...@@ -3908,7 +3916,7 @@ fn cmdTranslateC(comp: *Compilation, arena: Allocator, fancy_output: ?*Translate
39083916
3909 const c_src_basename = fs.path.basename(c_source_file.src_path);3917 const c_src_basename = fs.path.basename(c_source_file.src_path);
3910 const dep_basename = try std.fmt.allocPrint(arena, "{s}.d", .{c_src_basename});3918 const dep_basename = try std.fmt.allocPrint(arena, "{s}.d", .{c_src_basename});
3911 const out_dep_path = try comp.local_cache_directory.tmpFilePath(arena, dep_basename);3919 const out_dep_path = try comp.tmpFilePath(arena, dep_basename);
3912 break :blk out_dep_path;3920 break :blk out_dep_path;
3913 };3921 };
39143922
test/tests.zig-93
...@@ -777,52 +777,6 @@ pub fn addCliTests(b: *std.Build) *Step {...@@ -777,52 +777,6 @@ pub fn addCliTests(b: *std.Build) *Step {
777 step.dependOn(&cleanup.step);777 step.dependOn(&cleanup.step);
778 }778 }
779779
780 // Test `zig cc -x c -`
781 // Test author was not able to figure out how to start a child process and
782 // give an fd to it's stdin. fork/exec works, but we are limiting ourselves
783 // to POSIX.
784 //
785 // TODO: the "zig cc <..." step should be a RunStep.create(...)
786 // However, how do I create a command (RunStep) that invokes a command
787 // with a specific file descriptor in the stdin?
788 //if (builtin.os.tag != .windows) {
789 // const tmp_path = b.makeTempPath();
790 // var dir = std.fs.cwd().openDir(tmp_path, .{}) catch @panic("unhandled");
791 // dir.writeFile("truth.c", "int main() { return 42; }") catch @panic("unhandled");
792 // var infile = dir.openFile("truth.c", .{}) catch @panic("unhandled");
793
794 // const outfile = std.fs.path.joinZ(
795 // b.allocator,
796 // &[_][]const u8{ tmp_path, "truth" },
797 // ) catch @panic("unhandled");
798
799 // const pid_result = std.os.fork() catch @panic("unhandled");
800 // if (pid_result == 0) { // child
801 // std.os.dup2(infile.handle, std.os.STDIN_FILENO) catch @panic("unhandled");
802 // const argv = &[_:null]?[*:0]const u8{
803 // b.zig_exe, "cc",
804 // "-o", outfile,
805 // "-x", "c",
806 // "-",
807 // };
808 // const envp = &[_:null]?[*:0]const u8{
809 // std.fmt.allocPrintZ(b.allocator, "ZIG_GLOBAL_CACHE_DIR={s}", .{tmp_path}) catch @panic("unhandled"),
810 // };
811 // const err = std.os.execveZ(b.zig_exe, argv, envp);
812 // std.debug.print("execve error: {any}\n", .{err});
813 // std.os.exit(1);
814 // }
815
816 // const res = std.os.waitpid(pid_result, 0);
817 // assert(0 == res.status);
818
819 // // run the compiled executable and check if it's telling the truth.
820 // _ = exec(b.allocator, tmp_path, 42, &[_][]const u8{outfile}) catch @panic("unhandled");
821
822 // const cleanup = b.addRemoveDirTree(tmp_path);
823 // step.dependOn(&cleanup.step);
824 //}
825
826 {780 {
827 // Test `zig fmt`.781 // Test `zig fmt`.
828 // This test must use a temporary directory rather than a cache782 // This test must use a temporary directory rather than a cache
...@@ -1200,50 +1154,3 @@ pub fn addCases(...@@ -1200,50 +1154,3 @@ pub fn addCases(
1200 check_case_exe,1154 check_case_exe,
1201 );1155 );
1202}1156}
1203
1204fn exec(
1205 allocator: std.mem.Allocator,
1206 cwd: []const u8,
1207 expect_code: u8,
1208 argv: []const []const u8,
1209) !std.ChildProcess.ExecResult {
1210 const max_output_size = 100 * 1024;
1211 const result = std.ChildProcess.exec(.{
1212 .allocator = allocator,
1213 .argv = argv,
1214 .cwd = cwd,
1215 .max_output_bytes = max_output_size,
1216 }) catch |err| {
1217 std.debug.print("The following command failed:\n", .{});
1218 printCmd(cwd, argv);
1219 return err;
1220 };
1221 switch (result.term) {
1222 .Exited => |code| {
1223 if (code != expect_code) {
1224 std.debug.print(
1225 "The following command exited with error code {}, expected {}:\n",
1226 .{ code, expect_code },
1227 );
1228 printCmd(cwd, argv);
1229 std.debug.print("stderr:\n{s}\n", .{result.stderr});
1230 return error.CommandFailed;
1231 }
1232 },
1233 else => {
1234 std.debug.print("The following command terminated unexpectedly:\n", .{});
1235 printCmd(cwd, argv);
1236 std.debug.print("stderr:\n{s}\n", .{result.stderr});
1237 return error.CommandFailed;
1238 },
1239 }
1240 return result;
1241}
1242
1243fn printCmd(cwd: []const u8, argv: []const []const u8) void {
1244 std.debug.print("cd {s} && ", .{cwd});
1245 for (argv) |arg| {
1246 std.debug.print("{s} ", .{arg});
1247 }
1248 std.debug.print("\n", .{});
1249}