Skip to content

Commit f7698ea

Browse files
committed
v symlink: fix windows PATH setting (cmd.exe needs C: not c:)
1 parent 66b8462 commit f7698ea

File tree

3 files changed

+51
-17
lines changed

3 files changed

+51
-17
lines changed

cmd/tools/vsymlink.v

Lines changed: 18 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ $if windows {
88
}
99
}
1010
fn main() {
11-
vexe := pref.vexe_path()
11+
vexe := os.real_path(pref.vexe_path())
1212
$if windows {
1313
setup_symlink_windows(vexe)
1414
} $else {
@@ -36,10 +36,10 @@ fn setup_symlink(vexe string) {
3636
if ret.exit_code == 0 {
3737
println('Symlink "$link_path" has been created')
3838
} else {
39-
println('Failed to create symlink "$link_path". Try again with sudo.')
39+
eprintln('Failed to create symlink "$link_path". Try again with sudo.')
4040
}
4141
} else {
42-
println('Failed to create symlink "$link_path". Try again with sudo.')
42+
eprintln('Failed to create symlink "$link_path". Try again with sudo.')
4343
}
4444
}
4545

@@ -56,22 +56,24 @@ fn setup_symlink_windows(vexe string) {
5656
} else {
5757
os.rm(vsymlink)
5858
}
59-
// try to create a native symlink at .\.bin\v.exe
59+
// First, try to create a native symlink at .\.bin\v.exe
6060
os.symlink(vsymlink, vexe) or {
6161
// typically only fails if you're on a network drive (VirtualBox)
6262
// do batch file creation instead
63-
eprint('NOTE: Could not create a native symlink: $err')
63+
eprintln('Could not create a native symlink: $err')
6464
eprintln('Creating a batch file instead...')
6565
vsymlink = os.join_path(vsymlinkdir, 'v.bat')
6666
if os.exists(vsymlink) {
6767
os.rm(vsymlink)
6868
}
6969
os.write_file(vsymlink, '@echo off\n$vexe %*')
70+
eprintln('$vsymlink file written.')
7071
}
7172
if !os.exists(vsymlink) {
7273
warn_and_exit('Could not create $vsymlink')
7374
}
74-
print('Symlink $vsymlink to $vexe created.\n\nChecking system %PATH%...')
75+
println('Symlink $vsymlink to $vexe created.')
76+
println('Checking system %PATH%...')
7577
reg_sys_env_handle := get_reg_sys_env_handle() or {
7678
warn_and_exit(err)
7779
return
@@ -87,31 +89,36 @@ fn setup_symlink_windows(vexe string) {
8789
current_sys_paths := sys_env_path.split(os.path_delimiter).map(it.trim('/$os.path_separator'))
8890
mut new_paths := [vsymlinkdir]
8991
for p in current_sys_paths {
92+
if p == '' {
93+
continue
94+
}
9095
if p !in new_paths {
9196
new_paths << p
9297
}
9398
}
9499
new_sys_env_path := new_paths.join(';')
95100
if new_sys_env_path == sys_env_path {
96-
println('configured.')
101+
println('System %PATH% was already configured.')
97102
} else {
98-
print('not configured.\nAdding symlink directory to system %PATH%...')
103+
println('System %PATH% was not configured.')
104+
println('Adding symlink directory to system %PATH%...')
99105
set_reg_value(reg_sys_env_handle, 'Path', new_sys_env_path) or {
100106
warn_and_exit(err)
101107
C.RegCloseKey(reg_sys_env_handle)
102108
return
103109
}
104110
println('done.')
105111
}
106-
print('Letting running process know to update their Environment...')
112+
println('Notifying running processes to update their Environment...')
107113
send_setting_change_msg('Environment') or {
108-
eprintln('\n' + err)
114+
eprintln(err)
109115
warn_and_exit('You might need to run this again to have the `v` command in your %PATH%')
110116
C.RegCloseKey(reg_sys_env_handle)
111117
return
112118
}
113119
C.RegCloseKey(reg_sys_env_handle)
114-
println('finished.\n\nNote: restart your shell/IDE to load the new %PATH%.')
120+
println('')
121+
println('Note: restart your shell/IDE to load the new %PATH%.')
115122
println('After restarting your shell/IDE, give `v version` a try in another dir!')
116123
}
117124
}

vlib/os/os.v

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1141,9 +1141,27 @@ pub fn real_path(fpath string) string {
11411141
return fpath
11421142
}
11431143
}
1144-
return unsafe { fullpath.vstring() }
1144+
res := unsafe { fullpath.vstring() }
1145+
return normalize_drive_letter(res)
11451146
}
11461147

1148+
fn normalize_drive_letter(path string) string {
1149+
// normalize_drive_letter is needed, because a path like c:\nv\.bin (note the small `c`)
1150+
// in %PATH is NOT recognized by cmd.exe (and probably other programs too)...
1151+
// Capital drive letters do work fine.
1152+
$if !windows {
1153+
return path
1154+
}
1155+
if path.len > 2 && path[0] >= `a` && path[0] <= `z` && path[1] == `:` && path[2] == os.path_separator[0] {
1156+
unsafe {
1157+
x := &path.str[0]
1158+
(*x) = *x - 32
1159+
}
1160+
}
1161+
return path
1162+
}
1163+
1164+
11471165
// is_abs_path returns `true` if `path` is absolute.
11481166
pub fn is_abs_path(path string) bool {
11491167
$if windows {

vlib/os/os_windows.c.v

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -294,14 +294,23 @@ pub fn exec(cmd string) ?Result {
294294
}
295295
}
296296

297+
// See https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-createsymboliclinkw
297298
fn C.CreateSymbolicLinkW(&u16, &u16, u32) int
298299

299-
pub fn symlink(origin string, target string) ?bool {
300-
flags := if is_dir(origin) { 1 } else { 0 }
301-
if C.CreateSymbolicLinkW(origin.to_wide(), target.to_wide(), u32(flags)) != 0 {
302-
return true
300+
pub fn symlink(symlink_path string, target_path string) ?bool {
301+
mut flags := 0
302+
if is_dir(symlink_path) {
303+
flags |= 1
304+
}
305+
flags |= 2 // SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE
306+
res := C.CreateSymbolicLinkW(symlink_path.to_wide(), target_path.to_wide(), flags)
307+
if res == 0 {
308+
return error(get_error_msg(int(C.GetLastError())))
303309
}
304-
return error(get_error_msg(int(C.GetLastError())))
310+
if !exists(symlink_path) {
311+
return error('C.CreateSymbolicLinkW reported success, but symlink still does not exist')
312+
}
313+
return true
305314
}
306315

307316
pub fn (mut f File) close() {

0 commit comments

Comments
 (0)