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 , .{
@@ -305,7 +306,7 @@ fn verifyInstall(
305306) ! bool {
306307 var ver_buf : [std .fs .max_path_bytes ]u8 = undefined ;
307308 const ver_path = zvm .versionPath (& ver_buf , version );
308- const zig_path = try std .fmt .allocPrint (allocator , "{s}/zig " , .{ver_path });
309+ const zig_path = try std .fmt .allocPrint (allocator , "{s}/{s} " , .{ ver_path , platform . executableName ( "zig" ) });
309310 defer allocator .free (zig_path );
310311
311312 // Create a temporary test file in the cache directory
@@ -357,7 +358,7 @@ fn installZls(
357358 // Get the installed Zig version string
358359 var ver_buf : [std .fs .max_path_bytes ]u8 = undefined ;
359360 const ver_path = zvm .versionPath (& ver_buf , version );
360- const zig_path = try std .fmt .allocPrint (allocator , "{s}/zig " , .{ver_path });
361+ const zig_path = try std .fmt .allocPrint (allocator , "{s}/{s} " , .{ ver_path , platform . executableName ( "zig" ) });
361362 defer allocator .free (zig_path );
362363
363364 const result = std .process .run (allocator , zvm .io , .{
@@ -431,6 +432,7 @@ fn installZls(
431432 return ;
432433 },
433434 };
435+ const zls_exe_name = platform .executableName ("zls" );
434436
435437 // Download ZLS archive
436438 const zls_archive_name = if (std .mem .lastIndexOfScalar (u8 , zls_tarball , '/' )) | idx | zls_tarball [idx + 1 .. ] else "zls-archive" ;
@@ -443,6 +445,7 @@ fn installZls(
443445 // Extract to a temporary directory
444446 var temp_buf : [std .fs .max_path_bytes * 2 ]u8 = undefined ;
445447 const temp_dir = try std .fmt .bufPrint (& temp_buf , "{s}/zls-temp" , .{zvm .cache_dir });
448+ std .Io .Dir .cwd ().deleteTree (zvm .io , temp_dir ) catch {};
446449 std .Io .Dir .cwd ().createDirPath (zvm .io , temp_dir ) catch {};
447450
448451 archive .extractArchive (allocator , zvm .io , zls_archive_path , temp_dir ) catch {
@@ -459,18 +462,21 @@ fn installZls(
459462 var iter = temp_handle .iterate ();
460463 while (try iter .next (zvm .io )) | entry | {
461464 // Flat layout: zls binary at root of archive
462- if (entry .kind == .file and ( std .mem .eql (u8 , entry .name , "zls" ) or std . mem . eql ( u8 , entry . name , "zls.exe" ) )) {
465+ if (entry .kind == .file and std .mem .eql (u8 , entry .name , zls_exe_name )) {
463466 var src_buf : [std .fs .max_path_bytes * 2 ]u8 = undefined ;
464467 const src = try std .fmt .bufPrint (& src_buf , "{s}/{s}" , .{ temp_dir , entry .name });
465468
466469 var dst_buf : [std .fs .max_path_bytes * 2 ]u8 = undefined ;
467- const dst = try std .fmt .bufPrint (& dst_buf , "{s}/{s}/zls " , .{ zvm .data_dir , version });
470+ const dst = try std .fmt .bufPrint (& dst_buf , "{s}/{s}/{s} " , .{ zvm .data_dir , version , zls_exe_name });
468471
469472 try platform .copyFile (zvm .io , src , dst );
473+ deleteLegacyZlsWithoutExtension (zvm , version , zls_exe_name );
470474
471- _ = std .process .run (allocator , zvm .io , .{
472- .argv = &.{ "chmod" , "+x" , dst },
473- }) catch {};
475+ if (! platform .isWindows ()) {
476+ _ = std .process .run (allocator , zvm .io , .{
477+ .argv = &.{ "chmod" , "+x" , dst },
478+ }) catch {};
479+ }
474480 console .success ("Installed ZLS" , .{});
475481 found = true ;
476482 break ;
@@ -486,18 +492,21 @@ fn installZls(
486492
487493 var inner_iter = inner_dir .iterate ();
488494 while (try inner_iter .next (zvm .io )) | inner_entry | {
489- if (std . mem . eql ( u8 , inner_entry .name , "zls" ) or std .mem .eql (u8 , inner_entry .name , "zls.exe" )) {
495+ if (inner_entry .kind == .file and std .mem .eql (u8 , inner_entry .name , zls_exe_name )) {
490496 var src_buf : [std .fs .max_path_bytes * 2 ]u8 = undefined ;
491497 const src = try std .fmt .bufPrint (& src_buf , "{s}/{s}/{s}" , .{ temp_dir , entry .name , inner_entry .name });
492498
493499 var dst_buf : [std .fs .max_path_bytes * 2 ]u8 = undefined ;
494- const dst = try std .fmt .bufPrint (& dst_buf , "{s}/{s}/zls " , .{ zvm .data_dir , version });
500+ const dst = try std .fmt .bufPrint (& dst_buf , "{s}/{s}/{s} " , .{ zvm .data_dir , version , zls_exe_name });
495501
496502 try platform .copyFile (zvm .io , src , dst );
503+ deleteLegacyZlsWithoutExtension (zvm , version , zls_exe_name );
497504
498- _ = std .process .run (allocator , zvm .io , .{
499- .argv = &.{ "chmod" , "+x" , dst },
500- }) catch {};
505+ if (! platform .isWindows ()) {
506+ _ = std .process .run (allocator , zvm .io , .{
507+ .argv = &.{ "chmod" , "+x" , dst },
508+ }) catch {};
509+ }
501510 console .success ("Installed ZLS" , .{});
502511 found = true ;
503512 break ;
@@ -515,3 +524,13 @@ fn installZls(
515524 std .Io .Dir .cwd ().deleteFile (zvm .io , zls_archive_path ) catch {};
516525 std .Io .Dir .cwd ().deleteTree (zvm .io , temp_dir ) catch {};
517526}
527+
528+ /// Remove the extensionless ZLS file left by older Windows installs.
529+ /// No-op on Linux/macOS where the expected executable name is already "zls".
530+ fn deleteLegacyZlsWithoutExtension (zvm : * zvm_mod.ZVM , version : []const u8 , zls_exe_name : []const u8 ) void {
531+ if (std .mem .eql (u8 , zls_exe_name , "zls" )) return ;
532+
533+ var legacy_dst_buf : [std .fs .max_path_bytes * 2 ]u8 = undefined ;
534+ const legacy_dst = std .fmt .bufPrint (& legacy_dst_buf , "{s}/{s}/zls" , .{ zvm .data_dir , version }) catch return ;
535+ std .Io .Dir .cwd ().deleteFile (zvm .io , legacy_dst ) catch {};
536+ }
0 commit comments