diff --git a/src/pya/pya.cc b/src/pya/pya.cc index 111ad9ca0..67cdb2857 100644 --- a/src/pya/pya.cc +++ b/src/pya/pya.cc @@ -38,6 +38,7 @@ #include "tlLog.h" #include "tlStream.h" #include "tlTimer.h" +#include "tlExpression.h" #include #include @@ -47,6 +48,8 @@ #include #include #include +#include +#include namespace pya { @@ -2252,29 +2255,55 @@ PythonInterpreter::PythonInterpreter () const wchar_t *python_path = _wgetenv (L"KLAYOUT_PYTHONPATH"); if (python_path) { + Py_SetPath (python_path); + } else { - // by default take the installation path + "/lib/python" - // and inside this path take "DLLs", "Lib" and "Lib/site-packages". - // This is compatible with the installation. - QDir inst_dir (QCoreApplication::applicationDirPath ()); - inst_dir = inst_dir.filePath (tl::to_qstring ("lib")); - inst_dir = inst_dir.filePath (tl::to_qstring ("python")); + // If present, read the paths from a file in INST_PATH/.python-paths.txt. + // The content of this file is evaluated as an expression and the result + // is placed inside the Python path. + + try { - QDir inst_dir_lib = inst_dir.filePath (tl::to_qstring ("Lib")); + QString path; - QString path = inst_dir.absoluteFilePath (tl::to_qstring ("DLLs")) - + tl::to_qstring (";") - + inst_dir_lib.absolutePath () - + tl::to_qstring (";") - + inst_dir_lib.absoluteFilePath (tl::to_qstring ("site-packages")) - ; + QDir inst_dir (QCoreApplication::applicationDirPath ()); + QFileInfo fi (inst_dir.absoluteFilePath (tl::to_qstring(".python-paths.txt"))); + if (fi.exists ()) { - // note: this is a hack, but linking with toWCharArray fails since wchar_t is treated - // as a built-in type in our build - Py_SetPath ((const wchar_t *) path.utf16 ()); + tl::log << tl::to_string (QObject::tr ("Reading Python path from ")) << tl::to_string (fi.filePath ()); + QFile paths_txt (fi.filePath ()); + paths_txt.open (QIODevice::ReadOnly); + + tl::Eval eval; + eval.set_global_var ("inst_path", tl::Variant (tl::to_string (inst_dir.absolutePath ()))); + tl::Expression ex; + eval.parse (ex, paths_txt.readAll ().constData ()); + tl::Variant v = ex.execute (); + + if (v.is_list ()) { + for (tl::Variant::iterator i = v.begin (); i != v.end (); ++i) { + if (! path.isEmpty ()) { + path += tl::to_qstring (";"); + } + path += tl::to_qstring (i->to_string ()); + } + } + + } + + // note: this is a hack, but linking with toWCharArray fails since wchar_t is treated + // as a built-in type in our build + Py_SetPath ((const wchar_t *) path.utf16 ()); + + } catch (tl::Exception &ex) { + tl::error << tl::to_string (QObject::tr ("Evaluation of Python path expression failed")) << ": " << ex.msg (); + } catch (...) { + tl::error << tl::to_string (QObject::tr ("Evaluation of Python path expression failed")); + } + } # else @@ -2302,6 +2331,45 @@ PythonInterpreter::PythonInterpreter () Py_InitializeEx (0 /*don't set signals*/); + if (add_path_from_file) { + + // If present, read the paths from a file in INST_PATH/.python-paths.txt. + // The content of this file is evaluated as an expression and the result + // is placed inside the Python path. + + try { + + QDir inst_dir (QCoreApplication::applicationDirPath ()); + QFileInfo fi (inst_dir.absoluteFilePath (tl::to_qstring(".python-paths.txt"))); + if (fi.exists ()) { + + tl::log << tl::to_string (QObject::tr ("Reading Python path from ")) << tl::to_string (fi.filePath ()); + + QFile paths_txt (fi.filePath ()); + paths_txt.open (QIODevice::ReadOnly); + + tl::Eval eval; + eval.set_global_var ("inst_path", tl::Variant (tl::to_string (inst_dir.absolutePath ()))); + tl::Expression ex; + eval.parse (ex, paths_txt.readAll ().constData ()); + tl::Variant v = ex.execute (); + + if (v.is_list ()) { + for (tl::Variant::iterator i = v.begin (); i != v.end (); ++i) { + add_path (i->to_string ()); + } + } + + } + + } catch (tl::Exception &ex) { + tl::error << tl::to_string (QObject::tr ("Evaluation of Python path expression failed")) << ": " << ex.msg (); + } catch (...) { + tl::error << tl::to_string (QObject::tr ("Evaluation of Python path expression failed")); + } + + } + // Set dummy argv[] // TODO: more? char *argv[1] = { make_string (app_path) }; diff --git a/src/rba/rba.cc b/src/rba/rba.cc index 123682eeb..4744069f1 100644 --- a/src/rba/rba.cc +++ b/src/rba/rba.cc @@ -32,6 +32,8 @@ #include "tlAssert.h" #include "tlLog.h" #include "tlTimer.h" +#include "tlExpression.h" +#include "tlSystemPaths.h" #include "rba.h" #include "rbaInspector.h" @@ -50,6 +52,8 @@ #include #include #include +#include +#include #if !defined(HAVE_RUBY_VERSION_CODE) # define HAVE_RUBY_VERSION_CODE 10901 @@ -1427,6 +1431,15 @@ struct RubyConstDescriptor extern "C" void ruby_prog_init(); +static void +rba_add_path (const std::string &path) +{ + VALUE pv = rb_gv_get ("$:"); + if (pv != Qnil && TYPE (pv) == T_ARRAY) { + rb_ary_push (pv, rb_str_new (path.c_str (), path.size ())); + } +} + static void rba_init (RubyInterpreterPrivateData *d) { @@ -1778,6 +1791,45 @@ RubyInterpreter::initialize (int main_argc, char **main_argv, int (*main_func) ( ruby_init (); signal (SIGINT, org_sigint); +#if defined(_WIN32) + + // On Windows we derive additional path components from a file called ".ruby-paths.txt" + // inside the installation directory. This way, the installer can copy the deployment-time + // installation easier. + + try { + + QDir inst_dir (tl::to_qstring (tl::get_inst_path ())); + QFileInfo fi (inst_dir.absoluteFilePath (tl::to_qstring(".ruby-paths.txt"))); + if (fi.exists ()) { + + tl::log << tl::to_string (QObject::tr ("Reading Ruby path from ")) << tl::to_string (fi.filePath ()); + + QFile paths_txt (fi.filePath ()); + paths_txt.open (QIODevice::ReadOnly); + + tl::Eval eval; + eval.set_global_var ("inst_path", tl::Variant (tl::to_string (inst_dir.absolutePath ()))); + tl::Expression ex; + eval.parse (ex, paths_txt.readAll ().constData ()); + tl::Variant v = ex.execute (); + + if (v.is_list ()) { + for (tl::Variant::iterator i = v.begin (); i != v.end (); ++i) { + rba_add_path (i->to_string ()); + } + } + + } + + } catch (tl::Exception &ex) { + tl::error << tl::to_string (QObject::tr ("Evaluation of Ruby path expression failed")) << ": " << ex.msg (); + } catch (...) { + tl::error << tl::to_string (QObject::tr ("Evaluation of Ruby path expression failed")); + } + +#endif + // Remove setters for $0 and $PROGRAM_NAME (still both are linked) because // the setter does strange things with the process and the argv, specifically argv[0] above. static VALUE argv0 = Qnil; @@ -1856,10 +1908,7 @@ RubyInterpreter::remove_package_location (const std::string & /*package_path*/) void RubyInterpreter::add_path (const std::string &path) { - VALUE pv = rb_gv_get ("$:"); - if (pv != Qnil && TYPE (pv) == T_ARRAY) { - rb_ary_push (pv, rb_str_new (path.c_str (), path.size ())); - } + rba_add_path (path); } void diff --git a/src/tl/tlSystemPaths.cc b/src/tl/tlSystemPaths.cc index 75343718e..4e3b66097 100644 --- a/src/tl/tlSystemPaths.cc +++ b/src/tl/tlSystemPaths.cc @@ -25,6 +25,7 @@ #include "tlString.h" #include +#include #include #ifdef _WIN32 @@ -66,10 +67,42 @@ get_appdata_path () return tl::to_string (appdata_klayout_path); } +static std::string +get_inst_path_internal () +{ + // Note: the QCoreApplication::applicationDirPath cannot be used before + // a QApplication object is instantiated. Hence this custom implementation. +#ifdef _WIN32 + + wchar_t buffer[MAX_PATH]; + int len; + if ((len = GetModuleFileName(NULL, buffer, MAX_PATH)) > 0) { + QFileInfo fi (QString::fromUtf16 ((const ushort *) buffer, len)); + return tl::to_string (fi.absolutePath ()); + } + +#else + + QFileInfo proc_exe (tl::sprintf ("/proc/%d/exe"), getpid ()); + if (proc_exe.exists () && proc_exe.isSymLink ()) { + return QFileInfo (proc_exe.canonicalFilePath ()).absolutePath (); + } + +#endif + + // As a fallback use QCoreApplication::applicationDirPath, which however it not + // available before QCoreApplication is initialized + return tl::to_string (QCoreApplication::applicationDirPath ()); +} + std::string get_inst_path () { - return tl::to_string (QCoreApplication::applicationDirPath ()); + static std::string s_inst_path; + if (s_inst_path.empty ()) { + s_inst_path = get_inst_path_internal (); + } + return s_inst_path; } #ifdef _WIN32