Skip to content

Commit 93e60c9

Browse files
committed
Use platform.executableName for binaries
- Add executableName and isWindows helpers to platform.zig - Replace hardcoded names with platform.executableName in install, run, and upgrade - Add cleanup for legacy ZLS file left by older Windows installs - Skip chmod on Windows when installing ZLS - Use platform-aware zig/zls/zvm paths across commands - Clean temp directory prior to archive extraction
1 parent 7eae454 commit 93e60c9

4 files changed

Lines changed: 60 additions & 27 deletions

File tree

src/command/install.zig

Lines changed: 36 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,15 @@
44
//! Optionally installs ZLS (Zig Language Server) alongside Zig.
55

66
const std = @import("std");
7-
const zvm_mod = @import("../core/zvm.zig");
7+
88
const cli = @import("../cli.zig");
9-
const platform = @import("../core/platform.zig");
109
const Console = @import("../core/Console.zig");
11-
const version_map = @import("../network/version_map.zig");
10+
const crypto = @import("../core/crypto.zig");
11+
const platform = @import("../core/platform.zig");
12+
const zvm_mod = @import("../core/zvm.zig");
1213
const http_client = @import("../network/http_client.zig");
14+
const version_map = @import("../network/version_map.zig");
1315
const archive = @import("archive.zig");
14-
const crypto = @import("../core/crypto.zig");
1516

