aboutsummaryrefslogtreecommitdiff
path: root/v_windows/v/old/cmd/tools/vsymlink.v
diff options
context:
space:
mode:
Diffstat (limited to 'v_windows/v/old/cmd/tools/vsymlink.v')
-rw-r--r--v_windows/v/old/cmd/tools/vsymlink.v182
1 files changed, 182 insertions, 0 deletions
diff --git a/v_windows/v/old/cmd/tools/vsymlink.v b/v_windows/v/old/cmd/tools/vsymlink.v
new file mode 100644
index 0000000..c5d7d8a
--- /dev/null
+++ b/v_windows/v/old/cmd/tools/vsymlink.v
@@ -0,0 +1,182 @@
+import os
+import v.pref
+import v.util
+
+$if windows {
+ $if tinyc {
+ #flag -ladvapi32
+ #flag -luser32
+ }
+}
+fn main() {
+ C.atexit(cleanup_vtmp_folder)
+ vexe := os.real_path(pref.vexe_path())
+ $if windows {
+ setup_symlink_windows(vexe)
+ } $else {
+ setup_symlink_unix(vexe)
+ }
+}
+
+fn cleanup_vtmp_folder() {
+ os.rmdir_all(util.get_vtmp_folder()) or {}
+}
+
+fn setup_symlink_unix(vexe string) {
+ mut link_path := '/data/data/com.termux/files/usr/bin/v'
+ if !os.is_dir('/data/data/com.termux/files') {
+ link_dir := '/usr/local/bin'
+ if !os.exists(link_dir) {
+ os.mkdir_all(link_dir) or { panic(err) }
+ }
+ link_path = link_dir + '/v'
+ }
+ os.rm(link_path) or {}
+ os.symlink(vexe, link_path) or {
+ eprintln('Failed to create symlink "$link_path". Try again with sudo.')
+ exit(1)
+ }
+}
+
+fn setup_symlink_windows(vexe string) {
+ $if windows {
+ // Create a symlink in a new local folder (.\.bin\.v.exe)
+ // Puts `v` in %PATH% without polluting it with anything else (like make.bat).
+ // This will make `v` available on cmd.exe, PowerShell, and MinGW(MSYS)/WSL/Cygwin
+ vdir := os.real_path(os.dir(vexe))
+ vsymlinkdir := os.join_path(vdir, '.bin')
+ mut vsymlink := os.join_path(vsymlinkdir, 'v.exe')
+ // Remove old symlink first (v could have been moved, symlink rerun)
+ if !os.exists(vsymlinkdir) {
+ os.mkdir(vsymlinkdir) or { panic(err) }
+ } else {
+ if os.exists(vsymlink) {
+ os.rm(vsymlink) or { panic(err) }
+ } else {
+ vsymlink = os.join_path(vsymlinkdir, 'v.bat')
+ if os.exists(vsymlink) {
+ os.rm(vsymlink) or { panic(err) }
+ }
+ vsymlink = os.join_path(vsymlinkdir, '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
+ 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) or { panic(err) }
+ }
+ os.write_file(vsymlink, '@echo off\n$vexe %*') or { panic(err) }
+ eprintln('$vsymlink file written.')
+ }
+ if !os.exists(vsymlink) {
+ warn_and_exit('Could not create $vsymlink')
+ }
+ println('Symlink $vsymlink to $vexe created.')
+ println('Checking system %PATH%...')
+ reg_sys_env_handle := get_reg_sys_env_handle() or {
+ warn_and_exit(err.msg)
+ return
+ }
+ // TODO: Fix defers inside ifs
+ // defer {
+ // C.RegCloseKey(reg_sys_env_handle)
+ // }
+ // if the above succeeded, and we cannot get the value, it may simply be empty
+ sys_env_path := get_reg_value(reg_sys_env_handle, 'Path') or { '' }
+ 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('System %PATH% was already configured.')
+ } else {
+ 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 {
+ C.RegCloseKey(reg_sys_env_handle)
+ warn_and_exit(err.msg)
+ }
+ println('Done.')
+ }
+ println('Notifying running processes to update their Environment...')
+ send_setting_change_msg('Environment') or {
+ eprintln(err)
+ C.RegCloseKey(reg_sys_env_handle)
+ warn_and_exit('You might need to run this again to have the `v` command in your %PATH%')
+ }
+ C.RegCloseKey(reg_sys_env_handle)
+ println('Done.')
+ println('Note: Restart your shell/IDE to load the new %PATH%.')
+ println('After restarting your shell/IDE, give `v version` a try in another directory!')
+ }
+}
+
+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 { // wrap for cross-compile compat
+ // open the registry key
+ reg_key_path := 'Environment'
+ reg_env_key := voidptr(0) // or HKEY (HANDLE)
+ if C.RegOpenKeyEx(os.hkey_current_user, reg_key_path.to_wide(), 0, 1 | 2, &reg_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 := u32(4095) // this is the max length (not for the registry, but for the system %PATH%)
+ mut reg_value := unsafe { &u16(malloc(int(reg_value_size))) }
+ if C.RegQueryValueExW(reg_env_key, key.to_wide(), 0, 0, reg_value, &reg_value_size) != 0 {
+ return error('Unable to get registry value for "$key".')
+ }
+ return unsafe { 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.RegSetValueExW(reg_key, key.to_wide(), 0, C.REG_EXPAND_SZ, value.to_wide(),
+ value.len * 2) != 0 {
+ return error('Unable to set registry value for "$key". %PATH% may be too long.')
+ }
+ 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.SendMessageTimeoutW(os.hwnd_broadcast, os.wm_settingchange, 0, unsafe { &u32(message_data.to_wide()) },
+ os.smto_abortifhung, 5000, 0) == 0 {
+ return error('Could not broadcast WM_SETTINGCHANGE')
+ }
+ return true
+ }
+ return error('not on windows')
+}