187 lines
5.2 KiB
V
187 lines
5.2 KiB
V
import os
|
|
import v.pref
|
|
|
|
const (
|
|
hkey_current_user = voidptr(0x80000001)
|
|
hwnd_broadcast = voidptr(0xffff)
|
|
)
|
|
|
|
$if windows {
|
|
$if tinyc {
|
|
#flag -lAdvapi32
|
|
#flag -lUser32
|
|
}
|
|
}
|
|
|
|
fn main(){
|
|
$if windows {
|
|
setup_symlink_on_windows()
|
|
} $else {
|
|
setup_symlink_on_unix()
|
|
}
|
|
}
|
|
|
|
fn setup_symlink_on_unix(){
|
|
vexe := pref.vexe_path()
|
|
mut link_path := '/usr/local/bin/v'
|
|
mut ret := os.exec('ln -sf $vexe $link_path') or {
|
|
panic(err)
|
|
}
|
|
if ret.exit_code == 0 {
|
|
println('Symlink "$link_path" has been created')
|
|
} else if os.system("uname -o | grep -q \'[A/a]ndroid\'") == 0 {
|
|
println('Failed to create symlink "$link_path". Trying again with Termux path for Android.')
|
|
link_path = '/data/data/com.termux/files/usr/bin/v'
|
|
ret = os.exec('ln -sf $vexe $link_path') or {
|
|
panic(err)
|
|
}
|
|
if ret.exit_code == 0 {
|
|
println('Symlink "$link_path" has been created')
|
|
} else {
|
|
println('Failed to create symlink "$link_path". Try again with sudo.')
|
|
}
|
|
} else {
|
|
println('Failed to create symlink "$link_path". Try again with sudo.')
|
|
}
|
|
}
|
|
|
|
fn setup_symlink_on_windows(){
|
|
$if windows {
|
|
vexe := pref.vexe_path()
|
|
// NB: Putting $vdir directly into PATH will also result in
|
|
// make.bat being global, which is NOT what we want.
|
|
//
|
|
// Instead, we create a small launcher v.bat, in a new local
|
|
// folder .bin/ . That .bin/ folder can then be put in PATH
|
|
// without poluting it with anything else - just a `v`
|
|
// command will be available, similar to unix.
|
|
//
|
|
// Creating a real NTFS symlink to the real executable was also
|
|
// tried, but then os.real_path( os.executable() ) returns the
|
|
// path to the symlink, unfortunately, unlike on posix systems
|
|
// ¯\_(ツ)_/¯
|
|
vdir := os.real_path(os.dir(vexe))
|
|
vsymlinkdir := os.join_path(vdir, '.bin')
|
|
vsymlinkbat := os.join_path(vsymlinkdir, 'v.bat')
|
|
if os.exists(vsymlinkbat) {
|
|
print('Batch script $vsymlinkbat already exists, checking system %PATH%...')
|
|
}
|
|
else {
|
|
os.rmdir_all(vsymlinkdir)
|
|
os.mkdir_all(vsymlinkdir)
|
|
os.write_file(vsymlinkbat, '@echo off\n${vexe} %*')
|
|
if !os.exists(vsymlinkbat) {
|
|
eprintln('Could not create $vsymlinkbat')
|
|
exit(1)
|
|
}
|
|
else {
|
|
print('Created $vsymlinkbat, checking system %PATH%...')
|
|
}
|
|
}
|
|
|
|
reg_sys_env_handle := get_reg_sys_env_handle() or {
|
|
warn_and_exit(err)
|
|
return
|
|
}
|
|
defer {
|
|
C.RegCloseKey(reg_sys_env_handle)
|
|
}
|
|
|
|
sys_env_path := get_reg_value(reg_sys_env_handle, 'Path') or {
|
|
warn_and_exit(err)
|
|
return
|
|
}
|
|
|
|
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 !in new_paths {
|
|
new_paths << p
|
|
}
|
|
}
|
|
|
|
new_sys_env_path := new_paths.join(';')
|
|
|
|
if new_sys_env_path == sys_env_path {
|
|
println('configured.')
|
|
}
|
|
else {
|
|
print('not configured.\nSetting system %PATH%...')
|
|
set_reg_value(reg_sys_env_handle, 'Path', new_sys_env_path) or {
|
|
warn_and_exit(err)
|
|
return
|
|
}
|
|
println('done.')
|
|
}
|
|
|
|
print('Letting running process know to update their Environment...')
|
|
send_setting_change_msg('Environment') or {
|
|
eprintln('\n' + err)
|
|
warn_and_exit('You might need to run this again to have `v` in your %PATH%')
|
|
return
|
|
}
|
|
|
|
println('finished.\n\nNote: restart your shell/IDE to load the new %PATH%.')
|
|
println('\nAfter restarting your shell/IDE, give `v version` a try in another dir!')
|
|
}
|
|
}
|
|
|
|
fn warn_and_exit(err string) {
|
|
eprintln(err)
|
|
exit(1)
|
|
}
|
|
|
|
// get the system environment registry handle
|
|
fn get_reg_sys_env_handle() ?voidptr {
|
|
$if windows {
|
|
// open the registry key
|
|
reg_key_path := 'Environment'
|
|
reg_env_key := voidptr(0) // or HKEY (HANDLE)
|
|
if C.RegOpenKeyEx(hkey_current_user, reg_key_path.to_wide(), 0, 1 | 2, ®_env_key) != 0 {
|
|
return error('Could not open "$reg_key_path" in the registry')
|
|
}
|
|
|
|
return reg_env_key
|
|
}
|
|
return error('not on windows')
|
|
}
|
|
|
|
// get a value from a given $key
|
|
fn get_reg_value(reg_env_key voidptr, key string) ?string {
|
|
$if windows {
|
|
// query the value (shortcut the sizing step)
|
|
reg_value_size := 4095 // this is the max length (not for the registry, but for the system %PATH%)
|
|
mut reg_value := &u16(malloc(reg_value_size))
|
|
if C.RegQueryValueEx(reg_env_key, key.to_wide(), 0, 0, reg_value, ®_value_size) != 0 {
|
|
return error('Unable to get registry value for "$key", try rerunning as an Administrator')
|
|
}
|
|
|
|
return string_from_wide(reg_value)
|
|
}
|
|
return error('not on windows')
|
|
}
|
|
|
|
// sets the value for the given $key to the given $value
|
|
fn set_reg_value(reg_key voidptr, key string, value string) ?bool {
|
|
$if windows {
|
|
if C.RegSetValueEx(reg_key, key.to_wide(), 0, 1, value.to_wide(), 4095) != 0 {
|
|
return error('Unable to set registry value for "$key", are you running as an Administrator?')
|
|
}
|
|
|
|
return true
|
|
}
|
|
return error('not on windows')
|
|
}
|
|
|
|
// broadcasts a message to all listening windows (explorer.exe in particular)
|
|
// letting them know that the system environment has changed and should be reloaded
|
|
fn send_setting_change_msg(message_data string) ?bool {
|
|
$if windows {
|
|
if C.SendMessageTimeout(hwnd_broadcast, 0x001A, 0, message_data.to_wide(), 2, 5000, 0) == 0 {
|
|
return error('Could not broadcast WM_SETTINGCHANGE')
|
|
}
|
|
return true
|
|
}
|
|
return error('not on windows')
|
|
}
|