mirror of https://github.com/KLayout/klayout.git
Windows and Ruby/Python for deploymenu
This commit deals with the deployment issue on Windows where there is no global Ruby/Python installation and the installer needs to package all required files. The solution is to read the Ruby/Python path from a file that is evaluated upon startup. The installer will install these files together with the executable for Windows. This feature is only enabled on Windows. A specific issue occured: since the location of the file needs to be determined, the path of the executable needs to be known. The Ruby initialization requires the path to be set very early, before QCoreApplication is instantiated. But Qt complains in QCoreApplication::applicationDirPath so that this approach cannot be used for this purpose.
This commit is contained in:
parent
a8ac6aafe7
commit
f8b3c92191
100
src/pya/pya.cc
100
src/pya/pya.cc
|
|
@ -38,6 +38,7 @@
|
|||
#include "tlLog.h"
|
||||
#include "tlStream.h"
|
||||
#include "tlTimer.h"
|
||||
#include "tlExpression.h"
|
||||
|
||||
#include <cctype>
|
||||
#include <cstdio>
|
||||
|
|
@ -47,6 +48,8 @@
|
|||
#include <QCoreApplication>
|
||||
#include <QDir>
|
||||
#include <QVector>
|
||||
#include <QFile>
|
||||
#include <QFileInfo>
|
||||
|
||||
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) };
|
||||
|
|
|
|||
|
|
@ -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 <QString>
|
||||
#include <QByteArray>
|
||||
#include <QDir>
|
||||
#include <QFile>
|
||||
#include <QFileInfo>
|
||||
|
||||
#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
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@
|
|||
#include "tlString.h"
|
||||
|
||||
#include <QDir>
|
||||
#include <QFileInfo>
|
||||
#include <QCoreApplication>
|
||||
|
||||
#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
|
||||
|
|
|
|||
Loading…
Reference in New Issue