1617
/// Main entry point for the `zvm install` command.
1718
/// Resolves the requested version, checks if already installed,
@@ -45,7 +46,7 @@ pub fn run(
4546
if (version_map.getMasterVersion(vmap)) |remote_ver| {
4647
var ver_buf: [std.fs.max_path_bytes]u8 = undefined;
4748
const ver_path = zvm.versionPath(&ver_buf, version);
48-
const zig_path = try std.fmt.allocPrint(allocator, "{s}/zig", .{ver_path});
49+
const zig_path = try std.fmt.allocPrint(allocator, "{s}/{s}", .{ ver_path, platform.executableName("zig") });
4950
defer allocator.free(zig_path);
5051

5152
const result = std.process.run(allocator, zvm.io, .{
@@ -291,7 +292,7 @@ fn verifyInstall(
291292
) !bool {
292293
var ver_buf: [std.fs.max_path_bytes]u8 = undefined;
293294
const ver_path = zvm.versionPath(&ver_buf, version);
294-
const zig_path = try std.fmt.allocPrint(allocator, "{s}/zig", .{ver_path});
295+
const zig_path = try std.fmt.allocPrint(allocator, "{s}/{s}", .{ ver_path, platform.executableName("zig") });
295296
defer allocator.free(zig_path);
296297

297298
// Create a temporary test file in the cache directory
@@ -343,7 +344,7 @@ fn installZls(
343344
// Get the installed Zig version string
344345
var ver_buf: [std.fs.max_path_bytes]u8 = undefined;
345346
const ver_path = zvm.versionPath(&ver_buf, version);
346-
const zig_path = try std.fmt.allocPrint(allocator, "{s}/zig", .{ver_path});
347+
const zig_path = try std.fmt.allocPrint(allocator, "{s}/{s}", .{ ver_path, platform.executableName("zig") });
347348
defer allocator.free(zig_path);
348349

349350
const result = std.process.run(allocator, zvm.io, .{
@@ -417,6 +418,7 @@ fn installZls(
417418
return;
418419
},
419420
};
421+
const zls_exe_name = platform.executableName("zls");
420422

421423
// Download ZLS archive
422424
const zls_archive_name = if (std.mem.lastIndexOfScalar(u8, zls_tarball, '/')) |idx| zls_tarball[idx + 1 ..] else "zls-archive";
@@ -429,6 +431,7 @@ fn installZls(
429431
// Extract to a temporary directory
430432
var temp_buf: [std.fs.max_path_bytes * 2]u8 = undefined;
431433
const temp_dir = try std.fmt.bufPrint(&temp_buf, "{s}/zls-temp", .{zvm.cache_dir});
434+
std.Io.Dir.cwd().deleteTree(zvm.io, temp_dir) catch {};
432435
std.Io.Dir.cwd().createDirPath(zvm.io, temp_dir) catch {};
433436

434437
archive.extractArchive(allocator, zvm.io, zls_archive_path, temp_dir) catch {
@@ -445,18 +448,21 @@ fn installZls(
445448
var iter = temp_handle.iterate();
446449
while (try iter.next(zvm.io)) |entry| {
447450
// Flat layout: zls binary at root of archive
448-
if (entry.kind == .file and (std.mem.eql(u8, entry.name, "zls") or std.mem.eql(u8, entry.name, "zls.exe"))) {
451+
if (entry.kind == .file and std.mem.eql(u8, entry.name, zls_exe_name)) {
449452
var src_buf: [std.fs.max_path_bytes * 2]u8 = undefined;
450453
const src = try std.fmt.bufPrint(&src_buf, "{s}/{s}", .{ temp_dir, entry.name });
451454

452455
var dst_buf: [std.fs.max_path_bytes * 2]u8 = undefined;
453-
const dst = try std.fmt.bufPrint(&dst_buf, "{s}/{s}/zls", .{ zvm.data_dir, version });
456+
const dst = try std.fmt.bufPrint(&dst_buf, "{s}/{s}/{s}", .{ zvm.data_dir, version, zls_exe_name });
454457

455458
try platform.copyFile(zvm.io, src, dst);
459+
deleteLegacyZlsWithoutExtension(zvm, version, zls_exe_name);
456460

457-
_ = std.process.run(allocator, zvm.io, .{
458-
.argv = &.{ "chmod", "+x", dst },
459-
}) catch {};
461+
if (!platform.isWindows()) {
462+
_ = std.process.run(allocator, zvm.io, .{
463+
.argv = &.{ "chmod", "+x", dst },
464+
}) catch {};
465+
}
460466
console.success("Installed ZLS", .{});
461467
found = true;
462468
break;
@@ -472,18 +478,21 @@ fn installZls(
472478

473479
var inner_iter = inner_dir.iterate();
474480
while (try inner_iter.next(zvm.io)) |inner_entry| {
475-
if (std.mem.eql(u8, inner_entry.name, "zls") or std.mem.eql(u8, inner_entry.name, "zls.exe")) {
481+
if (inner_entry.kind == .file and std.mem.eql(u8, inner_entry.name, zls_exe_name)) {
476482
var src_buf: [std.fs.max_path_bytes * 2]u8 = undefined;
477483
const src = try std.fmt.bufPrint(&src_buf, "{s}/{s}/{s}", .{ temp_dir, entry.name, inner_entry.name });
478484

479485
var dst_buf: [std.fs.max_path_bytes * 2]u8 = undefined;
480-
const dst = try std.fmt.bufPrint(&dst_buf, "{s}/{s}/zls", .{ zvm.data_dir, version });
486+
const dst = try std.fmt.bufPrint(&dst_buf, "{s}/{s}/{s}", .{ zvm.data_dir, version, zls_exe_name });
481487

482488
try platform.copyFile(zvm.io, src, dst);
489+
deleteLegacyZlsWithoutExtension(zvm, version, zls_exe_name);
483490

484-
_ = std.process.run(allocator, zvm.io, .{
485-
.argv = &.{ "chmod", "+x", dst },
486-
}) catch {};
491+
if (!platform.isWindows()) {
492+
_ = std.process.run(allocator, zvm.io, .{
493+
.argv = &.{ "chmod", "+x", dst },
494+
}) catch {};
495+
}
487496
console.success("Installed ZLS", .{});
488497
found = true;
489498
break;
@@ -501,3 +510,13 @@ fn installZls(
501510
std.Io.Dir.cwd().deleteFile(zvm.io, zls_archive_path) catch {};
502511
std.Io.Dir.cwd().deleteTree(zvm.io, temp_dir) catch {};
503512
}
513+
514+
/// Remove the extensionless ZLS file left by older Windows installs.
515+
/// No-op on Linux/macOS where the expected executable name is already "zls".
516+
fn deleteLegacyZlsWithoutExtension(zvm: *zvm_mod.ZVM, version: []const u8, zls_exe_name: []const u8) void {
517+
if (std.mem.eql(u8, zls_exe_name, "zls")) return;
518+
519+
var legacy_dst_buf: [std.fs.max_path_bytes * 2]u8 = undefined;
520+
const legacy_dst = std.fmt.bufPrint(&legacy_dst_buf, "{s}/{s}/zls", .{ zvm.data_dir, version }) catch return;
521+
std.Io.Dir.cwd().deleteFile(zvm.io, legacy_dst) catch {};
522+
}

src/command/run.zig

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,10 @@
22
//! Spawns the zig binary from the requested version directory as a child process.
33

44
const std = @import("std");
5-
const zvm_mod = @import("../core/zvm.zig");
5+
66
const Console = @import("../core/Console.zig");
7+
const platform = @import("../core/platform.zig");
8+
const zvm_mod = @import("../core/zvm.zig");
79

810
/// Run a Zig command using a specific installed version.
911
/// All arguments after the version are passed through to the zig binary.
@@ -20,10 +22,10 @@ pub fn run(
2022
std.process.exit(1);
2123
}
2224

23-
// Build the zig binary path: data_dir/<version>/zig
25+
// Build the zig binary path: data_dir/<version>/zig[.exe]
2426
var path_buf: [std.fs.max_path_bytes]u8 = undefined;
2527
const version_dir = zvm.versionPath(&path_buf, version);
26-
const zig_path = try std.fmt.allocPrint(allocator, "{s}/zig", .{version_dir});
28+
const zig_path = try std.fmt.allocPrint(allocator, "{s}/{s}", .{ version_dir, platform.executableName("zig") });
2729
defer allocator.free(zig_path);
2830

2931
// Build argv: [zig_path, arg1, arg2, ...]

src/command/upgrade.zig

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,12 @@
55
const std = @import("std");
66
const builtin = @import("builtin");
77
const build_options = @import("build_options");
8-
const zvm_mod = @import("../core/zvm.zig");
8+
99
const Console = @import("../core/Console.zig");
1010
const platform = @import("../core/platform.zig");
11-
const http_client = @import("../network/http_client.zig");
1211
const update_check = @import("../core/update_check.zig");
12+
const zvm_mod = @import("../core/zvm.zig");
13+
const http_client = @import("../network/http_client.zig");
1314

1415
/// Search for the extracted binary inside self_dir, including subdirectories.
1516
/// The archive may extract into a versioned directory like zvm-v0.1.1-aarch64-macos/.
@@ -160,10 +161,7 @@ pub fn run(
160161
// Find the extracted binary and replace the current installation.
161162
// The archive may extract into a subdirectory (e.g. zvm-v0.1.1-aarch64-macos/zvm),
162163
// so search recursively.
163-
const exe_name = comptime switch (builtin.os.tag) {
164-
.windows => "zvm.exe",
165-
else => "zvm",
166-
};
164+
const exe_name = platform.executableName("zvm");
167165

168166
// Resolve the current zvm install directory
169167
const install_dir = blk: {
@@ -231,7 +229,7 @@ pub fn run(
231229
defer allocator.free(active);
232230
var ver_buf: [std.fs.max_path_bytes]u8 = undefined;
233231
const ver_path = zvm.versionPath(&ver_buf, active);
234-
const zig_path = std.fmt.allocPrint(allocator, "{s}/zig", .{ver_path}) catch return;
232+
const zig_path = std.fmt.allocPrint(allocator, "{s}/{s}", .{ ver_path, platform.executableName("zig") }) catch return;
235233
defer allocator.free(zig_path);
236234

237235
const ver_result = std.process.run(allocator, zvm.io, .{

src/core/platform.zig

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,20 @@ pub fn getArchiveExtension() []const u8 {
6161
};
6262
}
6363

64+
/// Returns the platform-specific executable filename for a tool.
65+
/// Windows executables must keep their .exe suffix so shells and editors can find them.
66+
pub fn executableName(comptime base_name: []const u8) []const u8 {
67+
return switch (builtin.os.tag) {
68+
.windows => base_name ++ ".exe",
69+
else => base_name,
70+
};
71+
}
72+
73+
/// Returns true when the current target uses Windows executable semantics.
74+
pub fn isWindows() bool {
75+
return builtin.os.tag == .windows;
76+
}
77+
6478
/// Create a symbolic link at `link_path` pointing to `target`.
6579
/// Removes any existing file/link at `link_path` before creation.
6680
/// On Windows, creates a directory junction (no admin privileges required).

0 commit comments

Comments
 (0)