vsymlink: real Windows symbolic link, fallback to batch, make.bat updates (#5841)
parent
f3a505b558
commit
f66967a88c
|
@ -1,11 +1,6 @@
|
|||
import os
|
||||
import v.pref
|
||||
|
||||
const (
|
||||
hkey_current_user = voidptr(0x80000001)
|
||||
hwnd_broadcast = voidptr(0xffff)
|
||||
)
|
||||
|
||||
$if windows {
|
||||
$if tinyc {
|
||||
#flag -lAdvapi32
|
||||
|
@ -14,15 +9,15 @@ $if windows {
|
|||
}
|
||||
|
||||
fn main(){
|
||||
vexe := pref.vexe_path()
|
||||
$if windows {
|
||||
setup_symlink_on_windows()
|
||||
setup_symlink_windows(vexe)
|
||||
} $else {
|
||||
setup_symlink_on_unix()
|
||||
setup_symlink(vexe)
|
||||
}
|
||||
}
|
||||
|
||||
fn setup_symlink_on_unix(){
|
||||
vexe := pref.vexe_path()
|
||||
fn setup_symlink(vexe string){
|
||||
mut link_path := '/usr/local/bin/v'
|
||||
mut ret := os.exec('ln -sf $vexe $link_path') or {
|
||||
panic(err)
|
||||
|
@ -45,40 +40,42 @@ fn setup_symlink_on_unix(){
|
|||
}
|
||||
}
|
||||
|
||||
fn setup_symlink_on_windows(){
|
||||
fn setup_symlink_windows(vexe string){
|
||||
$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
|
||||
// ¯\_(ツ)_/¯
|
||||
// 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')
|
||||
vsymlinkbat := os.join_path(vsymlinkdir, 'v.bat')
|
||||
if os.exists(vsymlinkbat) {
|
||||
print('Batch script $vsymlinkbat already exists, checking system %PATH%...')
|
||||
|
||||
mut vsymlink := os.join_path(vsymlinkdir, 'v.exe')
|
||||
|
||||
if !os.exists(vsymlinkdir) {
|
||||
os.mkdir_all(vsymlinkdir) // will panic if fails
|
||||
} else {
|
||||
os.rm(vsymlink)
|
||||
}
|
||||
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)
|
||||
|
||||
// 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
|
||||
eprint('NOTE: 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)
|
||||
}
|
||||
else {
|
||||
print('Created $vsymlinkbat, checking system %PATH%...')
|
||||
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 {
|
||||
warn_and_exit(err)
|
||||
return
|
||||
|
@ -104,7 +101,7 @@ fn setup_symlink_on_windows(){
|
|||
println('configured.')
|
||||
}
|
||||
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 {
|
||||
warn_and_exit(err)
|
||||
return
|
||||
|
@ -115,12 +112,12 @@ fn setup_symlink_on_windows(){
|
|||
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%')
|
||||
warn_and_exit('You might need to run this again to have the `v` command 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!')
|
||||
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
|
||||
fn get_reg_sys_env_handle() ?voidptr {
|
||||
$if windows {
|
||||
$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(hkey_current_user, reg_key_path.to_wide(), 0, 1 | 2, ®_env_key) != 0 {
|
||||
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
|
||||
}
|
||||
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')
|
||||
}
|
||||
|
||||
// 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
|
||||
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 {
|
||||
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 true
|
||||
|
|
59
make.bat
59
make.bat
|
@ -2,8 +2,24 @@
|
|||
|
||||
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 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
|
||||
|
||||
if "%~1"=="-local" goto :compile
|
||||
|
@ -27,6 +43,8 @@ if "%~1"=="-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 "%~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
|
||||
echo.
|
||||
|
@ -39,13 +57,14 @@ if %ERRORLEVEL% NEQ 0 (
|
|||
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 (
|
||||
rem In most cases, compile errors happen because the version of GCC installed is too old
|
||||
gcc --version>>%log_file% 2>>&1
|
||||
goto :compile_error
|
||||
)
|
||||
|
||||
echo ^> Compiling with .\v.exe self
|
||||
v.exe self>>%log_file% 2>>&1
|
||||
if %ERRORLEVEL% NEQ 0 goto :compile_error
|
||||
goto :success
|
||||
|
@ -79,16 +98,20 @@ if exist "%InstallDir%\Common7\Tools\vsdevcmd.bat" (
|
|||
|
||||
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
|
||||
|
||||
echo ^> Compiling with .\v.exe self
|
||||
v.exe -cc msvc self>>%log_file% 2>>&1
|
||||
del %ObjFile%
|
||||
del %ObjFile%>>%log_file% 2>>&1
|
||||
if %ERRORLEVEL% NEQ 0 goto :compile_error
|
||||
goto :success
|
||||
|
||||
:fresh_tcc
|
||||
rd /s /q %tcc_dir%
|
||||
|
||||
: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
|
||||
goto :tcc_strap
|
||||
|
||||
|
@ -98,31 +121,33 @@ echo Attempting to build v.c with TCC...
|
|||
|
||||
where /q tcc
|
||||
if %ERRORLEVEL% NEQ 0 (
|
||||
if exist "%tcc_path%" (
|
||||
set tcc_exe=%tcc_path%tcc.exe
|
||||
if exist "%tcc_dir%" (
|
||||
set tcc_exe=%tcc_dir%\tcc.exe
|
||||
) else if "%cloned_tcc%"=="" (
|
||||
echo ^> TCC not found
|
||||
echo ^> Downloading TCC from https://github.com/vlang/tccbin_win
|
||||
echo ^> Downloading TCC from %tcc_url%
|
||||
goto :clone_tcc
|
||||
) else (
|
||||
echo ^> TCC not found, even after cloning
|
||||
echo ^> TCC not found, even after cloning %cloned_tcc%
|
||||
goto :error
|
||||
)
|
||||
) else (
|
||||
for /f "delims=" %%i in ('where tcc') do set tcc_exe=%%i
|
||||
)
|
||||
|
||||
if exist "%tcc_path%" (
|
||||
if exist "%tcc_dir%" (
|
||||
if "%cloned_tcc%"=="" (
|
||||
echo ^> Updating prebuilt TCC...
|
||||
pushd "%tcc_path%"
|
||||
pushd "%tcc_dir%"\
|
||||
git pull -q
|
||||
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
|
||||
|
||||
echo ^> Compiling with .\v.exe self
|
||||
v.exe -cc "%tcc_exe%" self>>%log_file% 2>>&1
|
||||
if %ERRORLEVEL% NEQ 0 goto :compile_error
|
||||
goto :success
|
||||
|
@ -130,8 +155,6 @@ goto :success
|
|||
:compile_error
|
||||
echo.
|
||||
echo.
|
||||
echo Failed to compile - Create an issue at 'https://github.com/vlang' with the following info:
|
||||
echo.
|
||||
type %log_file%
|
||||
del %log_file%
|
||||
goto :error
|
||||
|
@ -144,21 +167,23 @@ exit /b 1
|
|||
|
||||
:success
|
||||
echo ^> V built successfully!
|
||||
echo ^> To add V to your PATH, run `.\v symlink`.
|
||||
del v_old.exe
|
||||
echo ^> To add V to your PATH, run `.\v.exe symlink`.
|
||||
del v_old.exe >>%log_file% 2>>&1
|
||||
del %log_file%
|
||||
|
||||
:version
|
||||
echo.
|
||||
echo | set /p="V version: "
|
||||
v.exe version
|
||||
.\v.exe version
|
||||
if "%cloned_tcc%" NEQ "" (
|
||||
if "%force_tcc%" == "" (
|
||||
echo.
|
||||
echo WARNING: No C compiler was detected in your PATH. `tcc` was used temporarily
|
||||
echo to build V, but it may have some bugs and may not work in all cases.
|
||||
echo A more advanced C compiler like GCC or MSVC is recommended.
|
||||
echo https://github.com/vlang/v/wiki/Installing-a-C-compiler-on-Windows
|
||||
echo.
|
||||
)
|
||||
)
|
||||
|
||||
popd
|
||||
|
|
|
@ -191,9 +191,17 @@ fn C._fileno(int) int
|
|||
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.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
|
||||
|
||||
|
||||
|
@ -304,9 +312,6 @@ fn C.WriteConsole() voidptr
|
|||
fn C.WriteFile() voidptr
|
||||
|
||||
|
||||
fn C.GetModuleFileName() int
|
||||
|
||||
|
||||
fn C._wchdir()
|
||||
|
||||
|
||||
|
|
|
@ -2,63 +2,67 @@ module os
|
|||
|
||||
// Ref - winnt.h
|
||||
const (
|
||||
success = 0 // ERROR_SUCCESS
|
||||
error_insufficient_buffer = 130
|
||||
success = 0x0000 // ERROR_SUCCESS
|
||||
error_insufficient_buffer = 0x0082
|
||||
)
|
||||
|
||||
const (
|
||||
file_share_read = 1
|
||||
file_share_write = 2
|
||||
file_share_delete = 4
|
||||
handle_generic_read = 0x80000000
|
||||
handle_open_existing = 0x00000003
|
||||
)
|
||||
|
||||
const (
|
||||
file_notify_change_file_name = 1
|
||||
file_notify_change_dir_name = 2
|
||||
file_notify_change_attributes = 4
|
||||
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
|
||||
file_share_read = 0x01
|
||||
file_share_write = 0x02
|
||||
file_share_delete = 0x04
|
||||
)
|
||||
|
||||
const (
|
||||
file_action_added = 1
|
||||
file_action_removed = 2
|
||||
file_action_modified = 3
|
||||
file_action_renamed_old_name = 4
|
||||
file_action_renamed_new_name = 5
|
||||
file_notify_change_file_name = 0x01
|
||||
file_notify_change_dir_name = 0x02
|
||||
file_notify_change_attributes = 0x04
|
||||
file_notify_change_size = 0x08
|
||||
file_notify_change_last_write = 0x10
|
||||
file_notify_change_last_access = 0x20
|
||||
file_notify_change_creation = 0x40
|
||||
file_notify_change_security = 0x80
|
||||
)
|
||||
|
||||
const (
|
||||
file_attr_readonly = 0x1
|
||||
file_attr_hidden = 0x2
|
||||
file_attr_system = 0x4
|
||||
file_attr_directory = 0x10
|
||||
file_attr_archive = 0x20
|
||||
file_attr_device = 0x40
|
||||
file_attr_normal = 0x80
|
||||
file_attr_temporary = 0x100
|
||||
file_attr_sparse_file = 0x200
|
||||
file_attr_reparse_point = 0x400
|
||||
file_attr_compressed = 0x800
|
||||
file_attr_offline = 0x1000
|
||||
file_attr_not_content_indexed = 0x2000
|
||||
file_attr_encrypted = 0x4000
|
||||
file_attr_integrity_stream = 0x8000
|
||||
file_attr_virtual = 0x10000
|
||||
file_attr_no_scrub_data = 0x20000
|
||||
file_action_added = 0x01
|
||||
file_action_removed = 0x02
|
||||
file_action_modified = 0x03
|
||||
file_action_renamed_old_name = 0x04
|
||||
file_action_renamed_new_name = 0x05
|
||||
)
|
||||
|
||||
const (
|
||||
file_attr_readonly = 0x00000001
|
||||
file_attr_hidden = 0x00000002
|
||||
file_attr_system = 0x00000004
|
||||
file_attr_directory = 0x00000010
|
||||
file_attr_archive = 0x00000020
|
||||
file_attr_device = 0x00000040
|
||||
file_attr_normal = 0x00000080
|
||||
file_attr_temporary = 0x00000100
|
||||
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_data_access = u32(0x...)
|
||||
)
|
||||
|
||||
const (
|
||||
file_type_disk = 0x1
|
||||
file_type_char = 0x2
|
||||
file_type_pipe = 0x3
|
||||
|
||||
file_type_unknown = 0x0
|
||||
file_type_unknown = 0x00
|
||||
file_type_disk = 0x01
|
||||
file_type_char = 0x02
|
||||
file_type_pipe = 0x03
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -82,25 +86,25 @@ const (
|
|||
enable_window_input = 0x0008
|
||||
enable_virtual_terminal_input = 0x0200
|
||||
// Output Screen Buffer
|
||||
enable_processed_output = 0x0001
|
||||
enable_wrap_at_eol_output = 0x0002
|
||||
enable_virtual_terminal_processing = 0x0004
|
||||
disable_newline_auto_return = 0x0008
|
||||
enable_lvb_grid_worldwide = 0x0010
|
||||
enable_processed_output = 0x01
|
||||
enable_wrap_at_eol_output = 0x02
|
||||
enable_virtual_terminal_processing = 0x04
|
||||
disable_newline_auto_return = 0x08
|
||||
enable_lvb_grid_worldwide = 0x10
|
||||
)
|
||||
|
||||
// File modes
|
||||
const (
|
||||
o_rdonly = 0 // open the file read-only.
|
||||
o_wronly = 1 // open the file write-only.
|
||||
o_rdwr = 2 // open the file read-write.
|
||||
o_rdonly = 0x0000 // open the file read-only.
|
||||
o_wronly = 0x0001 // open the file write-only.
|
||||
o_rdwr = 0x0002 // open the file read-write.
|
||||
o_append = 0x0008 // append data to the file when writing.
|
||||
o_create = 0x0100 // create a new file if none exists.
|
||||
o_trunc = 0x0200 // truncate regular writable file when opened.
|
||||
o_excl = 0x0400 // used with o_create, file must not exist.
|
||||
o_sync = 0 // open for synchronous I/O (ignored on Windows)
|
||||
o_noctty = 0 // make file non-controlling tty (ignored on Windows)
|
||||
o_nonblock = 0 // don't block on opening file (ignored on Windows)
|
||||
o_sync = 0x0000 // open for synchronous I/O (ignored on Windows)
|
||||
o_noctty = 0x0000 // make file non-controlling tty (ignored on Windows)
|
||||
o_nonblock = 0x0000 // don't block on opening file (ignored on Windows)
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -137,3 +141,24 @@ const (
|
|||
status_invalid_cruntime_parameter = 0xC0000417
|
||||
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
|
||||
)
|
29
vlib/os/os.v
29
vlib/os/os.v
|
@ -948,8 +948,30 @@ pub fn executable() string {
|
|||
}
|
||||
$if windows {
|
||||
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)
|
||||
// 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)
|
||||
}
|
||||
$if macos {
|
||||
|
@ -1003,6 +1025,11 @@ fn executable_fallback() string {
|
|||
return ''
|
||||
}
|
||||
mut exepath := os.args[0]
|
||||
$if windows {
|
||||
if !exepath.contains('.exe') {
|
||||
exepath += '.exe'
|
||||
}
|
||||
}
|
||||
if !os.is_abs_path(exepath) {
|
||||
if exepath.contains( os.path_separator ) {
|
||||
exepath = os.join_path(os.wd_at_startup, exepath)
|
||||
|
|
Loading…
Reference in New Issue