vsymlink: real Windows symbolic link, fallback to batch, make.bat updates (#5841)

pull/5852/head
Ryan Willis 2020-07-16 09:33:26 -07:00 committed by GitHub
parent f3a505b558
commit f66967a88c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 244 additions and 166 deletions

View File

@ -1,11 +1,6 @@
import os import os
import v.pref import v.pref
const (
hkey_current_user = voidptr(0x80000001)
hwnd_broadcast = voidptr(0xffff)
)
$if windows { $if windows {
$if tinyc { $if tinyc {
#flag -lAdvapi32 #flag -lAdvapi32
@ -14,15 +9,15 @@ $if windows {
} }
fn main(){ fn main(){
vexe := pref.vexe_path()
$if windows { $if windows {
setup_symlink_on_windows() setup_symlink_windows(vexe)
} $else { } $else {
setup_symlink_on_unix() setup_symlink(vexe)
} }
} }
fn setup_symlink_on_unix(){ fn setup_symlink(vexe string){
vexe := pref.vexe_path()
mut link_path := '/usr/local/bin/v' mut link_path := '/usr/local/bin/v'
mut ret := os.exec('ln -sf $vexe $link_path') or { mut ret := os.exec('ln -sf $vexe $link_path') or {
panic(err) panic(err)
@ -45,40 +40,42 @@ fn setup_symlink_on_unix(){
} }
} }
fn setup_symlink_on_windows(){ fn setup_symlink_windows(vexe string){
$if windows { $if windows {
vexe := pref.vexe_path() // Create a symlink in a new local folder (.\.bin\.v.exe)
// NB: Putting $vdir directly into PATH will also result in // Puts `v` in %PATH% without polluting it with anything else (like make.bat).
// make.bat being global, which is NOT what we want. // This will make `v` available on cmd.exe, PowerShell, and MinGW(MSYS)/WSL/Cygwin
//
// Instead, we create a small launcher v.bat, in a new local vdir := os.real_path(os.dir(vexe))
// folder .bin/ . That .bin/ folder can then be put in PATH vsymlinkdir := os.join_path(vdir, '.bin')
// without poluting it with anything else - just a `v`
// command will be available, similar to unix. mut vsymlink := os.join_path(vsymlinkdir, 'v.exe')
//
// Creating a real NTFS symlink to the real executable was also if !os.exists(vsymlinkdir) {
// tried, but then os.real_path( os.executable() ) returns the os.mkdir_all(vsymlinkdir) // will panic if fails
// path to the symlink, unfortunately, unlike on posix systems } else {
// ¯\_(ツ)_/¯ os.rm(vsymlink)
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) // try to create a native symlink at .\.bin\v.exe
os.mkdir_all(vsymlinkdir) os.symlink(vsymlink, vexe) or {
os.write_file(vsymlinkbat, '@echo off\n${vexe} %*') // typically only fails if you're on a network drive (VirtualBox)
if !os.exists(vsymlinkbat) { // do batch file creation instead
eprintln('Could not create $vsymlinkbat') eprint('NOTE: Could not create a native symlink: $err')
exit(1) eprintln('Creating a batch file instead...')
} vsymlink = os.join_path(vsymlinkdir, 'v.bat')
else { if os.exists(vsymlink) {
print('Created $vsymlinkbat, checking system %PATH%...') os.rm(vsymlink)
} }
os.write_file(vsymlink, '@echo off\n${vexe} %*')
} }
if !os.exists(vsymlink) {
warn_and_exit('Could not create $vsymlink')
}
print('Symlink $vsymlink to $vexe created.\n\nChecking system %PATH%...')
reg_sys_env_handle := get_reg_sys_env_handle() or { reg_sys_env_handle := get_reg_sys_env_handle() or {
warn_and_exit(err) warn_and_exit(err)
return return
@ -104,7 +101,7 @@ fn setup_symlink_on_windows(){
println('configured.') println('configured.')
} }
else { else {
print('not configured.\nSetting system %PATH%...') print('not configured.\nAdding symlink directory to system %PATH%...')
set_reg_value(reg_sys_env_handle, 'Path', new_sys_env_path) or { set_reg_value(reg_sys_env_handle, 'Path', new_sys_env_path) or {
warn_and_exit(err) warn_and_exit(err)
return return
@ -115,12 +112,12 @@ fn setup_symlink_on_windows(){
print('Letting running process know to update their Environment...') print('Letting running process know to update their Environment...')
send_setting_change_msg('Environment') or { send_setting_change_msg('Environment') or {
eprintln('\n' + err) eprintln('\n' + err)
warn_and_exit('You might need to run this again to have `v` in your %PATH%') warn_and_exit('You might need to run this again to have the `v` command in your %PATH%')
return return
} }
println('finished.\n\nNote: restart your shell/IDE to load the new %PATH%.') 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!') println('After restarting your shell/IDE, give `v version` a try in another dir!')
} }
} }
@ -131,14 +128,13 @@ fn warn_and_exit(err string) {
// get the system environment registry handle // get the system environment registry handle
fn get_reg_sys_env_handle() ?voidptr { fn get_reg_sys_env_handle() ?voidptr {
$if windows { $if windows { // wrap for cross-compile compat
// open the registry key // open the registry key
reg_key_path := 'Environment' reg_key_path := 'Environment'
reg_env_key := voidptr(0) // or HKEY (HANDLE) reg_env_key := voidptr(0) // or HKEY (HANDLE)
if C.RegOpenKeyEx(hkey_current_user, reg_key_path.to_wide(), 0, 1 | 2, &reg_env_key) != 0 { 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 error('Could not open "$reg_key_path" in the registry')
} }
return reg_env_key return reg_env_key
} }
return error('not on windows') return error('not on windows')
@ -171,11 +167,11 @@ fn set_reg_value(reg_key voidptr, key string, value string) ?bool {
return error('not on windows') return error('not on windows')
} }
// broadcasts a message to all listening windows (explorer.exe in particular) // Broadcasts a message to all listening windows (explorer.exe in particular)
// letting them know that the system environment has changed and should be reloaded // letting them know that the system environment has changed and should be reloaded
fn send_setting_change_msg(message_data string) ?bool { fn send_setting_change_msg(message_data string) ?bool {
$if windows { $if windows {
if C.SendMessageTimeout(hwnd_broadcast, 0x001A, 0, message_data.to_wide(), 2, 5000, 0) == 0 { if C.SendMessageTimeout(os.hwnd_broadcast, os.wm_settingchange, 0, message_data.to_wide(), os.smto_abortifhung, 5000, 0) == 0 {
return error('Could not broadcast WM_SETTINGCHANGE') return error('Could not broadcast WM_SETTINGCHANGE')
} }
return true return true

View File

@ -2,8 +2,24 @@
echo Building V echo Building V
REM default tcc
set tcc_url=https://github.com/vlang/tccbin_win
set tcc_dir=%~dp0thirdparty\tcc
REM let a particular environment specify their own tcc
if "%TCC_GIT%" =="" goto :init
set tcc_url="%TCC_GIT%"
:init
REM initialize the log file with the failure message
set log_file=%TEMP%\v_make.bat.log set log_file=%TEMP%\v_make.bat.log
set tcc_path=%~dp0thirdparty\tcc\ echo Failed to compile - Create an issue at 'https://github.com/vlang/v' with the following info:>%log_file%
echo.>>%log_file%
REM alleviate weird issues with this var
set cloned_tcc=
set ERRORLEVEL=
pushd %~dp0 pushd %~dp0
if "%~1"=="-local" goto :compile if "%~1"=="-local" goto :compile
@ -21,12 +37,14 @@ if exist "vc" (
:compile :compile
REM option to force msvc, gcc or tcc REM option to force msvc, gcc or tcc
if "%~1"=="-gcc" set force_gcc=1 & goto :gcc_strap if "%~1"=="-gcc" set force_gcc=1 & goto :gcc_strap
if "%~2"=="-gcc" set force_gcc=1 & goto :gcc_strap if "%~2"=="-gcc" set force_gcc=1 & goto :gcc_strap
if "%~1"=="-msvc" set force_msvc=1 & goto :msvc_strap if "%~1"=="-msvc" set force_msvc=1 & goto :msvc_strap
if "%~2"=="-msvc" set force_msvc=1 & goto :msvc_strap if "%~2"=="-msvc" set force_msvc=1 & goto :msvc_strap
if "%~1"=="-tcc" set force_tcc=1 & goto :tcc_strap if "%~1"=="-tcc" set force_tcc=1 & goto :tcc_strap
if "%~2"=="-tcc" set force_tcc=1 & goto :tcc_strap if "%~2"=="-tcc" set force_tcc=1 & goto :tcc_strap
if "%~1"=="-fresh_tcc" set force_tcc=1 & goto :fresh_tcc
if "%~2"=="-fresh_tcc" set force_tcc=1 & goto :fresh_tcc
:gcc_strap :gcc_strap
echo. echo.
@ -39,13 +57,14 @@ if %ERRORLEVEL% NEQ 0 (
goto :msvc_strap goto :msvc_strap
) )
gcc -std=c99 -municode -w -o v.exe vc\v_win.c>>%log_file% 2>>&1 gcc -std=c99 -municode -w -o v.exe .\vc\v_win.c>>%log_file% 2>>&1
if %ERRORLEVEL% NEQ 0 ( if %ERRORLEVEL% NEQ 0 (
rem In most cases, compile errors happen because the version of GCC installed is too old rem In most cases, compile errors happen because the version of GCC installed is too old
gcc --version>>%log_file% 2>>&1 gcc --version>>%log_file% 2>>&1
goto :compile_error goto :compile_error
) )
echo ^> Compiling with .\v.exe self
v.exe self>>%log_file% 2>>&1 v.exe self>>%log_file% 2>>&1
if %ERRORLEVEL% NEQ 0 goto :compile_error if %ERRORLEVEL% NEQ 0 goto :compile_error
goto :success goto :success
@ -79,16 +98,20 @@ if exist "%InstallDir%\Common7\Tools\vsdevcmd.bat" (
set ObjFile=.v.c.obj set ObjFile=.v.c.obj
cl.exe /nologo /w /volatile:ms /Fo%ObjFile% /O2 /MD /D_VBOOTSTRAP vc\v_win.c user32.lib kernel32.lib advapi32.lib shell32.lib /link /NOLOGO /OUT:v.exe /INCREMENTAL:NO>>%log_file% 2>>&1 cl.exe /volatile:ms /Fo%ObjFile% /O2 /MD /D_VBOOTSTRAP vc\v_win.c user32.lib kernel32.lib advapi32.lib shell32.lib /link /nologo /out:v.exe /incremental:no>>%log_file% 2>>&1
if %ERRORLEVEL% NEQ 0 goto :compile_error if %ERRORLEVEL% NEQ 0 goto :compile_error
echo ^> Compiling with .\v.exe self
v.exe -cc msvc self>>%log_file% 2>>&1 v.exe -cc msvc self>>%log_file% 2>>&1
del %ObjFile% del %ObjFile%>>%log_file% 2>>&1
if %ERRORLEVEL% NEQ 0 goto :compile_error if %ERRORLEVEL% NEQ 0 goto :compile_error
goto :success goto :success
:fresh_tcc
rd /s /q %tcc_dir%
:clone_tcc :clone_tcc
git clone --depth 1 --quiet https://github.com/vlang/tccbin_win %tcc_path% git clone --depth 1 --quiet %tcc_url% %tcc_dir%
set cloned_tcc=1 set cloned_tcc=1
goto :tcc_strap goto :tcc_strap
@ -98,31 +121,33 @@ echo Attempting to build v.c with TCC...
where /q tcc where /q tcc
if %ERRORLEVEL% NEQ 0 ( if %ERRORLEVEL% NEQ 0 (
if exist "%tcc_path%" ( if exist "%tcc_dir%" (
set tcc_exe=%tcc_path%tcc.exe set tcc_exe=%tcc_dir%\tcc.exe
) else if "%cloned_tcc%"=="" ( ) else if "%cloned_tcc%"=="" (
echo ^> TCC not found echo ^> TCC not found
echo ^> Downloading TCC from https://github.com/vlang/tccbin_win echo ^> Downloading TCC from %tcc_url%
goto :clone_tcc goto :clone_tcc
) else ( ) else (
echo ^> TCC not found, even after cloning echo ^> TCC not found, even after cloning %cloned_tcc%
goto :error goto :error
) )
) else ( ) else (
for /f "delims=" %%i in ('where tcc') do set tcc_exe=%%i for /f "delims=" %%i in ('where tcc') do set tcc_exe=%%i
) )
if exist "%tcc_path%" ( if exist "%tcc_dir%" (
if "%cloned_tcc%"=="" ( if "%cloned_tcc%"=="" (
echo ^> Updating prebuilt TCC... echo ^> Updating prebuilt TCC...
pushd "%tcc_path%" pushd "%tcc_dir%"\
git pull -q git pull -q
popd popd
) )
) )
call "%tcc_exe%" -std=c99 -municode -lws2_32 -lshell32 -ladvapi32 -bt10 -w -o v.exe vc\v_win.c
%tcc_exe% -std=c99 -municode -lws2_32 -lshell32 -ladvapi32 -bt10 -w -o v.exe vc\v_win.c
if %ERRORLEVEL% NEQ 0 goto :compile_error if %ERRORLEVEL% NEQ 0 goto :compile_error
echo ^> Compiling with .\v.exe self
v.exe -cc "%tcc_exe%" self>>%log_file% 2>>&1 v.exe -cc "%tcc_exe%" self>>%log_file% 2>>&1
if %ERRORLEVEL% NEQ 0 goto :compile_error if %ERRORLEVEL% NEQ 0 goto :compile_error
goto :success goto :success
@ -130,8 +155,6 @@ goto :success
:compile_error :compile_error
echo. echo.
echo. echo.
echo Failed to compile - Create an issue at 'https://github.com/vlang' with the following info:
echo.
type %log_file% type %log_file%
del %log_file% del %log_file%
goto :error goto :error
@ -144,21 +167,23 @@ exit /b 1
:success :success
echo ^> V built successfully! echo ^> V built successfully!
echo ^> To add V to your PATH, run `.\v symlink`. echo ^> To add V to your PATH, run `.\v.exe symlink`.
del v_old.exe del v_old.exe >>%log_file% 2>>&1
del %log_file% del %log_file%
:version :version
echo. echo.
echo | set /p="V version: " echo | set /p="V version: "
v.exe version .\v.exe version
if "%cloned_tcc%" NEQ "" ( if "%cloned_tcc%" NEQ "" (
echo. if "%force_tcc%" == "" (
echo WARNING: No C compiler was detected in your PATH. `tcc` was used temporarily echo.
echo to build V, but it may have some bugs and may not work in all cases. echo WARNING: No C compiler was detected in your PATH. `tcc` was used temporarily
echo A more advanced C compiler like GCC or MSVC is recommended. echo to build V, but it may have some bugs and may not work in all cases.
echo https://github.com/vlang/v/wiki/Installing-a-C-compiler-on-Windows echo A more advanced C compiler like GCC or MSVC is recommended.
echo. echo https://github.com/vlang/v/wiki/Installing-a-C-compiler-on-Windows
echo.
)
) )
popd popd

View File

@ -191,9 +191,17 @@ fn C._fileno(int) int
fn C._get_osfhandle(fd int) C.intptr_t fn C._get_osfhandle(fd int) C.intptr_t
fn C.GetModuleFileName() int
fn C.GetModuleFileNameW(hModule voidptr, lpFilename &u16, nSize u32) u32 fn C.GetModuleFileNameW(hModule voidptr, lpFilename &u16, nSize u32) u32
fn C.CreateFile() voidptr
fn C.CreateFileW(lpFilename &u16, dwDesiredAccess u32, dwShareMode u32, lpSecurityAttributes &u16, dwCreationDisposition u32, dwFlagsAndAttributes u32, hTemplateFile voidptr) u32
fn C.GetFinalPathNameByHandleW(hFile voidptr, lpFilePath &u16, nSize u32, dwFlags u32) int
fn C.CreatePipe(hReadPipe &voidptr, hWritePipe &voidptr, lpPipeAttributes voidptr, nSize u32) bool fn C.CreatePipe(hReadPipe &voidptr, hWritePipe &voidptr, lpPipeAttributes voidptr, nSize u32) bool
@ -304,9 +312,6 @@ fn C.WriteConsole() voidptr
fn C.WriteFile() voidptr fn C.WriteFile() voidptr
fn C.GetModuleFileName() int
fn C._wchdir() fn C._wchdir()

View File

@ -2,63 +2,67 @@ module os
// Ref - winnt.h // Ref - winnt.h
const ( const (
success = 0 // ERROR_SUCCESS success = 0x0000 // ERROR_SUCCESS
error_insufficient_buffer = 130 error_insufficient_buffer = 0x0082
) )
const ( const (
file_share_read = 1 handle_generic_read = 0x80000000
file_share_write = 2 handle_open_existing = 0x00000003
file_share_delete = 4
) )
const ( const (
file_notify_change_file_name = 1 file_share_read = 0x01
file_notify_change_dir_name = 2 file_share_write = 0x02
file_notify_change_attributes = 4 file_share_delete = 0x04
file_notify_change_size = 8
file_notify_change_last_write = 16
file_notify_change_last_access = 32
file_notify_change_creation = 64
file_notify_change_security = 128
) )
const ( const (
file_action_added = 1 file_notify_change_file_name = 0x01
file_action_removed = 2 file_notify_change_dir_name = 0x02
file_action_modified = 3 file_notify_change_attributes = 0x04
file_action_renamed_old_name = 4 file_notify_change_size = 0x08
file_action_renamed_new_name = 5 file_notify_change_last_write = 0x10
file_notify_change_last_access = 0x20
file_notify_change_creation = 0x40
file_notify_change_security = 0x80
) )
const ( const (
file_attr_readonly = 0x1 file_action_added = 0x01
file_attr_hidden = 0x2 file_action_removed = 0x02
file_attr_system = 0x4 file_action_modified = 0x03
file_attr_directory = 0x10 file_action_renamed_old_name = 0x04
file_attr_archive = 0x20 file_action_renamed_new_name = 0x05
file_attr_device = 0x40 )
file_attr_normal = 0x80
file_attr_temporary = 0x100 const (
file_attr_sparse_file = 0x200 file_attr_readonly = 0x00000001
file_attr_reparse_point = 0x400 file_attr_hidden = 0x00000002
file_attr_compressed = 0x800 file_attr_system = 0x00000004
file_attr_offline = 0x1000 file_attr_directory = 0x00000010
file_attr_not_content_indexed = 0x2000 file_attr_archive = 0x00000020
file_attr_encrypted = 0x4000 file_attr_device = 0x00000040
file_attr_integrity_stream = 0x8000 file_attr_normal = 0x00000080
file_attr_virtual = 0x10000 file_attr_temporary = 0x00000100
file_attr_no_scrub_data = 0x20000 file_attr_sparse_file = 0x00000200
file_attr_reparse_point = 0x00000400
file_attr_compressed = 0x00000800
file_attr_offline = 0x00001000
file_attr_not_content_indexed = 0x00002000
file_attr_encrypted = 0x00004000
file_attr_integrity_stream = 0x00008000
file_attr_virtual = 0x00010000
file_attr_no_scrub_data = 0x00020000
// file_attr_recall_on_open = u32(0x...) // file_attr_recall_on_open = u32(0x...)
// file_attr_recall_on_data_access = u32(0x...) // file_attr_recall_on_data_access = u32(0x...)
) )
const ( const (
file_type_disk = 0x1 file_type_unknown = 0x00
file_type_char = 0x2 file_type_disk = 0x01
file_type_pipe = 0x3 file_type_char = 0x02
file_type_pipe = 0x03
file_type_unknown = 0x0
) )
const ( const (
@ -82,58 +86,79 @@ const (
enable_window_input = 0x0008 enable_window_input = 0x0008
enable_virtual_terminal_input = 0x0200 enable_virtual_terminal_input = 0x0200
// Output Screen Buffer // Output Screen Buffer
enable_processed_output = 0x0001 enable_processed_output = 0x01
enable_wrap_at_eol_output = 0x0002 enable_wrap_at_eol_output = 0x02
enable_virtual_terminal_processing = 0x0004 enable_virtual_terminal_processing = 0x04
disable_newline_auto_return = 0x0008 disable_newline_auto_return = 0x08
enable_lvb_grid_worldwide = 0x0010 enable_lvb_grid_worldwide = 0x10
) )
// File modes // File modes
const ( const (
o_rdonly = 0 // open the file read-only. o_rdonly = 0x0000 // open the file read-only.
o_wronly = 1 // open the file write-only. o_wronly = 0x0001 // open the file write-only.
o_rdwr = 2 // open the file read-write. o_rdwr = 0x0002 // open the file read-write.
o_append = 0x0008 // append data to the file when writing. o_append = 0x0008 // append data to the file when writing.
o_create = 0x0100 // create a new file if none exists. o_create = 0x0100 // create a new file if none exists.
o_trunc = 0x0200 // truncate regular writable file when opened. o_trunc = 0x0200 // truncate regular writable file when opened.
o_excl = 0x0400 // used with o_create, file must not exist. o_excl = 0x0400 // used with o_create, file must not exist.
o_sync = 0 // open for synchronous I/O (ignored on Windows) o_sync = 0x0000 // open for synchronous I/O (ignored on Windows)
o_noctty = 0 // make file non-controlling tty (ignored on Windows) o_noctty = 0x0000 // make file non-controlling tty (ignored on Windows)
o_nonblock = 0 // don't block on opening file (ignored on Windows) o_nonblock = 0x0000 // don't block on opening file (ignored on Windows)
) )
const ( const (
status_access_violation = 0xC0000005 status_access_violation = 0xC0000005
status_in_page_error = 0xC0000006 status_in_page_error = 0xC0000006
status_invalid_handle = 0xC0000008 status_invalid_handle = 0xC0000008
status_invalid_parameter = 0xC000000D status_invalid_parameter = 0xC000000D
status_no_memory = 0xC0000017 status_no_memory = 0xC0000017
status_illegal_instruction = 0xC000001D status_illegal_instruction = 0xC000001D
status_noncontinuable_exception = 0xC0000025 status_noncontinuable_exception = 0xC0000025
status_invalid_disposition = 0xC0000026 status_invalid_disposition = 0xC0000026
status_array_bounds_exceeded = 0xC000008C status_array_bounds_exceeded = 0xC000008C
status_float_denormal_operand = 0xC000008D status_float_denormal_operand = 0xC000008D
status_float_divide_by_zero = 0xC000008E status_float_divide_by_zero = 0xC000008E
status_float_inexact_result = 0xC000008F status_float_inexact_result = 0xC000008F
status_float_invalid_operation = 0xC0000090 status_float_invalid_operation = 0xC0000090
status_float_overflow = 0xC0000091 status_float_overflow = 0xC0000091
status_float_stack_check = 0xC0000092 status_float_stack_check = 0xC0000092
status_float_underflow = 0xC0000093 status_float_underflow = 0xC0000093
status_integer_divide_by_zero = 0xC0000094 status_integer_divide_by_zero = 0xC0000094
status_integer_overflow = 0xC0000095 status_integer_overflow = 0xC0000095
status_privileged_instruction = 0xC0000096 status_privileged_instruction = 0xC0000096
status_stack_overflow = 0xC00000FD status_stack_overflow = 0xC00000FD
status_dll_not_found = 0xC0000135 status_dll_not_found = 0xC0000135
status_ordinal_not_found = 0xC0000138 status_ordinal_not_found = 0xC0000138
status_entrypoint_not_found = 0xC0000139 status_entrypoint_not_found = 0xC0000139
status_control_c_exit = 0xC000013A status_control_c_exit = 0xC000013A
status_dll_init_failed = 0xC0000142 status_dll_init_failed = 0xC0000142
status_float_multiple_faults = 0xC00002B4 status_float_multiple_faults = 0xC00002B4
status_float_multiple_traps = 0xC00002B5 status_float_multiple_traps = 0xC00002B5
status_reg_nat_consumption = 0xC00002C9 status_reg_nat_consumption = 0xC00002C9
status_heap_corruption = 0xC0000374 status_heap_corruption = 0xC0000374
status_stack_buffer_overrun = 0xC0000409 status_stack_buffer_overrun = 0xC0000409
status_invalid_cruntime_parameter = 0xC0000417 status_invalid_cruntime_parameter = 0xC0000417
status_assertion_failure = 0xC0000420 status_assertion_failure = 0xC0000420
) )
// Windows Registry Constants
pub const (
hkey_local_machine = voidptr(0x80000002)
hkey_current_user = voidptr(0x80000001)
key_query_value = 0x0001
key_set_value = 0x0002
key_enumerate_sub_keys = 0x0008
key_wow64_32key = 0x0200
)
// Windows Messages
pub const (
hwnd_broadcast = voidptr(0xFFFF)
wm_settingchange = 0x001A
smto_abortifhung = 0x0002
)

View File

@ -948,8 +948,30 @@ pub fn executable() string {
} }
$if windows { $if windows {
max := 512 max := 512
mut result := &u16(vcalloc(max * 2)) // max_path_len * sizeof(wchar_t) size := max * 2 // max_path_len * sizeof(wchar_t)
mut result := &u16(vcalloc(size))
len := C.GetModuleFileName(0, result, max) len := C.GetModuleFileName(0, result, max)
// determine if the file is a windows symlink
attrs := C.GetFileAttributesW(result)
is_set := attrs & 0x400 // FILE_ATTRIBUTE_REPARSE_POINT
if is_set != 0 { // it's a windows symlink
// gets handle with GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0
file := C.CreateFile(result, 0x80000000, 1, 0, 3, 0x80, 0)
if file != -1 {
final_path := &u16(vcalloc(size))
// https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-getfinalpathnamebyhandlew
final_len := C.GetFinalPathNameByHandleW(file, final_path, size, 0)
if final_len < size {
ret := string_from_wide2(final_path, final_len)
// remove '\\?\' from beginning (see link above)
return ret[4..]
}
else {
eprintln('os.executable() saw that the executable file path was too long')
}
}
C.CloseHandle(file)
}
return string_from_wide2(result, len) return string_from_wide2(result, len)
} }
$if macos { $if macos {
@ -1003,6 +1025,11 @@ fn executable_fallback() string {
return '' return ''
} }
mut exepath := os.args[0] mut exepath := os.args[0]
$if windows {
if !exepath.contains('.exe') {
exepath += '.exe'
}
}
if !os.is_abs_path(exepath) { if !os.is_abs_path(exepath) {
if exepath.contains( os.path_separator ) { if exepath.contains( os.path_separator ) {
exepath = os.join_path(os.wd_at_startup, exepath) exepath = os.join_path(os.wd_at_startup, exepath)