44//! Optionally installs ZLS (Zig Language Server) alongside Zig.
55
66const std = @import ("std" );
7- const zvm_mod = @import ( "../core/zvm.zig" );
7+
88const cli = @import ("../cli.zig" );
9- const platform = @import ("../core/platform.zig" );
109const 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" );
1213const http_client = @import ("../network/http_client.zig" );
14+ const version_map = @import ("../network/version_map.zig" );
1315const 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+ }
0 commit comments