From f7698ea16024b7a05ca380d4f046b094096ab1e6 Mon Sep 17 00:00:00 2001 From: Delyan Angelov Date: Thu, 22 Oct 2020 17:28:58 +0300 Subject: [PATCH] v symlink: fix windows PATH setting (cmd.exe needs C: not c:) --- cmd/tools/vsymlink.v | 29 ++++++++++++++++++----------- vlib/os/os.v | 20 +++++++++++++++++++- vlib/os/os_windows.c.v | 19 ++++++++++++++----- 3 files changed, 51 insertions(+), 17 deletions(-) diff --git a/cmd/tools/vsymlink.v b/cmd/tools/vsymlink.v index 256dd1a191..ddc73210e4 100644 --- a/cmd/tools/vsymlink.v +++ b/cmd/tools/vsymlink.v @@ -8,7 +8,7 @@ $if windows { } } fn main() { - vexe := pref.vexe_path() + vexe := os.real_path(pref.vexe_path()) $if windows { setup_symlink_windows(vexe) } $else { @@ -36,10 +36,10 @@ fn setup_symlink(vexe string) { if ret.exit_code == 0 { println('Symlink "$link_path" has been created') } else { - println('Failed to create symlink "$link_path". Try again with sudo.') + eprintln('Failed to create symlink "$link_path". Try again with sudo.') } } else { - println('Failed to create symlink "$link_path". Try again with sudo.') + eprintln('Failed to create symlink "$link_path". Try again with sudo.') } } @@ -56,22 +56,24 @@ fn setup_symlink_windows(vexe string) { } else { os.rm(vsymlink) } - // try to create a native symlink at .\.bin\v.exe + // First, try to create a native symlink at .\.bin\v.exe os.symlink(vsymlink, vexe) or { // typically only fails if you're on a network drive (VirtualBox) // do batch file creation instead - eprint('NOTE: Could not create a native symlink: $err') + eprintln('Could not create a native symlink: $err') eprintln('Creating a batch file instead...') vsymlink = os.join_path(vsymlinkdir, 'v.bat') if os.exists(vsymlink) { os.rm(vsymlink) } os.write_file(vsymlink, '@echo off\n$vexe %*') + eprintln('$vsymlink file written.') } if !os.exists(vsymlink) { warn_and_exit('Could not create $vsymlink') } - print('Symlink $vsymlink to $vexe created.\n\nChecking system %PATH%...') + println('Symlink $vsymlink to $vexe created.') + println('Checking system %PATH%...') reg_sys_env_handle := get_reg_sys_env_handle() or { warn_and_exit(err) return @@ -87,15 +89,19 @@ fn setup_symlink_windows(vexe string) { current_sys_paths := sys_env_path.split(os.path_delimiter).map(it.trim('/$os.path_separator')) mut new_paths := [vsymlinkdir] for p in current_sys_paths { + if p == '' { + continue + } if p !in new_paths { new_paths << p } } new_sys_env_path := new_paths.join(';') if new_sys_env_path == sys_env_path { - println('configured.') + println('System %PATH% was already configured.') } else { - print('not configured.\nAdding symlink directory to system %PATH%...') + println('System %PATH% was not configured.') + println('Adding symlink directory to system %PATH%...') set_reg_value(reg_sys_env_handle, 'Path', new_sys_env_path) or { warn_and_exit(err) C.RegCloseKey(reg_sys_env_handle) @@ -103,15 +109,16 @@ fn setup_symlink_windows(vexe string) { } println('done.') } - print('Letting running process know to update their Environment...') + println('Notifying running processes to update their Environment...') send_setting_change_msg('Environment') or { - eprintln('\n' + err) + eprintln(err) warn_and_exit('You might need to run this again to have the `v` command in your %PATH%') C.RegCloseKey(reg_sys_env_handle) return } C.RegCloseKey(reg_sys_env_handle) - println('finished.\n\nNote: restart your shell/IDE to load the new %PATH%.') + println('') + println('Note: restart your shell/IDE to load the new %PATH%.') println('After restarting your shell/IDE, give `v version` a try in another dir!') } } diff --git a/vlib/os/os.v b/vlib/os/os.v index 14b6c0ccdf..10b4810452 100644 --- a/vlib/os/os.v +++ b/vlib/os/os.v @@ -1141,9 +1141,27 @@ pub fn real_path(fpath string) string { return fpath } } - return unsafe { fullpath.vstring() } + res := unsafe { fullpath.vstring() } + return normalize_drive_letter(res) } +fn normalize_drive_letter(path string) string { + // normalize_drive_letter is needed, because a path like c:\nv\.bin (note the small `c`) + // in %PATH is NOT recognized by cmd.exe (and probably other programs too)... + // Capital drive letters do work fine. + $if !windows { + return path + } + if path.len > 2 && path[0] >= `a` && path[0] <= `z` && path[1] == `:` && path[2] == os.path_separator[0] { + unsafe { + x := &path.str[0] + (*x) = *x - 32 + } + } + return path +} + + // is_abs_path returns `true` if `path` is absolute. pub fn is_abs_path(path string) bool { $if windows { diff --git a/vlib/os/os_windows.c.v b/vlib/os/os_windows.c.v index 2ec69878a6..89c408b911 100644 --- a/vlib/os/os_windows.c.v +++ b/vlib/os/os_windows.c.v @@ -294,14 +294,23 @@ pub fn exec(cmd string) ?Result { } } +// See https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-createsymboliclinkw fn C.CreateSymbolicLinkW(&u16, &u16, u32) int -pub fn symlink(origin string, target string) ?bool { - flags := if is_dir(origin) { 1 } else { 0 } - if C.CreateSymbolicLinkW(origin.to_wide(), target.to_wide(), u32(flags)) != 0 { - return true +pub fn symlink(symlink_path string, target_path string) ?bool { + mut flags := 0 + if is_dir(symlink_path) { + flags |= 1 } - return error(get_error_msg(int(C.GetLastError()))) + flags |= 2 // SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE + res := C.CreateSymbolicLinkW(symlink_path.to_wide(), target_path.to_wide(), flags) + if res == 0 { + return error(get_error_msg(int(C.GetLastError()))) + } + if !exists(symlink_path) { + return error('C.CreateSymbolicLinkW reported success, but symlink still does not exist') + } + return true } pub fn (mut f File) close() {