diff --git a/Utilities/FileSystem.h b/Utilities/FileSystem.h index fc935249..e783ddce 100644 --- a/Utilities/FileSystem.h +++ b/Utilities/FileSystem.h @@ -12,6 +12,7 @@ #else #include #include +#include #endif #ifdef __APPLE__ #include @@ -217,21 +218,77 @@ namespace Utilities static std::string getProgramPath() { - char buffer[1000]; -#ifdef WIN32 - GetModuleFileName(NULL, buffer, 1000); + std::string executablePath; + std::string dynamicModulePath; + +#ifdef WIN32 + char exeBuffer[MAX_PATH] = { 0 }; + if (GetModuleFileNameA(NULL, exeBuffer, MAX_PATH)) + executablePath = exeBuffer; + + HMODULE moduleHandle = nullptr; + if (GetModuleHandleExA(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, + reinterpret_cast(&getProgramPath), &moduleHandle) && moduleHandle) + { + char moduleBuffer[MAX_PATH] = { 0 }; + if (GetModuleFileNameA(moduleHandle, moduleBuffer, MAX_PATH)) + dynamicModulePath = moduleBuffer; + } #elif defined(__APPLE__) - uint32_t bufferSize = sizeof(buffer); - _NSGetExecutablePath(buffer, &bufferSize); + char exeBuffer[1024] = { 0 }; + uint32_t bufferSize = sizeof(exeBuffer); + if (_NSGetExecutablePath(exeBuffer, &bufferSize) == 0) + executablePath = exeBuffer; + + Dl_info moduleInfo{}; + if (dladdr(reinterpret_cast(&getProgramPath), &moduleInfo) && moduleInfo.dli_fname) + dynamicModulePath = moduleInfo.dli_fname; #else - char szTmp[32]; - sprintf(szTmp, "/proc/%d/exe", getpid()); - int bytes = std::min((int)readlink(szTmp, buffer, 1000), 999); - buffer[bytes] = '\0'; + char exeBuffer[1024] = { 0 }; + char procPath[32]; + sprintf(procPath, "/proc/%d/exe", getpid()); + int exePathLength = std::min((int)readlink(procPath, exeBuffer, sizeof(exeBuffer) - 1), (int)(sizeof(exeBuffer) - 1)); + if (exePathLength > 0) + { + exeBuffer[exePathLength] = '\0'; + executablePath = exeBuffer; + } + + Dl_info moduleInfo{}; + if (dladdr(reinterpret_cast(&getProgramPath), &moduleInfo) && moduleInfo.dli_fname) + dynamicModulePath = moduleInfo.dli_fname; #endif - std::string::size_type pos = std::string(buffer).find_last_of("\\/"); - return std::string(buffer).substr(0, pos); + auto getParentDirectory = [](const std::string &path) -> std::string + { + size_t lastSeparator = path.find_last_of("\\/"); + return (lastSeparator != std::string::npos) ? path.substr(0, lastSeparator) : ""; + }; + + // If running as a dynamic module / Python extension (module differs from executable) + if (!dynamicModulePath.empty() && dynamicModulePath != executablePath) + { + std::string searchDirectory = getParentDirectory(dynamicModulePath); + + // Walk up the directory tree (up to 6 levels) to locate resources/data + for (int level = 0; level < 6 && !searchDirectory.empty(); ++level) + { + if (isDirectory(searchDirectory + "/resources") || isDirectory(searchDirectory + "/data")) + return searchDirectory; + + if (isDirectory(searchDirectory + "/bin/resources") || isDirectory(searchDirectory + "/bin/data")) + return searchDirectory + "/bin"; + + searchDirectory = getParentDirectory(searchDirectory); + } + + return getParentDirectory(dynamicModulePath); + } + else + { + // Standalone executable fallback + return getParentDirectory(executablePath); + } } static bool copyFile(const std::string &source, const std::string &dest) diff --git a/setup.py b/setup.py index 1e46c4db..1eaa32bf 100644 --- a/setup.py +++ b/setup.py @@ -1,6 +1,7 @@ import os import re import sys +import sysconfig import platform import subprocess import multiprocessing as mp @@ -50,11 +51,21 @@ def run(self): def build_extension(self, ext): extdir = os.path.abspath(os.path.dirname(self.get_ext_fullpath(ext.name))) bin_dir_windows = os.path.join(os.path.abspath(self.build_temp), "bin") - cmake_args = ['-DCMAKE_LIBRARY_OUTPUT_DIRECTORY=' + extdir, - # '-DPYBIND11_PYTHON_VERSION=' + f"{sys.version_info.major}.{sys.version_info.minor}" - '-DPYBIND11_FINDPYTHON=On', - '-DPython_EXECUTABLE=' + sys.executable, - ] + cmake_args = [ + "-DCMAKE_LIBRARY_OUTPUT_DIRECTORY=" + extdir, + "-DPYBIND11_FINDPYTHON=On", + "-DPython_EXECUTABLE=" + sys.executable, + "-DPython_INCLUDE_DIR=" + sysconfig.get_path("include"), + ] + if platform.system() == "Windows": + cmake_args.append( + "-DPython_LIBRARY=" + + os.path.join( + sys.base_prefix, + "libs", + f"python{sys.version_info.major}{sys.version_info.minor}.lib", + ) + ) print(f"Using cmake args {cmake_args}") print(f"Python version: {sys.version_info.major}.{sys.version_info.minor}.{sys.version_info.micro}")