Compare commits

...
14 Commits
Author SHA1 Message Date
Mikaël Capelle 79f585f483 Add extra classes for QVariant conversion. 2024-08-11 16:56:20 +02:00
Mikaël Capelle e85e69a7a1 Start adding bindings for extensions. 2024-08-11 13:30:07 +02:00
Mikaël Capelle 4c6ed4adc0 Add bindings for exceptions. 2024-08-11 12:53:28 +02:00
Mikaël Capelle 691a9a2f62 Remove unused import in test file. 2024-08-11 11:14:42 +02:00
Mikaël Capelle e155907d21 Fix logging for failed load. 2024-08-11 11:06:12 +02:00
Mikaël Capelle 54e05d31ef Add back IPlugin::requirements. 2024-08-10 15:38:09 +02:00
Mikaël Capelle bf05ac1593 Update following change to PluginSetting in uibase. 2024-08-10 12:23:12 +02:00
Mikaël Capelle e4576f67a6 Add some tests for QByteArray conversion. 2024-08-10 10:16:25 +02:00
Mikaël Capelle a4338db554 Fix after rebasing. 2024-08-09 22:13:43 +02:00
Mikaël Capelle 9b1793c726 Fix issue with unloading single .py file and fix tests. 2024-08-09 22:06:12 +02:00
Mikaël Capelle c658b396bc Properly load plugins. 2024-08-09 22:06:06 +02:00
Mikaël Capelle 8fa0d0ade8 Update following uibase changes for extensions. 2024-08-09 22:05:23 +02:00
Mikaël Capelle 2d151d0f66 Switch VersionInfo -> Version for ModOrganizer2. (#132) 2024-08-09 21:55:34 +02:00
Mikaël Capelle 7cca7018b3 Move to VCPKG.
* Minor cleaning of CMakeLists.txt. Fix C++ warnings and Qt deprecation.
* Avoid using Python from virtual environment.
2024-08-09 21:10:50 +02:00
53 changed files with 1009 additions and 683 deletions
+40 -10
View File
@@ -6,20 +6,50 @@ on:
pull_request: pull_request:
types: [opened, synchronize, reopened] types: [opened, synchronize, reopened]
env:
VCPKG_BINARY_SOURCES: "clear;x-gha,readwrite"
jobs: jobs:
build: build:
runs-on: windows-2022 runs-on: windows-2022
steps: steps:
- name: Build Plugin Python # https://learn.microsoft.com/en-us/vcpkg/consume/binary-caching-github-actions-cache
id: build-plugin-python - name: Export GitHub Actions cache environment variables
uses: ModOrganizer2/build-with-mob-action@master uses: actions/github-script@v7
with: with:
mo2-third-parties: gtest python spdlog boost sip pyqt pybind11 script: |
mo2-dependencies: cmake_common uibase core.exportVariable('ACTIONS_CACHE_URL', process.env.ACTIONS_CACHE_URL || '');
mo2-cmake-command: -DPLUGIN_PYTHON_TESTS=1 .. core.exportVariable('ACTIONS_RUNTIME_TOKEN', process.env.ACTIONS_RUNTIME_TOKEN || '');
- name: Build Plugin Python Tests
run: cmake --build vsbuild --config RelWithDebInfo -j4 --target python-tests --target runner-tests - uses: actions/setup-python@v5
working-directory: ${{ steps.build-plugin-python.outputs.working-directory }} with:
python-version: "3.12"
- name: Install Qt
uses: jurplel/install-qt-action@v3
with:
setup-python: false
version: 6.7.1
modules:
cache: true
- uses: actions/checkout@v4
- name: "Set environmental variables"
shell: bash
run: |
echo "VCPKG_ROOT=$VCPKG_INSTALLATION_ROOT" >> $GITHUB_ENV
- name: Configure Plugin Python build
shell: pwsh
run: |
cmake --preset vs2022-windows-standalone `
"-DCMAKE_PREFIX_PATH=${env:QT_ROOT_DIR}\msvc2019_64" `
-DPLUGIN_PYTHON_TESTING=ON
- name: Build Plugin Python
run: cmake --build vsbuild --config RelWithDebInfo --verbose `
--target python-tests --target runner-tests --target proxy
- name: Test Plugin Python - name: Test Plugin Python
run: ctest --test-dir vsbuild -C RelWithDebInfo --output-on-failure run: ctest --test-dir vsbuild -C RelWithDebInfo --output-on-failure
working-directory: ${{ steps.build-plugin-python.outputs.working-directory }}
+14 -26
View File
@@ -2,44 +2,32 @@ cmake_minimum_required(VERSION 3.16)
cmake_policy(SET CMP0144 NEW) cmake_policy(SET CMP0144 NEW)
if(DEFINED DEPENDENCIES_DIR)
include(${DEPENDENCIES_DIR}/modorganizer_super/cmake_common/mo2.cmake)
else()
include(${CMAKE_CURRENT_LIST_DIR}/../cmake_common/mo2.cmake)
endif()
project(plugin_python CXX) project(plugin_python CXX)
set(PYTHON_BUILD_PATH ${PYTHON_ROOT}/PCBuild/amd64) set(Python_FIND_VIRTUALENV STANDARD)
# find Python - lots of "Hints" since we have a weird setup # find Python before include mo2-cmake, otherwise this will trigger a bunch of CMP0111
set(Python_USE_STATIC_LIBS False) # due to the imported configuration mapping variables defined in mo2.cmake
set(Python_INCLUDE_DIR ${PYTHON_ROOT}/Include) find_package(Python ${MO2_PYTHON_VERSION} COMPONENTS Interpreter Development REQUIRED)
set(Python_EXECUTABLE ${PYTHON_BUILD_PATH}/python.exe) find_package(pybind11 CONFIG REQUIRED)
if (EXISTS "${PYTHON_BUILD_PATH}/python_d.exe")
set(Python_EXECUTABLE ${PYTHON_BUILD_PATH}/python_d.exe)
endif()
file(GLOB Python_LIBRARY ${PYTHON_BUILD_PATH}/python[0-9][0-9]*.lib)
find_package(Python COMPONENTS Interpreter Development REQUIRED)
# pybind11 needs uppercase (at least EXECUTABLE and LIBRARY) find_package(mo2-cmake CONFIG REQUIRED)
set(PYTHON_EXECUTABLE ${Python_EXECUTABLE})
set(PYTHON_INCLUDE_DIR ${Python_INCLUDE_DIR}) get_filename_component(Python_HOME ${Python_EXECUTABLE} PATH)
set(PYTHON_LIBRARY ${Python_LIBRARY}) set(Python_DLL_DIR "${Python_HOME}/DLLs")
set(Python_LIB_DIR "${Python_HOME}/Lib")
mo2_python_install_pyqt()
# useful for naming DLL, zip, etc. (3.10 -> 310) # useful for naming DLL, zip, etc. (3.10 -> 310)
set(Python_VERSION_SHORT ${Python_VERSION_MAJOR}${Python_VERSION_MINOR}) set(Python_VERSION_SHORT ${Python_VERSION_MAJOR}${Python_VERSION_MINOR})
# pybind11
add_subdirectory(${MO2_BUILD_PATH}/pybind11 ${CMAKE_CURRENT_BINARY_DIR}/pybind11)
# projects # projects
add_subdirectory(src) add_subdirectory(src)
set_property(DIRECTORY ${PROJECT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT proxy)
# tests (if requested) # tests (if requested)
set(PLUGIN_PYTHON_TESTS ${PLUGIN_PYTHON_TESTS} CACHE BOOL "build tests for plugin_python") set(PLUGIN_PYTHON_TESTING ${BUILD_TESTING} CACHE BOOL "build tests for plugin_python")
if (PLUGIN_PYTHON_TESTS) if (PLUGIN_PYTHON_TESTING)
enable_testing() enable_testing()
add_subdirectory(tests) add_subdirectory(tests)
endif() endif()
+73
View File
@@ -0,0 +1,73 @@
{
"configurePresets": [
{
"errors": {
"deprecated": true
},
"hidden": true,
"name": "cmake-dev",
"warnings": {
"deprecated": true,
"dev": true
}
},
{
"cacheVariables": {
"VCPKG_MANIFEST_NO_DEFAULT_FEATURES": {
"type": "BOOL",
"value": "ON"
}
},
"toolchainFile": "$env{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake",
"hidden": true,
"name": "vcpkg"
},
{
"cacheVariables": {
"VCPKG_MANIFEST_FEATURES": {
"type": "STRING",
"value": "testing"
}
},
"hidden": true,
"inherits": ["vcpkg"],
"name": "vcpkg-dev"
},
{
"binaryDir": "${sourceDir}/vsbuild",
"architecture": {
"strategy": "set",
"value": "x64"
},
"cacheVariables": {
"CMAKE_CXX_FLAGS": "/EHsc /MP /W4",
"VCPKG_TARGET_TRIPLET": {
"type": "STRING",
"value": "x64-windows-static-md"
}
},
"generator": "Visual Studio 17 2022",
"inherits": ["cmake-dev", "vcpkg-dev"],
"name": "vs2022-windows",
"toolset": "v143"
},
{
"cacheVariables": {
"VCPKG_MANIFEST_FEATURES": {
"type": "STRING",
"value": "standalone;testing"
}
},
"inherits": "vs2022-windows",
"name": "vs2022-windows-standalone"
}
],
"buildPresets": [
{
"name": "vs2022-windows",
"resolvePackageReferences": "on",
"configurePreset": "vs2022-windows"
}
],
"version": 4
}
+30 -4
View File
@@ -1,12 +1,38 @@
cmake_minimum_required(VERSION 3.16) cmake_minimum_required(VERSION 3.16)
find_package(Qt6 COMPONENTS Core)
find_package(mo2-uibase CONFIG REQUIRED)
pybind11_add_module(mobase MODULE) pybind11_add_module(mobase MODULE)
mo2_configure_library(mobase mo2_default_source_group()
SOURCE_TREE mo2_configure_target(mobase
NO_SOURCES
WARNINGS 4 WARNINGS 4
EXTERNAL_WARNINGS 4 EXTERNAL_WARNINGS 4
AUTOMOC ON AUTOMOC ON
TRANSLATIONS OFF TRANSLATIONS OFF
PRIVATE_DEPENDS uibase Qt::Core
) )
target_link_libraries(mobase PRIVATE pybind11::qt pybind11::utils) mo2_target_sources(mobase
FOLDER src
PRIVATE
deprecation.cpp
deprecation.h
mobase.cpp
pybind11_all.h
)
mo2_target_sources(mobase
FOLDER src/wrappers
PRIVATE
./wrappers/basic_classes.cpp
./wrappers/game_features.cpp
./wrappers/known_folders.h
./wrappers/pyfiletree.cpp
./wrappers/pyfiletree.h
./wrappers/pyplugins.cpp
./wrappers/pyplugins.h
./wrappers/utils.cpp
./wrappers/widgets.cpp
./wrappers/wrappers.cpp
./wrappers/wrappers.h
)
target_link_libraries(mobase PRIVATE pybind11::qt pybind11::utils mo2::uibase Qt6::Core)
+1 -1
View File
@@ -7,7 +7,7 @@
#include <QCoreApplication> #include <QCoreApplication>
#include "log.h" #include <uibase/log.h>
namespace py = pybind11; namespace py = pybind11;
+12 -18
View File
@@ -5,6 +5,9 @@
#include <tuple> #include <tuple>
#include <variant> #include <variant>
#include <uibase/exceptions.h>
#include <uibase/versioning.h>
#include <pybind11/embed.h> #include <pybind11/embed.h>
#include "pybind11_all.h" #include "pybind11_all.h"
@@ -12,23 +15,6 @@
#include "wrappers/pyfiletree.h" #include "wrappers/pyfiletree.h"
#include "wrappers/wrappers.h" #include "wrappers/wrappers.h"
// TODO: remove these include (only for testing)
#include <QDir>
#include <QFile>
#include <QWidget>
#include <iplugin.h>
#include <iplugindiagnose.h>
#include <ipluginfilemapper.h>
#include <iplugingame.h>
#include <iplugininstaller.h>
#include <iplugininstallersimple.h>
#include <ipluginlist.h>
#include <ipluginmodpage.h>
#include <ipluginpreview.h>
#include <iplugintool.h>
#include <isavegame.h>
#include <isavegameinfowidget.h>
using namespace MOBase; using namespace MOBase;
namespace py = pybind11; namespace py = pybind11;
@@ -41,6 +27,13 @@ PYBIND11_MODULE(mobase, m)
m.add_object("PyQt6.QtGui", py::module_::import("PyQt6.QtGui")); m.add_object("PyQt6.QtGui", py::module_::import("PyQt6.QtGui"));
m.add_object("PyQt6.QtWidgets", py::module_::import("PyQt6.QtWidgets")); m.add_object("PyQt6.QtWidgets", py::module_::import("PyQt6.QtWidgets"));
// exceptions
//
py::register_exception<Exception>(m, "MO2Exception");
py::register_exception<InvalidNXMLinkException>(m, "InvalidNXMLinkException");
py::register_exception<IncompatibilityException>(m, "IncompatibilityException");
py::register_exception<InvalidVersionException>(m, "InvalidVersionException");
// bindings // bindings
// //
mo2::python::add_basic_bindings(m); mo2::python::add_basic_bindings(m);
@@ -86,7 +79,8 @@ PYBIND11_MODULE(mobase, m)
// //
m.add_object( m.add_object(
"MoVariant", "MoVariant",
py::eval("None | bool | int | str | list[object] | dict[str, object]")); py::eval(
"None | bool | int | str | float | list[object] | dict[str, object]"));
// same thing for GameFeatureType // same thing for GameFeatureType
// //
+3 -3
View File
@@ -16,9 +16,9 @@
#include "pybind11_utils/shared_cpp_owner.h" #include "pybind11_utils/shared_cpp_owner.h"
#include "pybind11_utils/smart_variant_wrapper.h" #include "pybind11_utils/smart_variant_wrapper.h"
#include <game_feature.h> #include <uibase/game_features/game_feature.h>
#include <isavegame.h> #include <uibase/isavegame.h>
#include <pluginrequirements.h> #include <uibase/pluginrequirements.h>
namespace mo2::python { namespace mo2::python {
+157 -22
View File
@@ -4,21 +4,23 @@
#include <format> #include <format>
#include <executableinfo.h> #include <uibase/executableinfo.h>
#include <filemapping.h> #include <uibase/extensions/extension.h>
#include <guessedvalue.h> #include <uibase/extensions/iextensionlist.h>
#include <idownloadmanager.h> #include <uibase/filemapping.h>
#include <igamefeatures.h> #include <uibase/game_features/igamefeatures.h>
#include <iinstallationmanager.h> #include <uibase/guessedvalue.h>
#include <imodinterface.h> #include <uibase/idownloadmanager.h>
#include <imodrepositorybridge.h> #include <uibase/iinstallationmanager.h>
#include <imoinfo.h> #include <uibase/imodinterface.h>
#include <iplugin.h> #include <uibase/imodrepositorybridge.h>
#include <iplugindiagnose.h> #include <uibase/imoinfo.h>
#include <iplugingame.h> #include <uibase/iplugin.h>
#include <ipluginlist.h> #include <uibase/iplugindiagnose.h>
#include <pluginsetting.h> #include <uibase/iplugingame.h>
#include <versioninfo.h> #include <uibase/ipluginlist.h>
#include <uibase/pluginsetting.h>
#include <uibase/versioninfo.h>
#include "../deprecation.h" #include "../deprecation.h"
#include "pyfiletree.h" #include "pyfiletree.h"
@@ -33,6 +35,69 @@ namespace mo2::python {
void add_versioninfo_classes(py::module_ m) void add_versioninfo_classes(py::module_ m)
{ {
// Version
py::class_<Version> pyVersion(m, "Version");
py::enum_<Version::ReleaseType>(pyVersion, "ReleaseType")
.value("DEVELOPMENT", Version::Development)
.value("ALPHA", Version::Alpha)
.value("BETA", Version::Beta)
.value("RELEASE_CANDIDATE", Version::ReleaseCandidate)
.export_values();
py::enum_<Version::ParseMode>(pyVersion, "ParseMode")
.value("SEMVER", Version::ParseMode::SemVer)
.value("MO2", Version::ParseMode::MO2);
py::enum_<Version::FormatMode>(pyVersion, "FormatMode", py::arithmetic{})
.value("FORCE_SUBPATCH", Version::FormatMode::ForceSubPatch)
.value("NO_SEPARATOR", Version::FormatMode::NoSeparator)
.value("SHORT_ALPHA_BETA", Version::FormatMode::ShortAlphaBeta)
.value("NO_METADATA", Version::FormatMode::NoMetadata)
.value("CONDENSED",
static_cast<Version::FormatMode>(Version::FormatCondensed.toInt()))
.export_values();
pyVersion
.def_static("parse", &Version::parse, "value"_a,
"mode"_a = Version::ParseMode::SemVer)
.def(py::init<int, int, int, QString>(), "major"_a, "minor"_a, "patch"_a,
"metadata"_a = "")
.def(py::init<int, int, int, int, QString>(), "major"_a, "minor"_a,
"patch"_a, "subpatch"_a, "metadata"_a = "")
.def(py::init<int, int, int, Version::ReleaseType, QString>(), "major"_a,
"minor"_a, "patch"_a, "type"_a, "metadata"_a = "")
.def(py::init<int, int, int, int, Version::ReleaseType, QString>(),
"major"_a, "minor"_a, "patch"_a, "subpatch"_a, "type"_a,
"metadata"_a = "")
.def(py::init<int, int, int, Version::ReleaseType, int, QString>(),
"major"_a, "minor"_a, "patch"_a, "type"_a, "prerelease"_a,
"metadata"_a = "")
.def(py::init<int, int, int, int, Version::ReleaseType, int, QString>(),
"major"_a, "minor"_a, "patch"_a, "subpatch"_a, "type"_a,
"prerelease"_a, "metadata"_a = "")
.def(py::init<int, int, int, int,
std::vector<std::variant<int, Version::ReleaseType>>,
QString>(),
"major"_a, "minor"_a, "patch"_a, "subpatch"_a, "prereleases"_a,
"metadata"_a = "")
.def("isPreRelease", &Version::isPreRelease)
.def_property_readonly("major", &Version::major)
.def_property_readonly("minor", &Version::minor)
.def_property_readonly("patch", &Version::patch)
.def_property_readonly("subpatch", &Version::subpatch)
.def_property_readonly("prereleases", &Version::preReleases)
.def_property_readonly("build_metadata", &Version::buildMetadata)
.def("string", &Version::string, "mode"_a = Version::FormatCondensed)
.def("__str__", &Version::string)
.def(py::self < py::self)
.def(py::self > py::self)
.def(py::self <= py::self)
.def(py::self >= py::self)
.def(py::self != py::self)
.def(py::self == py::self);
// VersionInfo
py::enum_<MOBase::VersionInfo::ReleaseType>(m, "ReleaseType") py::enum_<MOBase::VersionInfo::ReleaseType>(m, "ReleaseType")
.value("final", MOBase::VersionInfo::RELEASE_FINAL) .value("final", MOBase::VersionInfo::RELEASE_FINAL)
.value("candidate", MOBase::VersionInfo::RELEASE_CANDIDATE) .value("candidate", MOBase::VersionInfo::RELEASE_CANDIDATE)
@@ -295,6 +360,38 @@ namespace mo2::python {
py::implicitly_convertible<QString, GuessedValue<QString>>(); py::implicitly_convertible<QString, GuessedValue<QString>>();
} }
void add_iextensionlist_classes(py::module_ m)
{
// TODO: add all bindings here
py::class_<IExtension>(m, "IExtension");
py::class_<IExtensionList>(m, "IExtensionList")
.def("installed", &IExtensionList::installed, "identifier"_a)
.def(
"enabled",
py::overload_cast<const QString&>(&IExtensionList::enabled, py::const_),
"identifier"_a)
.def(
"__getitem__",
[](IExtensionList const& self,
std::variant<std::size_t, QString> const& index) {
return std::visit(
[&self](auto&& value) {
if constexpr (std::is_same_v<std::decay_t<decltype(value)>,
std::size_t>) {
return self.at(value);
}
else {
return self.get(value);
}
},
index);
},
"index"_a, py::return_value_policy::reference)
.def("__len__", &IExtensionList::size);
}
void add_ipluginlist_classes(py::module_ m) void add_ipluginlist_classes(py::module_ m)
{ {
py::enum_<IPluginList::PluginState>(m, "PluginState", py::arithmetic()) py::enum_<IPluginList::PluginState>(m, "PluginState", py::arithmetic())
@@ -447,7 +544,17 @@ namespace mo2::python {
.def("overwritePath", &IOrganizer::overwritePath) .def("overwritePath", &IOrganizer::overwritePath)
.def("basePath", &IOrganizer::basePath) .def("basePath", &IOrganizer::basePath)
.def("modsPath", &IOrganizer::modsPath) .def("modsPath", &IOrganizer::modsPath)
.def("appVersion", &IOrganizer::appVersion) .def("appVersion",
[](IOrganizer& o) {
mo2::python::show_deprecation_warning(
"appVersion", "IOrganizer::appVersion() is deprecated, use "
"IOrganizer::version() instead.");
#pragma warning(push)
#pragma warning(disable : 4996)
return o.appVersion();
#pragma warning(pop)
})
.def("version", &IOrganizer::version)
.def("createMod", &IOrganizer::createMod, .def("createMod", &IOrganizer::createMod,
py::return_value_policy::reference, "name"_a) py::return_value_policy::reference, "name"_a)
.def("getGame", &IOrganizer::getGame, py::return_value_policy::reference, .def("getGame", &IOrganizer::getGame, py::return_value_policy::reference,
@@ -517,6 +624,8 @@ namespace mo2::python {
py::return_value_policy::reference) py::return_value_policy::reference)
.def("pluginList", &IOrganizer::pluginList, .def("pluginList", &IOrganizer::pluginList,
py::return_value_policy::reference) py::return_value_policy::reference)
.def("extensionList", &IOrganizer::extensionList,
py::return_value_policy::reference)
.def("modList", &IOrganizer::modList, py::return_value_policy::reference) .def("modList", &IOrganizer::modList, py::return_value_policy::reference)
.def("gameFeatures", &IOrganizer::gameFeatures, .def("gameFeatures", &IOrganizer::gameFeatures,
py::return_value_policy::reference) py::return_value_policy::reference)
@@ -721,12 +830,37 @@ namespace mo2::python {
add_modinterface_classes(m); add_modinterface_classes(m);
add_modrepository_classes(m); add_modrepository_classes(m);
py::class_<PluginSetting>(m, "PluginSetting") py::class_<Setting>(m, "Setting")
.def(py::init<const QString&, const QString&, const QVariant&>(), "key"_a, .def(py::init([](const QString& name, const QString& description,
"description"_a, "default_value"_a) const QVariant& defaultValue) {
.def_readwrite("key", &PluginSetting::key) mo2::python::show_deprecation_warning(
.def_readwrite("description", &PluginSetting::description) "Setting(key, description, default)",
.def_readwrite("default_value", &PluginSetting::defaultValue); "Setting(key, description, default) is deprecated, use "
"Setting(name, title, description, default) instead.");
return Setting(name, description, defaultValue);
}),
"key"_a, "description"_a, "default_value"_a)
.def(py::init<const QString&, const QString&, const QString&,
const QVariant&>(),
"name"_a, "title"_a, "description"_a, "default_value"_a)
.def(py::init<const QString&, const QString&, const QString&,
const QString&, const QVariant&>(),
"name"_a, "title"_a, "description"_a, "group"_a, "default_value"_a)
.def_property_readonly("name", &Setting::name)
.def_property_readonly("title", &Setting::title)
.def_property_readonly("description", &Setting::description)
.def_property_readonly("group", &Setting::group)
.def_property_readonly("default_value", &Setting::defaultValue);
// deprecated alias
m.attr("PluginSetting") = m.attr("Setting");
py::class_<SettingGroup>(m, "SettingGroup")
.def(py::init<const QString&, const QString&, const QString&>(), "name"_a,
"title"_a, "description"_a)
.def_property_readonly("name", &SettingGroup::name)
.def_property_readonly("title", &SettingGroup::title)
.def_property_readonly("description", &SettingGroup::description);
py::class_<PluginRequirementFactory>(m, "PluginRequirementFactory") py::class_<PluginRequirementFactory>(m, "PluginRequirementFactory")
// pluginDependency // pluginDependency
@@ -785,6 +919,7 @@ namespace mo2::python {
}) })
.def("absoluteIniFilePath", &IProfile::absoluteIniFilePath, "inifile"_a); .def("absoluteIniFilePath", &IProfile::absoluteIniFilePath, "inifile"_a);
add_iextensionlist_classes(m);
add_ipluginlist_classes(m); add_ipluginlist_classes(m);
add_imodlist_classes(m); add_imodlist_classes(m);
add_idownload_manager_classes(m); add_idownload_manager_classes(m);
+12 -12
View File
@@ -4,19 +4,19 @@
#include "../pybind11_all.h" #include "../pybind11_all.h"
#include <ipluginlist.h> #include <uibase/ipluginlist.h>
#include <isavegameinfowidget.h> #include <uibase/isavegameinfowidget.h>
#include <bsainvalidation.h> #include <uibase/game_features/bsainvalidation.h>
#include <dataarchives.h> #include <uibase/game_features/dataarchives.h>
#include <gameplugins.h> #include <uibase/game_features/gameplugins.h>
#include <igamefeatures.h> #include <uibase/game_features/igamefeatures.h>
#include <localsavegames.h> #include <uibase/game_features/localsavegames.h>
#include <moddatachecker.h> #include <uibase/game_features/moddatachecker.h>
#include <moddatacontent.h> #include <uibase/game_features/moddatacontent.h>
#include <savegameinfo.h> #include <uibase/game_features/savegameinfo.h>
#include <scriptextender.h> #include <uibase/game_features/scriptextender.h>
#include <unmanagedmods.h> #include <uibase/game_features/unmanagedmods.h>
#include "pyfiletree.h" #include "pyfiletree.h"
+2 -2
View File
@@ -5,8 +5,8 @@
#include "../pybind11_all.h" #include "../pybind11_all.h"
#include <ifiletree.h> #include <uibase/ifiletree.h>
#include <log.h> #include <uibase/log.h>
namespace py = pybind11; namespace py = pybind11;
using namespace MOBase; using namespace MOBase;
+1 -1
View File
@@ -3,7 +3,7 @@
#include "../pybind11_all.h" #include "../pybind11_all.h"
#include <ifiletree.h> #include <uibase/ifiletree.h>
namespace pybind11 { namespace pybind11 {
template <> template <>
+1 -4
View File
@@ -144,12 +144,9 @@ namespace mo2::python {
.def("init", &IPlugin::init, "organizer"_a) .def("init", &IPlugin::init, "organizer"_a)
.def("name", &IPlugin::name) .def("name", &IPlugin::name)
.def("localizedName", &IPlugin::localizedName) .def("localizedName", &IPlugin::localizedName)
.def("master", &IPlugin::master)
.def("author", &IPlugin::author)
.def("description", &IPlugin::description)
.def("version", &IPlugin::version)
.def("requirements", &IPlugin::requirements) .def("requirements", &IPlugin::requirements)
.def("settings", &IPlugin::settings) .def("settings", &IPlugin::settings)
.def("settingGroups", &IPlugin::settingGroups)
.def("enabledByDefault", &IPlugin::enabledByDefault); .def("enabledByDefault", &IPlugin::enabledByDefault);
py::class_<IPyPlugin, PyPlugin, IPlugin, py::class_<IPyPlugin, PyPlugin, IPlugin,
+19 -25
View File
@@ -3,18 +3,18 @@
#include "../pybind11_all.h" #include "../pybind11_all.h"
#include <iinstallationmanager.h> #include <uibase/iinstallationmanager.h>
#include <iplugin.h> #include <uibase/iplugin.h>
#include <iplugindiagnose.h> #include <uibase/iplugindiagnose.h>
#include <ipluginfilemapper.h> #include <uibase/ipluginfilemapper.h>
#include <iplugingame.h> #include <uibase/iplugingame.h>
#include <iplugininstaller.h> #include <uibase/iplugininstaller.h>
#include <iplugininstallercustom.h> #include <uibase/iplugininstallercustom.h>
#include <iplugininstallersimple.h> #include <uibase/iplugininstallersimple.h>
#include <ipluginmodpage.h> #include <uibase/ipluginmodpage.h>
#include <ipluginpreview.h> #include <uibase/ipluginpreview.h>
#include <iplugintool.h> #include <uibase/iplugintool.h>
// these needs to be defined in a header file for automoc - this file is included only // these needs to be defined in a header file for automoc - this file is included only
// in pyplugins.cpp // in pyplugins.cpp
@@ -45,25 +45,19 @@ namespace mo2::python {
{ {
PYBIND11_OVERRIDE(QString, PluginBase, localizedName, ); PYBIND11_OVERRIDE(QString, PluginBase, localizedName, );
} }
QString master() const override std::vector<std::shared_ptr<const IPluginRequirement>>
requirements() const override
{ {
PYBIND11_OVERRIDE(QString, PluginBase, master, ); PYBIND11_OVERRIDE(std::vector<std::shared_ptr<const IPluginRequirement>>,
PluginBase, requirements, );
} }
QString author() const override QList<Setting> settings() const override
{ {
PYBIND11_OVERRIDE_PURE(QString, PluginBase, author, ); PYBIND11_OVERRIDE_PURE(QList<Setting>, PluginBase, settings, );
} }
QString description() const override QList<SettingGroup> settingGroups() const override
{ {
PYBIND11_OVERRIDE_PURE(QString, PluginBase, description, ); PYBIND11_OVERRIDE(QList<SettingGroup>, PluginBase, settingGroups, );
}
VersionInfo version() const override
{
PYBIND11_OVERRIDE_PURE(VersionInfo, PluginBase, version, );
}
QList<PluginSetting> settings() const override
{
PYBIND11_OVERRIDE_PURE(QList<PluginSetting>, PluginBase, settings, );
} }
}; };
+2 -2
View File
@@ -2,8 +2,8 @@
#include "../pybind11_all.h" #include "../pybind11_all.h"
#include <report.h> #include <uibase/report.h>
#include <utility.h> #include <uibase/utility.h>
#include "known_folders.h" #include "known_folders.h"
+1 -1
View File
@@ -2,7 +2,7 @@
#include "../pybind11_all.h" #include "../pybind11_all.h"
#include <report.h> #include <uibase/report.h>
namespace py = pybind11; namespace py = pybind11;
using namespace MOBase; using namespace MOBase;
+4 -4
View File
@@ -10,10 +10,10 @@
// IOrganizer must be bring here to properly compile the Python bindings of // IOrganizer must be bring here to properly compile the Python bindings of
// plugin requirements // plugin requirements
#include <imoinfo.h> #include <uibase/imoinfo.h>
#include <isavegame.h> #include <uibase/isavegame.h>
#include <isavegameinfowidget.h> #include <uibase/isavegameinfowidget.h>
#include <pluginrequirements.h> #include <uibase/pluginrequirements.h>
using namespace pybind11::literals; using namespace pybind11::literals;
namespace py = pybind11; namespace py = pybind11;
+1 -1
View File
@@ -10,7 +10,7 @@
#include <QList> #include <QList>
#include <QObject> #include <QObject>
#include <iplugingame.h> #include <uibase/iplugingame.h>
namespace mo2::python { namespace mo2::python {
+9 -52
View File
@@ -4,73 +4,30 @@
<context> <context>
<name>ProxyPython</name> <name>ProxyPython</name>
<message> <message>
<location filename="proxy/proxypython.cpp" line="88"/> <location filename="proxy/proxypython.cpp" line="170"/>
<source>Python Initialization failed</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="proxy/proxypython.cpp" line="89"/>
<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="proxy/proxypython.cpp" line="162"/>
<source>Python Proxy</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="proxy/proxypython.cpp" line="172"/>
<source>Proxy Plugin to allow plugins written in python to be loaded</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="proxy/proxypython.cpp" line="244"/>
<source>ModOrganizer path contains a semicolon</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="proxy/proxypython.cpp" line="246"/>
<source>Python DLL not found</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="proxy/proxypython.cpp" line="248"/>
<source>Invalid Python DLL</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="proxy/proxypython.cpp" line="250"/>
<source>Initializing Python failed</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="proxy/proxypython.cpp" line="252"/>
<location filename="proxy/proxypython.cpp" line="281"/>
<source>invalid problem key %1</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="proxy/proxypython.cpp" line="260"/>
<source>The path to Mod Organizer (%1) contains a semicolon.&lt;br&gt;While this is legal on NTFS drives, many applications 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 can offer is to remove the semicolon or move MO to a path without a semicolon.</source> <source>The path to Mod Organizer (%1) contains a semicolon.&lt;br&gt;While this is legal on NTFS drives, many applications 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 can offer is to remove the semicolon or move MO to a path without a semicolon.</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="proxy/proxypython.cpp" line="270"/> <location filename="proxy/proxypython.cpp" line="180"/>
<source>The Python plugin DLL was not found, maybe your antivirus deleted it. Re-installing MO2 might fix the problem.</source> <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> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="proxy/proxypython.cpp" line="273"/> <location filename="proxy/proxypython.cpp" line="183"/>
<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> <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> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="proxy/proxypython.cpp" line="278"/> <location filename="proxy/proxypython.cpp" line="188"/>
<source>The initialization of the Python plugin DLL failed, unfortunately without any details.</source> <source>The initialization of the Python plugin DLL failed, unfortunately without any details.</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message>
<location filename="proxy/proxypython.cpp" line="191"/>
<source>no failure</source>
<translation type="unfinished"></translation>
</message>
</context> </context>
<context> <context>
<name>QObject</name> <name>QObject</name>
+45 -22
View File
@@ -1,9 +1,12 @@
cmake_minimum_required(VERSION 3.16) cmake_minimum_required(VERSION 3.16)
set(PLUGIN_NAME "plugin_python") find_package(mo2-uibase CONFIG REQUIRED)
add_library(proxy SHARED) set(PROXY_NAME "python")
add_library(proxy SHARED proxypython.cpp proxypython.h)
mo2_configure_plugin(proxy mo2_configure_plugin(proxy
NO_SOURCES
WARNINGS 4 WARNINGS 4
EXTERNAL_WARNINGS 4 EXTERNAL_WARNINGS 4
TRANSLATIONS OFF TRANSLATIONS OFF
@@ -11,19 +14,25 @@ mo2_configure_plugin(proxy
${CMAKE_CURRENT_SOURCE_DIR}/../runner ${CMAKE_CURRENT_SOURCE_DIR}/../runner
${CMAKE_CURRENT_SOURCE_DIR}/../mobase ${CMAKE_CURRENT_SOURCE_DIR}/../mobase
${CMAKE_CURRENT_SOURCE_DIR}/../pybind11-qt) ${CMAKE_CURRENT_SOURCE_DIR}/../pybind11-qt)
target_link_libraries(proxy PRIVATE runner) mo2_default_source_group()
set_target_properties(proxy PROPERTIES OUTPUT_NAME ${PLUGIN_NAME}) target_link_libraries(proxy PRIVATE runner mo2::uibase)
mo2_install_target(proxy FOLDER) set_target_properties(proxy PROPERTIES OUTPUT_NAME "python_proxy")
set(PLUGIN_PYTHON_DIR ${MO2_INSTALL_PATH}/bin/plugins/${PLUGIN_NAME}) set(PROXY_PYTHON_DIR ${MO2_INSTALL_BIN}/proxies/python)
# install runner # install runner and proxy
install(FILES $<TARGET_FILE:proxy> DESTINATION ${PROXY_PYTHON_DIR})
install(FILES $<TARGET_FILE:runner> DESTINATION ${PROXY_PYTHON_DIR}/dlls)
# install PDB
install(FILES $<TARGET_PDB_FILE:proxy> DESTINATION pdb)
# delay loading since the dll is not in the standard folder
target_link_options(proxy PRIVATE "/DELAYLOAD:runner.dll") target_link_options(proxy PRIVATE "/DELAYLOAD:runner.dll")
mo2_install_target(runner INSTALLDIR ${PLUGIN_PYTHON_DIR}/dlls)
# translations (custom location) # translations (custom location)
mo2_add_translations(proxy mo2_add_translations(proxy
TS_FILE ${CMAKE_CURRENT_SOURCE_DIR}/../${PLUGIN_NAME}_en.ts TS_FILE ${CMAKE_CURRENT_SOURCE_DIR}/../plugin_python_en.ts
SOURCES SOURCES
${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}
${CMAKE_CURRENT_SOURCE_DIR}/../runner ${CMAKE_CURRENT_SOURCE_DIR}/../runner
@@ -31,25 +40,39 @@ mo2_add_translations(proxy
${CMAKE_CURRENT_SOURCE_DIR}/../pybind11-qt) ${CMAKE_CURRENT_SOURCE_DIR}/../pybind11-qt)
# install DLLs files needed # install DLLs files needed
set(DLL_DIRS ${PLUGIN_PYTHON_DIR}/dlls) set(DLL_DIRS ${PROXY_PYTHON_DIR}/dlls)
file(GLOB dlls_to_install file(GLOB dlls_to_install
${PYTHON_BUILD_PATH}/libffi*.dll # ${PYTHON_BUILD_PATH}/libffi*.dll
${PYTHON_BUILD_PATH}/python${Python_VERSION_SHORT}*.dll) ${Python_HOME}/python${Python_VERSION_MAJOR}*.dll)
install(FILES ${dlls_to_install} DESTINATION ${DLL_DIRS}) install(FILES ${dlls_to_install} DESTINATION ${DLL_DIRS})
# install Python files # install Python .pyd files
set(PYLIB_DIR ${PLUGIN_PYTHON_DIR}/libs) set(PYLIB_DIR ${PROXY_PYTHON_DIR}/libs)
file(GLOB libs_to_install ${PYTHON_BUILD_PATH}/pythoncore/*.pyd) file(GLOB libs_to_install ${Python_DLL_DIR}/*.pyd)
install(FILES ${libs_to_install} DESTINATION ${PYLIB_DIR}) 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) # generate + install standard library
set(pythoncore_zip "${CMAKE_CURRENT_BINARY_DIR}/pythoncore.zip")
add_custom_command(
TARGET proxy POST_BUILD
COMMAND ${Python_EXECUTABLE}
"${CMAKE_CURRENT_SOURCE_DIR}\\build_pythoncore.py"
${pythoncore_zip}
)
install(FILES ${pythoncore_zip} DESTINATION ${PYLIB_DIR})
# install mobase # install mobase
install(TARGETS mobase DESTINATION ${PYLIB_DIR}) install(TARGETS mobase DESTINATION ${PYLIB_DIR})
# install PyQt6 # install PyQt6
#file(GLOB PYQT_DIR ${MO2_BUILD_PATH}/PyQt${QT_MAJOR_VERSION}*) install(
set(PYQT_LIB_DIR ${PYTHON_ROOT}/Lib/site-packages/PyQt${QT_MAJOR_VERSION}) DIRECTORY ${CMAKE_BINARY_DIR}/pylibs/PyQt${MO2_QT_VERSION_MAJOR}
set(PYQT_TARGET_DIR ${PYLIB_DIR}/PyQt${QT_MAJOR_VERSION}) DESTINATION ${PYLIB_DIR}
file(GLOB pyqt_files ${PYQT_LIB_DIR}/*.py ${PYQT_LIB_DIR}/*.pyd ${PYQT_LIB_DIR}/*.pyi) PATTERN "*.pyd"
install(FILES ${pyqt_files} DESTINATION ${PYQT_TARGET_DIR}) PATTERN "*.pyi"
PATTERN "__pycache__" EXCLUDE
PATTERN "bindings" EXCLUDE
PATTERN "lupdate" EXCLUDE
PATTERN "Qt6" EXCLUDE
PATTERN "uic" EXCLUDE
)
+14
View File
@@ -0,0 +1,14 @@
import sys
import zipfile
from pathlib import Path
_EXCLUDE_MODULES = ["ensurepip", "idlelib", "test", "tkinter", "turtle_demo", "venv"]
libdir = Path(sys.executable).parent.joinpath("Lib")
assert libdir.exists()
with zipfile.PyZipFile(sys.argv[1], optimize=2, mode="w") as fp:
fp.writepy(libdir) # pyright: ignore[reportArgumentType]
for path in libdir.iterdir():
if path.is_dir() and path.name not in _EXCLUDE_MODULES:
fp.writepy(path) # pyright: ignore[reportArgumentType]

Some files were not shown because too many files have changed in this diff Show More