diff --git a/3rdparty/ext_pyqt/CMakeLists.txt b/3rdparty/ext_pyqt/CMakeLists.txt --- a/3rdparty/ext_pyqt/CMakeLists.txt +++ b/3rdparty/ext_pyqt/CMakeLists.txt @@ -5,7 +5,6 @@ message("WARNING: using system python3!") SET(PYTHON_EXECUTABLE_PATH python3) endif() - ExternalProject_Add( ext_pyqt DOWNLOAD_DIR ${EXTERNALS_DOWNLOAD_DIR} URL http://files.kde.org/krita/build/dependencies/PyQt5_gpl-5.6.tar.gz @@ -30,15 +29,15 @@ --spec win32-g++ --verbose --sipdir ${PREFIX_ext_pyqt}/share/sip - --destdir ${PREFIX_ext_pyqt}/share/krita/pykrita - --stubsdir ${PREFIX_ext_pyqt}/share/krita/pykrita/PyQt5 + --destdir ${PREFIX_ext_pyqt}/lib/krita-python-libs + --stubsdir ${PREFIX_ext_pyqt}/lib/krita-python-libs/PyQt5 --no-qml-plugin --no-python-dbus --no-qsci-api --no-tools --disable QtSql --disable QtTest --disable QtWinExtras ) ExternalProject_Add( ext_pyqt DOWNLOAD_DIR ${EXTERNALS_DOWNLOAD_DIR} - URL https://sourceforge.net/projects/pyqt/files/PyQt5/PyQt-5.9/PyQt5_gpl-5.9.zip - URL_MD5 d978884753df265896eda436d8f4e07b + URL https://sourceforge.net/projects/pyqt/files/PyQt5/PyQt-5.9.2/PyQt5_gpl-5.9.2.zip + URL_MD5 67eacf2f486f77136c9758e03fe6889e PATCH_COMMAND ${PATCH_COMMAND} -p1 -i ${CMAKE_CURRENT_SOURCE_DIR}/pyqt-configure-fix.patch CONFIGURE_COMMAND ${PYTHON_EXECUTABLE} /configure.py ${_PYQT_conf} diff --git a/3rdparty/ext_pyqt/pyqt-configure-fix.patch b/3rdparty/ext_pyqt/pyqt-configure-fix.patch --- a/3rdparty/ext_pyqt/pyqt-configure-fix.patch +++ b/3rdparty/ext_pyqt/pyqt-configure-fix.patch @@ -1,15 +1,20 @@ ---- a/configure.py 2017-07-03 18:25:14.000000000 +0800 -+++ b/configure.py 2017-07-15 01:19:44.622435500 +0800 -@@ -2168,10 +2168,8 @@ - if source is None: - for disabled in run_test_program(mname, test, verbose): - if disabled: -- inform("Disabled %s features: %s" % (mname, -- ', '.join(disabled))) -- -- target_config.pyqt_disabled_features.extend(disabled) -+ inform("Disabled %s features: %s" % (mname, disabled)) -+ target_config.pyqt_disabled_features.append(disabled) - - # Include the module in the build. - target_config.pyqt_modules.append(mname) +--- a/configure.py ++++ b/configure.py +@@ -965,7 +965,7 @@ class TargetConfiguration: + # it where it is. + if not self.static: + pro_lines.extend(['win32 {', +- ' LIBS += ' + self.get_pylib_link_arguments(name=False), ++ ' LIBS += ' + self.get_pylib_link_arguments(), + '}']) + + @staticmethod +@@ -1977,7 +1977,7 @@ def run_make(target_config, verbose, exe, makefile_name): + + remove_file(platform_exe) + +- args = [make, '-f', makefile_name] ++ args = [make, '-f', makefile_name, 'CXXFLAGS=-D_hypot=hypot'] + + if makefile_target != '': + args.append(makefile_target) diff --git a/3rdparty/ext_sip/CMakeLists.txt b/3rdparty/ext_sip/CMakeLists.txt --- a/3rdparty/ext_sip/CMakeLists.txt +++ b/3rdparty/ext_sip/CMakeLists.txt @@ -23,15 +23,15 @@ list(APPEND _SIP_conf --platform win32-g++ -b ${PREFIX_ext_sip}/bin - -d ${PREFIX_ext_sip}/share/krita/pykrita + -d ${PREFIX_ext_sip}/lib/krita-python-libs -e ${PREFIX_ext_sip}/include --sipdir ${PREFIX_ext_sip}/share/sip --target-py-version 3.6 ) ExternalProject_Add( ext_sip DOWNLOAD_DIR ${EXTERNALS_DOWNLOAD_DIR} - URL https://sourceforge.net/projects/pyqt/files/sip/sip-4.19.3/sip-4.19.3.zip - URL_MD5 1098da9ee1915354fedf38fd6fbe22ce + URL https://sourceforge.net/projects/pyqt/files/sip/sip-4.19.6/sip-4.19.6.zip + URL_MD5 6a4080268c2aa9225965415fe8706967 CONFIGURE_COMMAND ${PYTHON_EXECUTABLE} /configure.py ${_SIP_conf} BUILD_COMMAND mingw32-make -j${SUBMAKE_JOBS} LDFLAGS=${SECURITY_SHARED_LINKER_FLAGS} diff --git a/CMakeLists.txt b/CMakeLists.txt --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -641,13 +641,6 @@ add_definitions(${QT_DEFINITIONS} ${QT_QTDBUS_DEFINITIONS}) -if(WIN32) - set(LIB_INSTALL_DIR ${LIB_INSTALL_DIR} - RUNTIME DESTINATION ${BIN_INSTALL_DIR} - LIBRARY ${INSTALL_TARGETS_DEFAULT_ARGS} - ARCHIVE ${INSTALL_TARGETS_DEFAULT_ARGS} ) -endif() - ## ## Test endianness ## diff --git a/cmake/modules/FindPyQt5.cmake b/cmake/modules/FindPyQt5.cmake --- a/cmake/modules/FindPyQt5.cmake +++ b/cmake/modules/FindPyQt5.cmake @@ -31,7 +31,7 @@ if (WIN32) - EXECUTE_PROCESS(COMMAND ${CMAKE_COMMAND} -E env "PYTHONPATH=${CMAKE_PREFIX_PATH}/share/krita/pykrita" ${PYTHON_EXECUTABLE} ${_find_pyqt5_py} OUTPUT_VARIABLE pyqt5_config) + EXECUTE_PROCESS(COMMAND ${CMAKE_COMMAND} -E env "PYTHONPATH=${CMAKE_PREFIX_PATH}/lib/krita-python-libs" ${PYTHON_EXECUTABLE} ${_find_pyqt5_py} OUTPUT_VARIABLE pyqt5_config) else (WIN32) EXECUTE_PROCESS(COMMAND ${PYTHON_EXECUTABLE} ${_find_pyqt5_py} OUTPUT_VARIABLE pyqt5_config) endif (WIN32) diff --git a/cmake/modules/FindPythonLibrary.cmake b/cmake/modules/FindPythonLibrary.cmake --- a/cmake/modules/FindPythonLibrary.cmake +++ b/cmake/modules/FindPythonLibrary.cmake @@ -12,11 +12,6 @@ # PYTHON_LONG_VERSION - The version of the Python interpreter found as a human # readable string. # -# PYTHON_SITE_PACKAGES_INSTALL_DIR - this cache variable can be used for installing -# own python modules. You may want to adjust this to be the -# same as ${PYTHON_SITE_PACKAGES_DIR}, but then admin -# privileges may be required for installation. -# # PYTHON_SITE_PACKAGES_DIR - Location of the Python site-packages directory. # # PYTHON_INCLUDE_PATH - Directory holding the python.h include file. @@ -34,8 +29,6 @@ if (PYTHONINTERP_FOUND) - option(INSTALL_PYTHON_FILES_IN_PYTHON_PREFIX "Install the Python files in the Python packages dir" FALSE) - # Set the Python libraries to what we actually found for interpreters set(Python_ADDITIONAL_VERSIONS "${PYTHON_VERSION_MAJOR}.${PYTHON_VERSION_MINOR}") # These are kept for compatibility @@ -55,18 +48,6 @@ ) message(STATUS "Python system site-packages directory: ${PYTHON_SITE_PACKAGES_DIR}") - if(INSTALL_PYTHON_FILES_IN_PYTHON_PREFIX) - set(PYTHON_SITE_PACKAGES_INSTALL_DIR ${PYTHON_SITE_PACKAGES_DIR}) - else() - execute_process(COMMAND ${PYTHON_EXECUTABLE} -c "from distutils.sysconfig import get_python_lib; print(get_python_lib(True, prefix='${CMAKE_INSTALL_PREFIX}'))" - OUTPUT_VARIABLE PYTHON_SITE_PACKAGES_INSTALL_DIR - OUTPUT_STRIP_TRAILING_WHITESPACE - ) - endif() - - if(NOT PYTHON_SITE_PACKAGES_INSTALL_DIR STREQUAL PYTHON_SITE_PACKAGES_DIR) - message(STATUS "The Python files will be installed to ${PYTHON_SITE_PACKAGES_INSTALL_DIR}. Make sure to add them to the Python search path (e.g. by setting PYTHONPATH)") - endif() endif(PYTHONINTERP_FOUND) diff --git a/cmake/modules/FindSIP.cmake b/cmake/modules/FindSIP.cmake --- a/cmake/modules/FindSIP.cmake +++ b/cmake/modules/FindSIP.cmake @@ -34,7 +34,7 @@ FIND_FILE(_find_sip_py FindSIP.py PATHS ${CMAKE_MODULE_PATH}) if (WIN32) - EXECUTE_PROCESS(COMMAND ${CMAKE_COMMAND} -E env "PYTHONPATH=${CMAKE_PREFIX_PATH}/share/krita/pykrita" ${PYTHON_EXECUTABLE} ${_find_sip_py} OUTPUT_VARIABLE sip_config) + EXECUTE_PROCESS(COMMAND ${CMAKE_COMMAND} -E env "PYTHONPATH=${CMAKE_PREFIX_PATH}/lib/krita-python-libs" ${PYTHON_EXECUTABLE} ${_find_sip_py} OUTPUT_VARIABLE sip_config) else (WIN32) EXECUTE_PROCESS(COMMAND ${PYTHON_EXECUTABLE} ${_find_sip_py} OUTPUT_VARIABLE sip_config) endif (WIN32) diff --git a/cmake/modules/SIPMacros.cmake b/cmake/modules/SIPMacros.cmake --- a/cmake/modules/SIPMacros.cmake +++ b/cmake/modules/SIPMacros.cmake @@ -100,11 +100,11 @@ DEPENDS ${_abs_module_sip} ${SIP_EXTRA_FILES_DEPEND} ) # not sure if type MODULE could be uses anywhere, limit to cygwin for now - IF (CYGWIN) + IF (WIN32 OR CYGWIN) ADD_LIBRARY(${_logical_name} MODULE ${_sip_output_files} ) - ELSE (CYGWIN) + ELSE (WIN32 OR CYGWIN) ADD_LIBRARY(${_logical_name} SHARED ${_sip_output_files} ) - ENDIF (CYGWIN) + ENDIF (WIN32 OR CYGWIN) TARGET_LINK_LIBRARIES(${_logical_name} ${PYTHON_LIBRARY}) TARGET_LINK_LIBRARIES(${_logical_name} ${EXTRA_LINK_LIBRARIES}) SET_TARGET_PROPERTIES(${_logical_name} PROPERTIES PREFIX "" OUTPUT_NAME ${_child_module_name}) diff --git a/packaging/windows/package-complete.cmd b/packaging/windows/package-complete.cmd --- a/packaging/windows/package-complete.cmd +++ b/packaging/windows/package-complete.cmd @@ -593,6 +593,7 @@ endlocal :: Krita plugins xcopy /Y %KRITA_INSTALL_DIR%\lib\kritaplugins\*.dll %pkg_root%\lib\kritaplugins\ +xcopy /Y /S /I %KRITA_INSTALL_DIR%\lib\krita-python-libs %pkg_root%\lib\krita-python-libs :: Share xcopy /Y /S /I %KRITA_INSTALL_DIR%\share\color %pkg_root%\share\color @@ -680,6 +681,11 @@ set relpath=!relpath:~%pkg_root_len_plus_one%! call :split-debug "%%F" !relpath! ) +for /r "%pkg_root%\lib\krita-python-libs\" %%F in (*.pyd) do ( + set relpath=%%F + set relpath=!relpath:~%pkg_root_len_plus_one%! + call :split-debug "%%F" !relpath! +) endlocal if not "%ARG_PRE_ZIP_HOOK%" == "" ( diff --git a/plugins/extensions/pykrita/plugin/CMakeLists.txt b/plugins/extensions/pykrita/plugin/CMakeLists.txt --- a/plugins/extensions/pykrita/plugin/CMakeLists.txt +++ b/plugins/extensions/pykrita/plugin/CMakeLists.txt @@ -35,7 +35,7 @@ # Install "built-in" api install( DIRECTORY krita - DESTINATION ${DATA_INSTALL_DIR}/krita/pykrita + DESTINATION ${LIB_INSTALL_DIR}/krita-python-libs FILES_MATCHING PATTERN "*.py" ) diff --git a/plugins/extensions/pykrita/plugin/config.h.cmake b/plugins/extensions/pykrita/plugin/config.h.cmake --- a/plugins/extensions/pykrita/plugin/config.h.cmake +++ b/plugins/extensions/pykrita/plugin/config.h.cmake @@ -18,7 +18,6 @@ // Boston, MA 02110-1301, USA. #define PYKRITA_PYTHON_LIBRARY "${PYTHON_LIBRARY}" -#define PYKRITA_PYTHON_SITE_PACKAGES_INSTALL_DIR "${PYTHON_SITE_PACKAGES_INSTALL_DIR}" /// Name of the file where per-plugin configuration is stored #define CONFIG_FILE "kritapykritarc" diff --git a/plugins/extensions/pykrita/plugin/utilities.h b/plugins/extensions/pykrita/plugin/utilities.h --- a/plugins/extensions/pykrita/plugin/utilities.h +++ b/plugins/extensions/pykrita/plugin/utilities.h @@ -98,7 +98,7 @@ * Set the Python paths by calling Py_SetPath. This should be called before * initialization to ensure the proper libraries get loaded. */ - static bool setPath(const QStringList& paths); + static bool setPath(const QStringList& scriptPaths); /** * Make sure the Python interpreter is initialized. Ideally should be only diff --git a/plugins/extensions/pykrita/plugin/utilities.cpp b/plugins/extensions/pykrita/plugin/utilities.cpp --- a/plugins/extensions/pykrita/plugin/utilities.cpp +++ b/plugins/extensions/pykrita/plugin/utilities.cpp @@ -143,6 +143,7 @@ QLibrary* s_pythonLibrary = 0; #endif PyThreadState* s_pythonThreadState = 0; +bool isPythonPathSet = false; } // anonymous namespace const char* Python::PYKRITA_ENGINE = "pykrita"; @@ -278,47 +279,80 @@ return true; } -bool Python::setPath(const QStringList& paths) -{ - if (Py_IsInitialized()) { - warnScript << "Setting paths when Python interpreter is already initialized"; +namespace +{ + +QString findKritaPythonLibsPath() +{ + QDir rootDir(KoResourcePaths::getApplicationRoot()); + //Q_FOREACH (const QFileInfo &entry, rootDir.entryInfoList(QStringList() << "lib*", QDir::Dirs)) { + Q_FOREACH (const QFileInfo &entry, rootDir.entryInfoList(QStringList() << "lib*", QDir::Dirs | QDir::NoDotAndDotDot)) { + QDir libDir(entry.absoluteFilePath()); + if (libDir.cd("krita-python-libs")) { + return libDir.absolutePath(); + } else { + // Handle cases like Linux where libs are placed in a sub-dir + // with the ABI name + Q_FOREACH (const QFileInfo &subEntry, libDir.entryInfoList(QDir::Dirs | QDir::NoDotAndDotDot)) { + QDir subDir(subEntry.absoluteFilePath()); + if (subDir.cd("krita-python-libs")) { + return subDir.absolutePath(); + } + } + } } + return QString(); +} + +} // namespace + +bool Python::setPath(const QStringList& scriptPaths) +{ + KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(!Py_IsInitialized(), false); + KIS_SAFE_ASSERT_RECOVER_RETURN_VALUE(!isPythonPathSet, false); #ifdef Q_OS_WIN constexpr char pathSeparator = ';'; #else constexpr char pathSeparator = ':'; #endif - QString joinedPaths = paths.join(pathSeparator); - // Append the default search path - // TODO: Properly handle embedded Python + QString originalPaths; + // Start with the script paths + QStringList paths(scriptPaths); + + // Append the Krita libraries path + QString pythonLibsPath = findKritaPythonLibsPath(); + if (pythonLibsPath.isEmpty()) { + errScript << "Cannot find krita-python-libs"; + return false; + } + dbgScript << "Found krita-python-libs at" << pythonLibsPath; + paths.append(pythonLibsPath); + #ifdef Q_OS_WIN - QString currentPaths; - // Find embeddable Python - // TODO: Don't hard-code the paths + // Find embeddable Python at /python QDir pythonDir(KoResourcePaths::getApplicationRoot()); if (pythonDir.cd("python")) { - dbgScript << "Found embeddable Python at" << pythonDir.absolutePath(); - currentPaths = pythonDir.absolutePath() + pathSeparator - + pythonDir.absoluteFilePath("python36.zip"); + dbgScript << "Found bundled Python at" << pythonDir.absolutePath(); + // The default paths for Windows embeddable Python is ./python36.zip;./ + // HACK: Assuming bundled Python is version 3.6.* + // FIXME: Should we read python36._pth for the paths or use Py_GetPath? + paths.append(pythonDir.absoluteFilePath("python36.zip")); + paths.append(pythonDir.absolutePath()); } else { -# if 1 - // Use local Python??? - currentPaths = QString::fromWCharArray(Py_GetPath()); - warnScript << "Embeddable Python not found."; - warnScript << "Default paths:" << currentPaths; -# else - // Or should we fail? - errScript << "Embeddable Python not found, not setting Python paths"; + errScript << "Bundled Python not found, cannot set Python library paths"; return false; -# endif } #else - QString currentPaths = QString::fromLocal8Bit(qgetenv("PYTHONPATH")); + // TODO: Handle embedded Python or virtualenv + // If using a system Python install, respect the current PYTHONPATH + originalPaths = QString::fromLocal8Bit(qgetenv("PYTHONPATH")); #endif - if (!currentPaths.isEmpty()) { - joinedPaths = joinedPaths + pathSeparator + currentPaths; + + QString joinedPaths = paths.join(pathSeparator); + if (!originalPaths.isEmpty()) { + joinedPaths = joinedPaths + pathSeparator + originalPaths; } - dbgScript << "Setting paths:" << joinedPaths; + infoScript << "Setting python paths:" << joinedPaths; #ifdef Q_OS_WIN QVector joinedPathsWChars(joinedPaths.size() + 1, 0); joinedPaths.toWCharArray(joinedPathsWChars.data()); @@ -326,6 +360,7 @@ #else qputenv("PYTHONPATH", joinedPaths.toLocal8Bit()); #endif + isPythonPathSet = true; return true; } diff --git a/plugins/extensions/pykrita/sip/CMakeLists.txt b/plugins/extensions/pykrita/sip/CMakeLists.txt --- a/plugins/extensions/pykrita/sip/CMakeLists.txt +++ b/plugins/extensions/pykrita/sip/CMakeLists.txt @@ -18,7 +18,7 @@ set(SIP_TAGS ALL WS_X11 ${PYQT5_VERSION_TAG}) set(SIP_EXTRA_OPTIONS -g -x PyKDE_QVector) -set(PYTHON_SITE_PACKAGES_INSTALL_DIR ${DATA_INSTALL_DIR}/krita/pykrita/) +set(PYTHON_SITE_PACKAGES_INSTALL_DIR ${LIB_INSTALL_DIR}/krita-python-libs) file(GLOB PYKRITA_KRITA_sip_files ./krita/*.sip) set(SIP_EXTRA_FILES_DEPEND ${PYKRITA_KRITA_sip_files}) add_sip_python_module(PyKrita.krita ./krita/kritamod.sip kritalibkis kritaui kritaimage kritalibbrush)