diff --git a/build.maple b/build.maple index 3e4e19b..f7c80c0 100644 --- a/build.maple +++ b/build.maple @@ -35,7 +35,7 @@ task:test = { task:symlink = { help = 'Makes a Clockwork symlink in /usr/bin' category = 'installation' - run = 'v run ./scripts/symlink.vsh' + run = '${v} run ./cmd/symlink/' } task:install = { @@ -47,14 +47,14 @@ task:install = { task:bump = { help = 'Bumps the Clockwork version' category = 'misc' - run = 'v bump --${args} v.mod api/api.v' + run = '${v} bump --${args} v.mod api/api.v' } task:doc = { help = 'Generates HTML documentation for the Clockwork API' category = 'docs' run = [ - 'v doc -f html -o doc/ -readme ./api', + '${v} doc -f html -o doc/ -readme ./api', 'mv -v doc/api.html doc/index.html' ] } diff --git a/cmd/symlink/symlink.v b/cmd/symlink/symlink.v new file mode 100644 index 0000000..49f93f5 --- /dev/null +++ b/cmd/symlink/symlink.v @@ -0,0 +1,3 @@ +fn main() { + setup_symlink() +} diff --git a/cmd/symlink/symlink_nix.c.v b/cmd/symlink/symlink_nix.c.v new file mode 100644 index 0000000..8606253 --- /dev/null +++ b/cmd/symlink/symlink_nix.c.v @@ -0,0 +1,23 @@ +// https://github.com/vlang/v/blob/master/cmd/tools/vsymlink/vsymlink_nix.c.v +import os + +const clockwork_executable = os.join_path_single(os.getwd(), 'build/clockwork') + +fn setup_symlink() { + mut link_path := '/data/data/com.termux/files/usr/bin/clockwork' + if !os.is_dir('/data/data/com.termux/files') { + link_dir := os.local_bin_dir() + if !os.exists(link_dir) { + os.mkdir_all(link_dir) or { panic(err) } + } + link_path = link_dir + '/clockwork' + } + + os.rm(link_path) or {} + + os.symlink(clockwork_executable, link_path) or { + eprintln('Failed to create symlink "${link_path}". Check if you have permissions.') + eprintln('Error: ${err}') + exit(1) + } +} diff --git a/cmd/symlink/symlink_windows.c.v b/cmd/symlink/symlink_windows.c.v new file mode 100644 index 0000000..e1a25a0 --- /dev/null +++ b/cmd/symlink/symlink_windows.c.v @@ -0,0 +1,151 @@ +// https://github.com/vlang/v/blob/master/cmd/tools/clockwork_symlink/clockwork_symlink_windows.c.v +import os + +$if tinyc { + #flag -ladvapi32 + #flag -luser32 +} + +const clockwork_executable = os.join_path_single(os.getwd(), 'build/clockwork.exe') + +fn setup_symlink() { + // Create a symlink in a new local folder (.\.bin\clockwork.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 + clockwork_dir := os.real_path(os.dir(clockwork_executable)) + symlink_dir := os.join_path(clockwork_dir, '.bin') + mut clockwork_symlink := os.join_path(symlink_dir, 'clockwork.exe') + // Remove old symlink first (v could have been moved, symlink rerun) + if !os.exists(symlink_dir) { + os.mkdir(symlink_dir) or { panic(err) } + } else { + if os.exists(clockwork_symlink) { + os.rm(clockwork_symlink) or { panic(err) } + } else { + clockwork_symlink = os.join_path(symlink_dir, 'clockwork.bat') + if os.exists(clockwork_symlink) { + os.rm(clockwork_symlink) or { panic(err) } + } + clockwork_symlink = os.join_path(symlink_dir, 'clockwork.exe') + } + } + // First, try to create a native symlink at .\.bin\clockwork.exe + os.symlink(clockwork_executable, clockwork_symlink) 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...') + clockwork_symlink = os.join_path(symlink_dir, 'clockwork.bat') + if os.exists(clockwork_symlink) { + os.rm(clockwork_symlink) or { panic(err) } + } + os.write_file(clockwork_symlink, '@echo off\n"${clockwork_executable}" %*') or { panic(err) } + eprintln('${clockwork_symlink} file written.') + } + if !os.exists(clockwork_symlink) { + warn_and_exit('Could not create ${clockwork_symlink}') + } + println('Symlink ${clockwork_symlink} to ${clockwork_executable} 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 := [symlink_dir] + for p in current_sys_paths { + if p == '' { + continue + } + if p !in new_paths { + new_paths << p + } + } + new_sys_env_path := new_paths.join(os.path_delimiter) + 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 `clockwork` command in your %PATH%') + } + C.RegCloseKey(reg_sys_env_handle) + if os.getenv('GITHUB_JOB') != '' { + // Append V's install location to GITHUBs PATH environment variable. + // Resources: + // 1. https://docs.github.com/en/actions/using-workflows/workflow-commands-for-github-actions#environment-files + // 2. https://docs.github.com/en/actions/using-workflows/workflow-commands-for-github-actions#setting-an-environment-variable + mut content := os.read_file(os.getenv('GITHUB_PATH')) or { + eprintln('The `GITHUB_PATH` env variable is not defined.') + exit(1) + } + content += '\n${new_sys_env_path}\n' + os.write_file(os.getenv('GITHUB_PATH'), content) or { + panic('Failed to write to GITHUB_PATH.') + } + } + println('Done.') + println('Note: Restart your shell/IDE to load the new %PATH%.') + println('After restarting your shell/IDE, give `clockwork --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 { + // open the registry key + reg_key_path := 'Environment' + reg_env_key := unsafe { nil } // or HKEY (HANDLE) + if C.RegOpenKeyEx(os.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 +} + +// get a value from a given $key +fn get_reg_value(reg_env_key voidptr, key string) !string { + // 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, ®_value_size) != 0 { + return error('Unable to get registry value for "${key}".') + } + return unsafe { string_from_wide(reg_value) } +} + +// sets the value for the given $key to the given $value +fn set_reg_value(reg_key voidptr, key string, value string) !bool { + 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 +} + +// 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 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 +} diff --git a/scripts/symlink.vsh b/scripts/symlink.vsh deleted file mode 100644 index 7682d63..0000000 --- a/scripts/symlink.vsh +++ /dev/null @@ -1,19 +0,0 @@ -const clockwork_executable = join_path_single(getwd(), 'build/clockwork') - -// https://github.com/vlang/v/blob/master/cmd/tools/vsymlink/vsymlink_nix.c.v -mut link_path := '/data/data/com.termux/files/usr/bin/clockwork' -if !is_dir('/data/data/com.termux/files') { - link_dir := local_bin_dir() - if !exists(link_dir) { - mkdir_all(link_dir) or { panic(err) } - } - link_path = link_dir + '/clockwork' -} - -rm(link_path) or {} - -symlink(clockwork_executable, link_path) or { - eprintln('Failed to create symlink "${link_path}". You may (but probably do not) need sudo.') - eprintln('Error: ${err}') - exit(1) -}