Make runner a static lib. Update translation paths. Clean CMake stuff.

This commit is contained in:
Mikaël Capelle
2022-05-06 17:40:11 +02:00
parent 604c50d3b3
commit e6c5923bb1
19 changed files with 380 additions and 396 deletions
+15 -9
View File
@@ -6,22 +6,28 @@ else()
include(${CMAKE_CURRENT_LIST_DIR}/../cmake_common/mo2.cmake)
endif()
project(plugin_python CXX)
set(PYTHON_BUILD_PATH ${PYTHON_ROOT}/PCBuild/amd64)
set(PYVERSION 310)
set(PYBIND11_FINDPYTHON true)
set(PYTHON_EXECUTABLE ${PYTHON_BUILD_PATH}/python.exe)
set(PYTHON_INCLUDE_DIRS ${PYTHON_ROOT}/Include)
set(PYTHON_LIBRARIES ${MO2_INSTALL_LIBS_PATH}/python${PYVERSION}.lib)
# find Python - lots of "Hints" since we have a weird setup
set(Python_USE_STATIC_LIBS False)
set(Python_INCLUDE_DIR ${PYTHON_ROOT}/Include)
set(Python_EXECUTABLE ${PYTHON_BUILD_PATH}/python.exe)
file(GLOB Python_LIBRARY ${PYTHON_BUILD_PATH}/python[0-9]+.lib)
find_package(Python COMPONENTS Interpreter Development REQUIRED)
# useful for naming DLL, zip, etc. (3.10 -> 310)
set(Python_VERSION_SHORT ${Python_VERSION_MAJOR}${Python_VERSION_MINOR})
# pybind11
add_subdirectory(${MO2_BUILD_PATH}/pybind11 ${CMAKE_CURRENT_BINARY_DIR}/pybind11)
project(plugin_python)
# projects
add_subdirectory(src)
set_property(DIRECTORY ${PROJECT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT proxy)
set_property(DIRECTORY ${PROJECT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT plugin_python)
# tests (if requested)
set(PLUGIN_PYTHON_TESTS ${PLUGIN_PYTHON_TESTS} CACHE BOOL "build tests for plugin_python")
if (PLUGIN_PYTHON_TESTS)
enable_testing()
-3
View File
@@ -6,6 +6,3 @@ add_subdirectory(pybind11-utils)
add_subdirectory(mobase)
add_subdirectory(runner)
add_subdirectory(proxy)
# force plugin_python to build mobase
add_dependencies(plugin_python mobase)
-3
View File
@@ -10,6 +10,3 @@ mo2_configure_library(mobase
PRIVATE_DEPENDS uibase Qt::Core
)
target_link_libraries(mobase PRIVATE pybind11::qt pybind11::utils)
target_include_directories(mobase PRIVATE ${PYTHON_ROOT}/Include)
install(TARGETS mobase DESTINATION bin/plugins/plugin_python/libs)
@@ -4,70 +4,70 @@
<context>
<name>ProxyPython</name>
<message>
<location filename="proxypython.cpp" line="121"/>
<location filename="proxy/proxypython.cpp" line="97"/>
<source>Python Initialization failed</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="proxypython.cpp" line="122"/>
<location filename="proxy/proxypython.cpp" line="98"/>
<source>On a previous start the Python Plugin failed to initialize.
Do you want to try initializing python again (at the risk of another crash)?
Suggestion: Select &quot;no&quot;, and click the warning sign for further help.Afterwards you have to re-enable the python plugin.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="proxypython.cpp" line="173"/>
<location filename="proxy/proxypython.cpp" line="149"/>
<source>Python Proxy</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="proxypython.cpp" line="183"/>
<location filename="proxy/proxypython.cpp" line="159"/>
<source>Proxy Plugin to allow plugins written in python to be loaded</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="proxypython.cpp" line="255"/>
<location filename="proxy/proxypython.cpp" line="231"/>
<source>ModOrganizer path contains a semicolon</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="proxypython.cpp" line="257"/>
<location filename="proxy/proxypython.cpp" line="233"/>
<source>Python DLL not found</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="proxypython.cpp" line="259"/>
<location filename="proxy/proxypython.cpp" line="235"/>
<source>Invalid Python DLL</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="proxypython.cpp" line="261"/>
<location filename="proxy/proxypython.cpp" line="237"/>
<source>Initializing Python failed</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="proxypython.cpp" line="263"/>
<location filename="proxypython.cpp" line="293"/>
<location filename="proxy/proxypython.cpp" line="239"/>
<location filename="proxy/proxypython.cpp" line="269"/>
<source>invalid problem key %1</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="proxypython.cpp" line="271"/>
<location filename="proxy/proxypython.cpp" line="247"/>
<source>The path to Mod Organizer (%1) contains a semicolon. &lt;br&gt;While this is legal on NTFS drives, many softwares do not handle it correctly.&lt;br&gt;Unfortunately MO depends on libraries that seem to fall into that group.&lt;br&gt;As a result the python plugin cannot be loaded, and the only solution we canoffer is to remove the semicolon or move MO to a path without a semicolon.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="proxypython.cpp" line="282"/>
<location filename="proxy/proxypython.cpp" line="258"/>
<source>The Python plugin DLL was not found, maybe your antivirus deleted it. Re-installing MO2 might fix the problem.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="proxypython.cpp" line="285"/>
<location filename="proxy/proxypython.cpp" line="261"/>
<source>The Python plugin DLL is invalid, maybe your antivirus is blocking it. Re-installing MO2 and adding exclusions for it to your AV might fix the problem.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="proxypython.cpp" line="290"/>
<location filename="proxy/proxypython.cpp" line="266"/>
<source>The initialization of the Python plugin DLL failed, unfortunately without any details.</source>
<translation type="unfinished"></translation>
</message>
@@ -75,7 +75,7 @@ Do you want to try initializing python again (at the risk of another crash)?
<context>
<name>QObject</name>
<message>
<location filename="../runner/error.h" line="75"/>
<location filename="runner/error.h" line="75"/>
<source>An unknown exception was thrown in python code.</source>
<translation type="unfinished"></translation>
</message>
+41 -4
View File
@@ -1,12 +1,49 @@
cmake_minimum_required(VERSION 3.16)
add_library(plugin_python SHARED)
mo2_configure_plugin(plugin_python
set(PLUGIN_NAME "plugin_python")
add_library(proxy SHARED)
mo2_configure_plugin(proxy
WARNINGS 4
EXTERNAL_WARNINGS 4
TRANSLATIONS OFF
EXTRA_TRANSLATIONS
${CMAKE_CURRENT_SOURCE_DIR}/../runner
${CMAKE_CURRENT_SOURCE_DIR}/../mobase
${CMAKE_CURRENT_SOURCE_DIR}/../pybind11-qt)
target_link_libraries(plugin_python PRIVATE pythonrunner)
mo2_install_target(plugin_python FOLDER)
target_link_libraries(proxy PRIVATE runner)
set_target_properties(proxy PROPERTIES OUTPUT_NAME ${PLUGIN_NAME})
mo2_install_target(proxy FOLDER)
# translations (custom location)
mo2_add_translations(proxy
TS_FILE ${CMAKE_CURRENT_SOURCE_DIR}/../${PLUGIN_NAME}_en.ts
SOURCES
${CMAKE_CURRENT_SOURCE_DIR}
${CMAKE_CURRENT_SOURCE_DIR}/../runner
${CMAKE_CURRENT_SOURCE_DIR}/../mobase
${CMAKE_CURRENT_SOURCE_DIR}/../pybind11-qt)
# install DLLs files needed
set(DLL_DIRS ${MO2_INSTALL_PATH}/bin/plugins/${PLUGIN_NAME}/dlls)
file(GLOB dlls_to_install
${PYTHON_BUILD_PATH}/libffi*.dll
${PYTHON_BUILD_PATH}/python${Python_VERSION_SHORT}.dll)
install(FILES ${dlls_to_install} DESTINATION ${DLL_DIRS})
# install Python files
set(PYLIB_DIR ${MO2_INSTALL_PATH}/bin/plugins/${PLUGIN_NAME}/libs)
file(GLOB libs_to_install ${PYTHON_BUILD_PATH}/pythoncore/*.pyd)
install(FILES ${libs_to_install} DESTINATION ${PYLIB_DIR})
install(FILES ${PYTHON_BUILD_PATH}/pythoncore/python${Python_VERSION_SHORT}.zip
DESTINATION ${PYLIB_DIR} RENAME pythoncore.zip)
# install mobase
install(TARGETS mobase DESTINATION ${PYLIB_DIR})
# install PyQt6
file(GLOB PYQT_DIR ${MO2_BUILD_PATH}/PyQt${QT_MAJOR_VERSION}*)
set(PYQT_LIB_DIR ${PYQT_DIR}/Lib/site-packages/PyQt${QT_MAJOR_VERSION})
set(PYQT_TARGET_DIR ${PYLIB_DIR}/PyQt${QT_MAJOR_VERSION})
file(GLOB pyqt_files ${PYQT_LIB_DIR}/*.py ${PYQT_LIB_DIR}/*.pyd ${PYQT_LIB_DIR}/*.pyi)
install(FILES ${pyqt_files} DESTINATION ${PYQT_TARGET_DIR})
+5 -29
View File
@@ -20,13 +20,15 @@ along with python proxy plugin. If not, see <http://www.gnu.org/licenses/>.
#include <filesystem>
#include <Windows.h>
#include <QCoreApplication>
#include <QDirIterator>
#include <QMessageBox>
#include <QWidget>
#include <QtPlugin>
#include "log.h"
#include <log.h>
#include <utility.h>
#include <versioninfo.h>
@@ -52,15 +54,12 @@ fs::path getPluginFolder()
}
ProxyPython::ProxyPython()
: m_MOInfo{nullptr}, m_RunnerLib{nullptr}, m_Runner{nullptr},
m_LoadFailure(FailureType::NONE)
: m_MOInfo{nullptr}, m_Runner{nullptr}, m_LoadFailure(FailureType::NONE)
{
}
bool ProxyPython::init(IOrganizer* moInfo)
{
using CreatePythonRunner_func = IPythonRunner* (*)();
m_MOInfo = moInfo;
if (m_MOInfo && !m_MOInfo->isPluginEnabled(this)) {
@@ -92,29 +91,6 @@ bool ProxyPython::init(IOrganizer* moInfo)
return false;
}
const auto runnerPath = pluginFolder / "pythonrunner.dll";
m_RunnerLib = ::LoadLibraryW(runnerPath.c_str());
if (!m_RunnerLib) {
DWORD error = ::GetLastError();
log::error("failed to load python runner ({}): {}", error,
qUtf8Printable(windowsErrorString(error)));
if (error == ERROR_MOD_NOT_FOUND) {
m_LoadFailure = FailureType::DLL_NOT_FOUND;
}
else {
m_LoadFailure = FailureType::INVALID_DLL;
}
return true;
}
const CreatePythonRunner_func createPythonRunner =
(CreatePythonRunner_func)::GetProcAddress(m_RunnerLib, "CreatePythonRunner");
if (!createPythonRunner) {
m_LoadFailure = FailureType::INVALID_DLL;
return true;
}
if (m_MOInfo && m_MOInfo->persistent(name(), "tryInit", false).toBool()) {
m_LoadFailure = FailureType::INITIALIZATION;
if (QMessageBox::question(
@@ -139,7 +115,7 @@ bool ProxyPython::init(IOrganizer* moInfo)
m_MOInfo->setPersistent(name(), "tryInit", true);
}
m_Runner = std::unique_ptr<IPythonRunner>{createPythonRunner()};
m_Runner = mo2::python::createPythonRunner();
if (m_Runner) {
const auto libpath = pluginFolder / "libs";
+1 -6
View File
@@ -23,8 +23,6 @@ along with python proxy plugin. If not, see <http://www.gnu.org/licenses/>.
#include <map>
#include <memory>
#include <Windows.h>
#include <iplugindiagnose.h>
#include <ipluginproxy.h>
@@ -35,9 +33,7 @@ class ProxyPython : public QObject,
public MOBase::IPluginDiagnose {
Q_OBJECT
Q_INTERFACES(MOBase::IPlugin MOBase::IPluginProxy MOBase::IPluginDiagnose)
#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
Q_PLUGIN_METADATA(IID "org.mo2.ProxyPython")
#endif
public:
ProxyPython();
@@ -63,8 +59,7 @@ public: // IPluginDiagnose
private:
MOBase::IOrganizer* m_MOInfo;
HMODULE m_RunnerLib;
std::unique_ptr<IPythonRunner> m_Runner;
std::unique_ptr<mo2::python::IPythonRunner> m_Runner;
enum class FailureType : unsigned int {
NONE = 0,
+1 -2
View File
@@ -10,8 +10,7 @@ mo2_configure_library(pybind11-qt
PRIVATE_DEPENDS Qt::Core Qt::Widgets
)
target_link_libraries(pybind11-qt PUBLIC pybind11::pybind11)
target_include_directories(pybind11-qt
PUBLIC ${PYTHON_ROOT}/Include ${CMAKE_CURRENT_SOURCE_DIR}/include)
target_include_directories(pybind11-qt PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include)
# this is kind of broken but it only works with this...
target_compile_definitions(pybind11-qt PUBLIC QT_NO_KEYWORDS)
+1 -2
View File
@@ -2,7 +2,6 @@ cmake_minimum_required(VERSION 3.16)
add_library(pybind11-utils INTERFACE)
target_link_libraries(pybind11-utils INTERFACE pybind11::pybind11)
target_include_directories(pybind11-utils
INTERFACE ${PYTHON_ROOT}/Include ${CMAKE_CURRENT_SOURCE_DIR}/include)
target_include_directories(pybind11-utils INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/include)
add_library(pybind11::utils ALIAS pybind11-utils)
+7 -31
View File
@@ -1,7 +1,7 @@
cmake_minimum_required(VERSION 3.16)
add_library(pythonrunner SHARED)
mo2_configure_library(pythonrunner
add_library(runner STATIC)
mo2_configure_library(runner
SOURCE_TREE
WARNINGS 4
EXTERNAL_WARNINGS 4
@@ -9,33 +9,9 @@ mo2_configure_library(pythonrunner
TRANSLATIONS OFF
PUBLIC_DEPENDS uibase Qt::Core
)
target_link_libraries(pythonrunner PRIVATE pybind11::embed pybind11::qt)
target_include_directories(pythonrunner
PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}
PRIVATE ${PYTHON_ROOT}/Include)
target_link_libraries(runner PRIVATE pybind11::embed pybind11::qt)
target_include_directories(runner PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
mo2_install_target(runner)
# this is kind of broken but it only works with this...
target_compile_definitions(pythonrunner
PRIVATE QT_NO_KEYWORDS PYTHONRUNNER_LIBRARY)
mo2_install_target(pythonrunner INSTALLDIR bin/plugins/plugin_python)
# install DLLs files needed
set(DLL_DIRS ${MO2_INSTALL_PATH}/bin/plugins/plugin_python/dlls)
file(GLOB dlls_to_install
${PYTHON_BUILD_PATH}/libffi*.dll
${PYTHON_BUILD_PATH}/python${PYVERSION}.dll)
install(FILES ${dlls_to_install} DESTINATION ${DLL_DIRS})
# install Python files
set(PYLIB_DIR ${MO2_INSTALL_PATH}/bin/plugins/plugin_python/libs)
file(GLOB libs_to_install ${PYTHON_BUILD_PATH}/pythoncore/*.pyd)
install(FILES ${libs_to_install} DESTINATION ${PYLIB_DIR})
install(FILES ${PYTHON_BUILD_PATH}/pythoncore/python${PYVERSION}.zip
DESTINATION ${PYLIB_DIR} RENAME pythoncore.zip)
# install PyQt6
file(GLOB PYQT_DIR ${MO2_BUILD_PATH}/PyQt${QT_MAJOR_VERSION}*)
set(PYQT_LIB_DIR ${PYQT_DIR}/Lib/site-packages/PyQt${QT_MAJOR_VERSION})
set(PYQT_TARGET_DIR ${PYLIB_DIR}/PyQt${QT_MAJOR_VERSION})
file(GLOB pyqt_files ${PYQT_LIB_DIR}/*.py ${PYQT_LIB_DIR}/*.pyd ${PYQT_LIB_DIR}/*.pyi)
install(FILES ${pyqt_files} DESTINATION ${PYQT_TARGET_DIR})
# force runner to build mobase
add_dependencies(runner mobase)
File diff suppressed because it is too large Load Diff
+27 -30
View File
@@ -1,44 +1,41 @@
#ifndef PYTHONRUNNER_H
#define PYTHONRUNNER_H
#include <filesystem>
#include <map>
#include <memory>
#include <QList>
#include <QObject>
#include <QString>
#include <QStringList>
#include <dllimport.h>
#include <imoinfo.h>
namespace mo2::python {
class IPythonRunner {
public:
virtual QList<QObject*> load(const QString& identifier) = 0;
virtual void unload(const QString& identifier) = 0;
// initialize Python
// python runner interface
//
// paths contains the list of built-in paths for the Python library (pythonxxx.zip,
// etc.), an empty list uses the default Python paths (e.g., the PYTHONPATH
// environment variable)
class IPythonRunner {
public:
virtual QList<QObject*> load(const QString& identifier) = 0;
virtual void unload(const QString& identifier) = 0;
// initialize Python
//
// paths contains the list of built-in paths for the Python library
// (pythonxxx.zip, etc.), an empty list uses the default Python paths (e.g., the
// PYTHONPATH environment variable)
//
virtual bool initialize(QStringList const& paths = {}) = 0;
// check if the runner has been initialized, i.e., initialize() has been called
// and succeeded
virtual bool isInitialized() const = 0;
virtual ~IPythonRunner() {}
};
// create the Python runner
//
virtual bool initialize(QStringList const& paths = {}) = 0;
std::unique_ptr<IPythonRunner> createPythonRunner();
// check if the runner has been initialized, i.e., initialize() has been called and
// succeeded
virtual bool isInitialized() const = 0;
virtual ~IPythonRunner() {}
};
#ifdef PYTHONRUNNER_LIBRARY
#define PYDLLEXPORT Q_DECL_EXPORT
#else // PYTHONRUNNER_LIBRARY
#define PYDLLEXPORT Q_DECL_IMPORT
#endif // PYTHONRUNNER_LIBRARY
// create the Python runner
//
extern "C" PYDLLEXPORT IPythonRunner* CreatePythonRunner();
} // namespace mo2::python
#endif // PYTHONRUNNER_H
+10 -13
View File
@@ -4,37 +4,34 @@ cmake_minimum_required(VERSION 3.22)
# things
# first we configure the tests as with other tests
add_executable(pythonrunner-tests EXCLUDE_FROM_ALL)
mo2_configure_tests(pythonrunner-tests WARNINGS OFF)
add_executable(runner-tests EXCLUDE_FROM_ALL)
mo2_configure_tests(runner-tests WARNINGS OFF)
# add mocks
target_include_directories(pythonrunner-tests
target_include_directories(runner-tests
PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/../mocks)
# link to pythonrunner - we set PYTHONRUNNER_LIBRARY to not export the symbol but
# loads it directly, we are going to add the DLL path below
target_compile_definitions(pythonrunner-tests PUBLIC PYTHONRUNNER_LIBRARY)
target_link_libraries(pythonrunner-tests PUBLIC pythonrunner)
# link to runner
target_link_libraries(runner-tests PUBLIC runner)
set(PYLIB_DIR ${CMAKE_CURRENT_BINARY_DIR}/pylibs)
mo2_python_pip_install(pythonrunner-tests
mo2_python_pip_install(runner-tests
DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/pylibs
PACKAGES pytest PyQt6==6.3.0)
add_dependencies(pythonrunner-tests mobase)
add_dependencies(runner-tests mobase)
# we set multiple properties, including:
# - updated PATH for DLLs (Qt, etc.), Python and pythonrunner
# - updated PATH for DLLs (Qt, etc.), Python and runner
# - PYTHONPATH
set(pythoncore "${PYTHON_ROOT}/PCbuild/amd64/pythoncore")
file(GLOB pythoncorezip "${pythoncore}/python*.zip")
set(pythoncorezip "${pythoncore}/python${Python_VERSION_SHORT}.zip")
set(PYTHONPATH "${PYLIB_DIR}\\;$<TARGET_FILE_DIR:mobase>\\;${pythoncore}\\;${pythoncorezip}")
set(extra_paths "${MO2_INSTALL_PATH}/bin/dlls")
string(APPEND extra_paths "\\;${PYTHON_ROOT}/PCbuild/amd64")
string(APPEND extra_paths "\\;$<TARGET_FILE_DIR:pythonrunner>")
set_tests_properties(${pythonrunner-tests_gtests}
set_tests_properties(${runner-tests_gtests}
PROPERTIES
WORKING_DIRECTORY "${MO2_INSTALL_PATH}/bin"
ENVIRONMENT "PLUGIN_DIR=${CMAKE_CURRENT_SOURCE_DIR}/plugins"
+1 -1
View File
@@ -17,7 +17,7 @@ TEST(IPluginDiagnose, Simple)
{
const auto plugins_folder = QString(std::getenv("PLUGIN_DIR"));
std::unique_ptr<IPythonRunner> runner(CreatePythonRunner());
auto runner = mo2::python::createPythonRunner();
runner->initialize();
// load objects
+1 -1
View File
@@ -15,7 +15,7 @@ TEST(IPluginFileMapper, Simple)
{
const auto plugins_folder = QString(std::getenv("PLUGIN_DIR"));
std::unique_ptr<IPythonRunner> runner(CreatePythonRunner());
auto runner = mo2::python::createPythonRunner();
runner->initialize();
// load objects
+1 -1
View File
@@ -18,7 +18,7 @@ TEST(IPluginGame, Simple)
{
const auto plugins_folder = QString(std::getenv("PLUGIN_DIR"));
std::unique_ptr<IPythonRunner> runner(CreatePythonRunner());
auto runner = mo2::python::createPythonRunner();
runner->initialize();
// load objects
+1 -1
View File
@@ -15,7 +15,7 @@ TEST(IPluginInstaller, Simple)
{
const auto plugins_folder = QString(std::getenv("PLUGIN_DIR"));
std::unique_ptr<IPythonRunner> runner(CreatePythonRunner());
auto runner = mo2::python::createPythonRunner();
runner->initialize();
// load objects
+1 -1
View File
@@ -13,7 +13,7 @@ TEST(IPlugin, Basic)
{
const auto plugins_folder = QString(std::getenv("PLUGIN_DIR"));
std::unique_ptr<IPythonRunner> runner(CreatePythonRunner());
auto runner = mo2::python::createPythonRunner();
runner->initialize();
// load objects
+1 -1
View File
@@ -9,7 +9,7 @@ TEST(Lifetime, Plugins)
{
const auto plugins_folder = QString(std::getenv("PLUGIN_DIR"));
std::unique_ptr<IPythonRunner> runner(CreatePythonRunner());
auto runner = mo2::python::createPythonRunner();
runner->initialize();
{