diff --git a/src/os/executable_darwin.go b/src/os/executable_darwin.go new file mode 100644 index 0000000000..e689db23fb --- /dev/null +++ b/src/os/executable_darwin.go @@ -0,0 +1,19 @@ +//go:build darwin + +package os + +// via runtime because we need argc/argv ptrs +func runtime_executable_path() string + +func Executable() (string, error) { + p := runtime_executable_path() + if p != "" && p[0] == '/' { + // absolute path + return p, nil + } + cwd, err := Getwd() + if err != nil { + return "", err + } + return joinPath(cwd, p), nil +} diff --git a/src/os/executable_other.go b/src/os/executable_other.go index 5da50ede18..ce99c13009 100644 --- a/src/os/executable_other.go +++ b/src/os/executable_other.go @@ -1,4 +1,4 @@ -//go:build !linux || baremetal +//go:build (!linux && !darwin) || baremetal package os diff --git a/src/runtime/os_darwin.go b/src/runtime/os_darwin.go index e7f7b368fb..df5c598807 100644 --- a/src/runtime/os_darwin.go +++ b/src/runtime/os_darwin.go @@ -229,3 +229,38 @@ func call_syscall6(fn, a1, a2, a3, a4, a5, a6 uintptr) int32 //export tinygo_syscall6X func call_syscall6X(fn, a1, a2, a3, a4, a5, a6 uintptr) uintptr + +//go:linkname os_runtime_executable_path os.runtime_executable_path +func os_runtime_executable_path() string { + argv := (*unsafe.Pointer)(unsafe.Pointer(main_argv)) + + // skip over argv + argv = (*unsafe.Pointer)(unsafe.Add(unsafe.Pointer(argv), (uintptr(main_argc)+1)*unsafe.Sizeof(argv))) + + // skip over envv + for (*argv) != nil { + argv = (*unsafe.Pointer)(unsafe.Add(unsafe.Pointer(argv), unsafe.Sizeof(argv))) + } + + // next string is exe path + argv = (*unsafe.Pointer)(unsafe.Add(unsafe.Pointer(argv), unsafe.Sizeof(argv))) + + cstr := unsafe.Pointer(*argv) + length := strlen(cstr) + argString := _string{ + length: length, + ptr: (*byte)(cstr), + } + executablePath := *(*string)(unsafe.Pointer(&argString)) + + // strip "executable_path=" prefix if available, it's added after OS X 10.11. + executablePath = stringsTrimPrefix(executablePath, "executable_path=") + return executablePath +} + +func stringsTrimPrefix(s, prefix string) string { + if len(s) >= len(prefix) && s[:len(prefix)] == prefix { + return s[len(prefix):] + } + return s +}