From caee7cae1c1f1123d66f1992a25cc497c9d839bb Mon Sep 17 00:00:00 2001 From: Ian Rees Date: Fri, 17 Mar 2017 18:26:55 +1300 Subject: [PATCH] Get path on OSX when imported from external Python --- src/App/Application.cpp | 41 ++++++++++++---------- src/App/Application.h | 7 ++-- src/Main/MainPy.cpp | 78 +++++++++++++++++++++++++++++++++++------ 3 files changed, 96 insertions(+), 30 deletions(-) diff --git a/src/App/Application.cpp b/src/App/Application.cpp index 9e55f392c5..110ac4017b 100644 --- a/src/App/Application.cpp +++ b/src/App/Application.cpp @@ -2281,30 +2281,35 @@ std::string Application::FindHomePath(const char* sCall) std::string Application::FindHomePath(const char* call) { - uint32_t sz = 0; - char *buf; + // If Python is intialized at this point, then we're being run from + // MainPy.cpp, which hopefully rewrote argv[0] to point at the + // FreeCAD shared library. + if (!Py_IsInitialized()) { + uint32_t sz = 0; + char *buf; - _NSGetExecutablePath(NULL, &sz); //function only returns "sz" if first arg is to small to hold value - buf = new char[++sz]; + _NSGetExecutablePath(NULL, &sz); //function only returns "sz" if first arg is to small to hold value + buf = new char[++sz]; - if (_NSGetExecutablePath(buf, &sz) == 0) { - char resolved[PATH_MAX]; - char* path = realpath(buf, resolved); - delete [] buf; + if (_NSGetExecutablePath(buf, &sz) == 0) { + char resolved[PATH_MAX]; + char* path = realpath(buf, resolved); + delete [] buf; - if (path) { - std::string Call(resolved), TempHomePath; - std::string::size_type pos = Call.find_last_of(PATHSEP); - TempHomePath.assign(Call,0,pos); - pos = TempHomePath.find_last_of(PATHSEP); - TempHomePath.assign(TempHomePath,0,pos+1); - return TempHomePath; + if (path) { + std::string Call(resolved), TempHomePath; + std::string::size_type pos = Call.find_last_of(PATHSEP); + TempHomePath.assign(Call,0,pos); + pos = TempHomePath.find_last_of(PATHSEP); + TempHomePath.assign(TempHomePath,0,pos+1); + return TempHomePath; + } + } else { + delete [] buf; } - } else { - delete [] buf; } - return call; // error + return call; } #elif defined (FC_OS_WIN32) diff --git a/src/App/Application.h b/src/App/Application.h index 94a9ba6fd1..00bc677b27 100644 --- a/src/App/Application.h +++ b/src/App/Application.h @@ -336,9 +336,12 @@ private: static void ParseOptions(int argc, char ** argv); /// checks if the environment is allreight //static void CheckEnv(void); - // search for the home path + /// Search for the FreeCAD home path based on argv[0] + /*! + * There are multiple implementations of this method per-OS + */ static std::string FindHomePath(const char* sCall); - /// print the help massage + /// Print the help message static void PrintInitHelp(void); /// figure out some things static void ExtractUserPath(); diff --git a/src/Main/MainPy.cpp b/src/Main/MainPy.cpp index 23483444ee..ccfc6b8dde 100644 --- a/src/Main/MainPy.cpp +++ b/src/Main/MainPy.cpp @@ -128,21 +128,79 @@ extern "C" argv[0][PATH_MAX-1] = '\0'; // ensure null termination // this is a workaround to avoid a crash in libuuid.so #elif defined(FC_OS_MACOSX) - uint32_t sz = 0; - char *buf; - _NSGetExecutablePath(NULL, &sz); - buf = (char*) malloc(++sz); - int err=_NSGetExecutablePath(buf, &sz); - if (err != 0) { + // The MacOS approach uses the Python sys.path list to find the path + // to FreeCAD.so - this should be OS-agnostic, except these two + // strings, and the call to access(). + const static char libName[] = "/FreeCAD.so"; + const static char upDir[] = "/../"; + + char *buf = NULL; + + PyObject *pySysPath = PySys_GetObject("path"); + if ( PyList_Check(pySysPath) ) { + int i; + // pySysPath should be a *PyList of strings - iterate through it + // backwards since the FreeCAD path was likely appended just before + // we were imported. + for (i = PyList_Size(pySysPath) - 1; i >= 0 ; --i) { + char *basePath; + PyObject *pyPath = PyList_GetItem(pySysPath, i); + long sz = 0; + +#if PY_MAJOR_VERSION >= 3 + if ( PyUnicode_Check(pyPath) ) { + // Python 3 string + basePath = PyUnicode_AsUTF8AndSize(pyPath, &sz); + + } +#else + if ( PyString_Check(pyPath) ) { + // Python 2 string type + PyString_AsStringAndSize(pyPath, &basePath, &sz); + + } else if ( PyUnicode_Check(pyPath) ) { + // Python 2 unicode type - explicitly use UTF-8 codec + PyObject *fromUnicode = PyUnicode_AsUTF8String(pyPath); + PyString_AsStringAndSize(fromUnicode, &basePath, &sz); + Py_XDECREF(fromUnicode); + } +#endif // #if/else PY_MAJOR_VERSION >= 3 + else { + continue; + } + + if (sz + sizeof(libName) > PATH_MAX) { + continue; + } + + // buf gets assigned to argv[0], which is free'd at the end + buf = (char *)malloc(sz + sizeof(libName)); + if (buf == NULL) { + break; + } + + strcpy(buf, basePath); + + // append libName to buf + strcat(buf, libName); + if (access(buf, R_OK | X_OK) == 0) { + + // The FreeCAD "home" path is one level up from + // libName, so replace libName with upDir. + strcpy(buf + sz, upDir); + buf[sz + sizeof(upDir)] = '\0'; + break; + } + } // end for (i = PyList_Size(pySysPath) - 1; i >= 0 ; --i) { + } // end if ( PyList_Check(pySysPath) ) { + + if (buf == NULL) { PyErr_SetString(PyExc_ImportError, "Cannot get path of the FreeCAD module!"); return; } - argv[0] = (char*)malloc(PATH_MAX); - strncpy(argv[0], buf, PATH_MAX); - argv[0][PATH_MAX-1] = '\0'; // ensure null termination - free(buf); + argv[0] = buf; #else # error "Implement: Retrieve the path of the module for your platform." #endif