diff --git a/test/standalone.zig b/test/standalone.zig index 3725456fa1..4f27106e6d 100644 --- a/test/standalone.zig +++ b/test/standalone.zig @@ -198,6 +198,10 @@ pub const build_cases = [_]BuildCase{ .build_root = "test/standalone/windows_spawn", .import = @import("standalone/windows_spawn/build.zig"), }, + .{ + .build_root = "test/standalone/self_exe_symlink", + .import = @import("standalone/self_exe_symlink/build.zig"), + }, .{ .build_root = "test/standalone/c_compiler", .import = @import("standalone/c_compiler/build.zig"), diff --git a/test/standalone/self_exe_symlink/build.zig b/test/standalone/self_exe_symlink/build.zig new file mode 100644 index 0000000000..10e0a6a512 --- /dev/null +++ b/test/standalone/self_exe_symlink/build.zig @@ -0,0 +1,43 @@ +const std = @import("std"); + +pub const requires_symlinks = true; + +pub fn build(b: *std.Build) void { + const test_step = b.step("test", "Test it"); + b.default_step = test_step; + + const optimize: std.builtin.OptimizeMode = .Debug; + const target: std.zig.CrossTarget = .{}; + + // The test requires getFdPath in order to to get the path of the + // File returned by openSelfExe + if (!std.os.isGetFdPathSupportedOnTarget(target.getOs())) return; + + const main = b.addExecutable(.{ + .name = "main", + .root_source_file = .{ .path = "main.zig" }, + .optimize = optimize, + .target = target, + }); + + const create_symlink_exe = b.addExecutable(.{ + .name = "create-symlink", + .root_source_file = .{ .path = "create-symlink.zig" }, + .optimize = optimize, + .target = target, + }); + + var run_create_symlink = b.addRunArtifact(create_symlink_exe); + run_create_symlink.addArtifactArg(main); + const symlink_path = run_create_symlink.addOutputFileArg("main-symlink"); + run_create_symlink.expectExitCode(0); + run_create_symlink.skip_foreign_checks = true; + + var run_from_symlink = std.Build.Step.Run.create(b, "run symlink"); + run_from_symlink.addFileArg(symlink_path); + run_from_symlink.expectExitCode(0); + run_from_symlink.skip_foreign_checks = true; + run_from_symlink.step.dependOn(&run_create_symlink.step); + + test_step.dependOn(&run_from_symlink.step); +} diff --git a/test/standalone/self_exe_symlink/create-symlink.zig b/test/standalone/self_exe_symlink/create-symlink.zig new file mode 100644 index 0000000000..e558df04d6 --- /dev/null +++ b/test/standalone/self_exe_symlink/create-symlink.zig @@ -0,0 +1,15 @@ +const std = @import("std"); + +pub fn main() anyerror!void { + var gpa = std.heap.GeneralPurposeAllocator(.{}){}; + defer if (gpa.deinit() == .leak) @panic("found memory leaks"); + const allocator = gpa.allocator(); + + var it = try std.process.argsWithAllocator(allocator); + defer it.deinit(); + _ = it.next() orelse unreachable; // skip binary name + const exe_path = it.next() orelse unreachable; + const symlink_path = it.next() orelse unreachable; + + try std.fs.cwd().symLink(exe_path, symlink_path, .{}); +} diff --git a/test/standalone/self_exe_symlink/main.zig b/test/standalone/self_exe_symlink/main.zig new file mode 100644 index 0000000000..d84860a8f8 --- /dev/null +++ b/test/standalone/self_exe_symlink/main.zig @@ -0,0 +1,17 @@ +const std = @import("std"); + +pub fn main() !void { + var gpa = std.heap.GeneralPurposeAllocator(.{}){}; + defer std.debug.assert(gpa.deinit() == .ok); + const allocator = gpa.allocator(); + + const self_path = try std.fs.selfExePathAlloc(allocator); + defer allocator.free(self_path); + + var self_exe = try std.fs.openSelfExe(.{}); + defer self_exe.close(); + var buf: [std.fs.MAX_PATH_BYTES]u8 = undefined; + const self_exe_path = try std.os.getFdPath(self_exe.handle, &buf); + + try std.testing.expectEqualStrings(self_exe_path, self_path); +}