commit 651b5a0332294c0b07dc6e6815417eef5b3c8921
Author: Michael Cronenworth <mike(a)cchtml.com>
Date: Sun Apr 26 20:57:33 2020 -0500
Python 3 and libfmt fixes
kodi-18-libfmt.patch | 44 ++++++
kodi-18-python3-0003.patch | 370 +++++++++++++++++++++++++++++++++++++++++++++
kodi.spec | 23 ++-
3 files changed, 432 insertions(+), 5 deletions(-)
---
diff --git a/kodi-18-libfmt.patch b/kodi-18-libfmt.patch
new file mode 100644
index 0000000..cb17b27
--- /dev/null
+++ b/kodi-18-libfmt.patch
@@ -0,0 +1,44 @@
+From 8fa5fa50dbbb94f223635517079b4827513d88d8 Mon Sep 17 00:00:00 2001
+From: Philipp Kerling <yol(a)casix.org>
+Date: Wed, 15 Apr 2020 20:31:55 +0200
+Subject: [PATCH] Fix missing cast to void* in %p format
+
+POSIX says %p argument must be pointer to void. Newer libfmt seems to
+have an issue with arbitary pointers, even failing at compile time, so
+make sure the cast is there (fmt::ptr casts to void*).
+---
+ .../VideoPlayer/DVDCodecs/Video/AddonVideoCodec.cpp | 10 ++++++++--
+ 1 file changed, 8 insertions(+), 2 deletions(-)
+
+diff --git a/xbmc/cores/VideoPlayer/DVDCodecs/Video/AddonVideoCodec.cpp
b/xbmc/cores/VideoPlayer/DVDCodecs/Video/AddonVideoCodec.cpp
+index fdd6c1aeb354..c062daeb78c0 100644
+--- a/xbmc/cores/VideoPlayer/DVDCodecs/Video/AddonVideoCodec.cpp
++++ b/xbmc/cores/VideoPlayer/DVDCodecs/Video/AddonVideoCodec.cpp
+@@ -260,8 +260,14 @@ CDVDVideoCodec::VCReturn CAddonVideoCodec::GetPicture(VideoPicture*
pVideoPictur
+ }
+ }
+
+- CLog::Log(LOGDEBUG, LOGVIDEO, "CAddonVideoCodec: GetPicture::VC_PICTURE with
pts %llu %dx%d (%dx%d) %f %p:%d offset:%d,%d,%d, stride:%d,%d,%d", picture.pts,
pVideoPicture->iWidth, pVideoPicture->iHeight, pVideoPicture->iDisplayWidth,
pVideoPicture->iDisplayHeight, m_displayAspect,
+- picture.decodedData, picture.decodedDataSize, picture.planeOffsets[0],
picture.planeOffsets[1], picture.planeOffsets[2], picture.stride[0], picture.stride[1],
picture.stride[2]);
++ CLog::Log(LOGDEBUG, LOGVIDEO,
++ "CAddonVideoCodec: GetPicture::VC_PICTURE with pts {} {}x{} ({}x{})
{} {}:{} "
++ "offset:{},{},{}, stride:{},{},{}",
++ picture.pts, pVideoPicture->iWidth, pVideoPicture->iHeight,
++ pVideoPicture->iDisplayWidth, pVideoPicture->iDisplayHeight,
m_displayAspect,
++ fmt::ptr(picture.decodedData), picture.decodedDataSize,
picture.planeOffsets[0],
++ picture.planeOffsets[1], picture.planeOffsets[2], picture.stride[0],
++ picture.stride[1], picture.stride[2]);
+
+ if (picture.width != m_width || picture.height != m_height)
+ {
+--- a/xbmc/windowing/X11/WinSystemX11.cpp 2020-02-29 04:56:32.000000000 -0600
++++ b/xbmc/windowing/X11/WinSystemX11.cpp 2020-04-26 17:19:00.784441374 -0500
+@@ -1025,7 +1025,7 @@
+
+ if(status == Success && items_read)
+ {
+- CLog::Log(LOGDEBUG,"Window Manager Name: %s", data);
++ CLog::Log(LOGDEBUG,"Window Manager Name: %s", (char *) data);
+ }
+ else
+ CLog::Log(LOGDEBUG,"Window Manager Name: ");
diff --git a/kodi-18-python3-0003.patch b/kodi-18-python3-0003.patch
new file mode 100644
index 0000000..40ba94f
--- /dev/null
+++ b/kodi-18-python3-0003.patch
@@ -0,0 +1,370 @@
+--- a/xbmc/interfaces/python/PythonInvoker.cpp 2020-04-26 17:20:43.652416200 -0500
++++ b/xbmc/interfaces/python/PythonInvoker.cpp 2020-04-26 18:45:41.779608311 -0500
+@@ -45,6 +45,14 @@
+ #include "platform/linux/XTimeUtils.h"
+ #endif
+
++// clang-format off
++// This breaks fmt because of SEP define, don't include
++// before anything that includes logging
++#include <osdefs.h>
++// clang-format on
++
++#include <cassert>
++
+ #ifdef TARGET_WINDOWS
+ extern "C" FILE *fopen_utf8(const char *_Filename, const char *_Mode);
+ #else
+@@ -178,31 +186,47 @@
+ std::string scriptDir = URIUtils::GetDirectory(realFilename);
+ URIUtils::RemoveSlashAtEnd(scriptDir);
+
+- // get the global lock
+- extern PyThreadState* savestate;
+- PyEval_RestoreThread(savestate);
+- if (!m_threadState)
+- {
+- m_threadState = Py_NewInterpreter();
+- if (m_threadState == NULL)
+- {
+- PyEval_ReleaseLock();
+- CLog::Log(LOGERROR, "CPythonInvoker(%d, %s): FAILED to get thread
m_threadState!", GetId(), m_sourceFile.c_str());
+- return false;
++ // set m_threadState if it's not set.
++ PyThreadState* l_threadState = nullptr;
++ bool newInterp = false;
++ {
++ if (!m_threadState)
++ {
++ // TODO: Re-write everything.
++ // this is a TOTAL hack. We need the GIL but we need to borrow a PyThreadState in
order to get it
++ // as of Python 3.2 since PyEval_AcquireLock is deprecated
++ extern PyThreadState* savestate;
++ PyEval_RestoreThread(savestate);
++ l_threadState = Py_NewInterpreter();
++ PyEval_ReleaseThread(l_threadState);
++ if (l_threadState == NULL)
++ {
++ CLog::Log(LOGERROR, "CPythonInvoker(%d, %s): FAILED to get thread
m_threadState!", GetId(),
++ m_sourceFile.c_str());
++ return false;
++ }
++ newInterp = true;
+ }
+- // swap in my thread m_threadState
+- PyThreadState_Swap(m_threadState);
++ else
++ l_threadState = m_threadState;
++ }
+
+- m_languageHook = new
XBMCAddon::Python::PythonLanguageHook(m_threadState->interp);
++ // get the GIL
++ PyEval_RestoreThread(l_threadState);
++ if (newInterp)
++ {
++ m_languageHook = new
XBMCAddon::Python::PythonLanguageHook(l_threadState->interp);
+ m_languageHook->RegisterMe();
+
+ onInitialization();
+ setState(InvokerStateInitialized);
+
+ if (realFilename == m_sourceFile)
+- CLog::Log(LOGDEBUG, "CPythonInvoker(%d, %s): the source file to load is
\"%s\"", GetId(), m_sourceFile.c_str(), m_sourceFile.c_str());
++ CLog::Log(LOGDEBUG, "CPythonInvoker(%d, %s): the source file to load is
\"%s\"", GetId(),
++ m_sourceFile.c_str(), m_sourceFile.c_str());
+ else
+- CLog::Log(LOGDEBUG, "CPythonInvoker(%d, %s): the source file to load is
\"%s\" (\"%s\")", GetId(), m_sourceFile.c_str(),
m_sourceFile.c_str(), realFilename.c_str());
++ CLog::Log(LOGDEBUG, "CPythonInvoker(%d, %s): the source file to load is
\"%s\" (\"%s\")",
++ GetId(), m_sourceFile.c_str(), m_sourceFile.c_str(),
realFilename.c_str());
+
+ // get path from script file name and add python path's
+ // this is used for python so it will search modules from script path first
+@@ -213,15 +237,18 @@
+ {
+ std::set<std::string> paths;
+ getAddonModuleDeps(m_addon, paths);
+- for (std::set<std::string>::const_iterator it = paths.begin(); it !=
paths.end(); ++it)
+- addPath(*it);
++ for (const auto& it : paths)
++ addPath(it);
+ }
+ else
+ { // for backwards compatibility.
+ // we don't have any addon so just add all addon modules installed
+- CLog::Log(LOGWARNING, "CPythonInvoker(%d): Script invoked without an addon.
Adding all addon "
+- "modules installed to python path as fallback. This behaviour will be
removed in future "
+- "version.", GetId());
++ CLog::Log(
++ LOGWARNING,
++ "CPythonInvoker(%d): Script invoked without an addon. Adding all addon
"
++ "modules installed to python path as fallback. This behaviour will be
removed in future "
++ "version.",
++ GetId());
+ ADDON::VECADDONS addons;
+ CServiceBroker::GetAddonMgr().GetAddons(addons, ADDON::ADDON_SCRIPT_MODULE);
+ for (unsigned int i = 0; i < addons.size(); ++i)
+@@ -230,24 +257,24 @@
+
+ // we want to use sys.path so it includes site-packages
+ // if this fails, default to using Py_GetPath
+- PyObject *sysMod(PyImport_ImportModule("sys")); // must call Py_DECREF
when finished
+- PyObject *sysModDict(PyModule_GetDict(sysMod)); // borrowed ref, no need to delete
+- PyObject *pathObj(PyDict_GetItemString(sysModDict, "path")); // borrowed
ref, no need to delete
++ PyObject* sysMod(PyImport_ImportModule("sys")); // must call Py_DECREF
when finished
++ PyObject* sysModDict(PyModule_GetDict(sysMod)); // borrowed ref, no need to delete
++ PyObject* pathObj(PyDict_GetItemString(sysModDict, "path")); // borrowed
ref, no need to delete
+
+ if (pathObj != NULL && PyList_Check(pathObj))
+ {
+ for (int i = 0; i < PyList_Size(pathObj); i++)
+ {
+- PyObject *e = PyList_GetItem(pathObj, i); // borrowed ref, no need to delete
++ PyObject* e = PyList_GetItem(pathObj, i); // borrowed ref, no need to delete
+ if (e != NULL && PyUnicode_Check(e))
+- addNativePath(PyUnicode_AsUTF8(e)); // returns internal data, don't delete
or modify
++ addPath(PyUnicode_AsUTF8(e)); // returns internal data, don't delete or
modify
+ }
+ }
+ else
+ {
+ std::string GetPath;
+ g_charsetConverter.wToUTF8(Py_GetPath(), GetPath);
+- addNativePath(GetPath);
++ addPath(GetPath);
+ }
+
+ Py_DECREF(sysMod); // release ref to sysMod
+@@ -255,14 +282,21 @@
+ #ifdef TARGET_WINDOWS
+ std::string pyPathUtf8;
+ g_charsetConverter.systemToUtf8(m_pythonPath, pyPathUtf8, false);
+- CLog::Log(LOGDEBUG, "CPythonInvoker(%d, %s): setting the Python path to
%s", GetId(), m_sourceFile.c_str(), pyPathUtf8.c_str());
++ CLog::Log(LOGDEBUG, "CPythonInvoker(%d, %s): setting the Python path to
%s", GetId(),
++ m_sourceFile.c_str(), pyPathUtf8.c_str());
+ #else // ! TARGET_WINDOWS
+- CLog::Log(LOGDEBUG, "CPythonInvoker(%d, %s): setting the Python path to
%s", GetId(), m_sourceFile.c_str(), m_pythonPath.c_str());
++ CLog::Log(LOGDEBUG, "CPythonInvoker(%d, %s): setting the Python path to
%s", GetId(),
++ m_sourceFile.c_str(), m_pythonPath.c_str());
+ #endif // ! TARGET_WINDOWS
+
+ std::wstring pypath;
+ g_charsetConverter.utf8ToW(m_pythonPath, pypath);
+ PySys_SetPath(pypath.c_str());
++
++ { // set the m_threadState to this new interp
++ CSingleLock lockMe(m_critical);
++ m_threadState = l_threadState;
++ }
+ }
+ else
+ // swap in my thread m_threadState
+@@ -275,18 +309,13 @@
+ PyObject* module = PyImport_AddModule("__main__");
+ PyObject* moduleDict = PyModule_GetDict(module);
+
+- // when we are done initing we store thread m_threadState so we can be aborted
+- PyThreadState_Swap(NULL);
+- PyEval_ReleaseLock();
+-
+ // we need to check if we was asked to abort before we had inited
+ bool stopping = false;
+- { CSingleLock lock(m_critical);
++ {
++ GilSafeSingleLock lock(m_critical);
+ stopping = m_stop;
+ }
+
+- PyEval_AcquireThread((PyThreadState*)m_threadState);
+-
+ bool failed = false;
+ std::string exceptionType, exceptionValue, exceptionTraceback;
+ if (!stopping)
+@@ -305,11 +334,11 @@
+ return false;
+ }
+ #endif
+- FILE* fp = _Py_fopen(nativeFilename.c_str(), "r");
++ FILE* fp = _Py_fopen(nativeFilename.c_str(), "rb");
+
+ if (fp != NULL)
+ {
+- PyObject* f = PyUnicode_FromString(nativeFilename.c_str());
++ PyObject* f = PyUnicode_FromString(realFilename.c_str());
+ PyDict_SetItemString(moduleDict, "__file__", f);
+
+ onPythonModuleInitialization(moduleDict);
+@@ -317,7 +346,7 @@
+ Py_DECREF(f);
+ setState(InvokerStateRunning);
+ XBMCAddon::Python::PyContext pycontext; // this is a guard class that marks this
callstack as being in a python context
+- executeScript(fp, nativeFilename, module, moduleDict);
++ executeScript(fp, realFilename, moduleDict);
+ }
+ else
+ CLog::Log(LOGERROR, "CPythonInvoker(%d, %s): %s not found!", GetId(),
m_sourceFile.c_str(), m_sourceFile.c_str());
+@@ -381,14 +410,15 @@
+
+ if (m_threadState)
+ {
+- PyObject *m = PyImport_AddModule("xbmc");
++ PyObject* m = PyImport_AddModule("xbmc");
+ if (m == NULL || PyObject_SetAttrString(m, "abortRequested",
PyBool_FromLong(1)))
+- CLog::Log(LOGERROR, "CPythonInvoker(%d, %s): failed to set
abortRequested", GetId(), m_sourceFile.c_str());
++ CLog::Log(LOGERROR, "CPythonInvoker(%d, %s): failed to set
abortRequested", GetId(),
++ m_sourceFile.c_str());
+
+ // make sure all sub threads have finished
+- for (PyThreadState *old = nullptr; m_threadState != nullptr;)
++ for (PyThreadState* old = nullptr; m_threadState != nullptr;)
+ {
+- PyThreadState *s = m_threadState->interp->tstate_head;
++ PyThreadState* s = m_threadState->interp->tstate_head;
+ for (; s && s == m_threadState;)
+ s = s->next;
+
+@@ -397,7 +427,8 @@
+
+ if (old != s)
+ {
+- CLog::Log(LOGINFO, "CPythonInvoker(%d, %s): waiting on thread %"
PRIu64, GetId(), m_sourceFile.c_str(), (uint64_t)s->thread_id);
++ CLog::Log(LOGINFO, "CPythonInvoker(%d, %s): waiting on thread %"
PRIu64, GetId(),
++ m_sourceFile.c_str(), (uint64_t)s->thread_id);
+ old = s;
+ }
+
+@@ -412,21 +443,21 @@
+ // pending calls must be cleared out
+ XBMCAddon::RetardedAsyncCallbackHandler::clearPendingCalls(m_threadState);
+
+- PyThreadState_Swap(NULL);
+- PyEval_ReleaseLock();
++ assert(m_threadState != nullptr);
++ PyEval_ReleaseThread(m_threadState);
+
+ setState(stateToSet);
+
+ return true;
+ }
+
+-void CPythonInvoker::executeScript(void *fp, const std::string &script, void
*module, void *moduleDict)
++void CPythonInvoker::executeScript(FILE* fp, const std::string& script, PyObject*
moduleDict)
+ {
+- if (fp == NULL || script.empty() || module == NULL || moduleDict == NULL)
++ if (fp == NULL || script.empty() || moduleDict == NULL)
+ return;
+
+ int m_Py_file_input = Py_file_input;
+- PyRun_FileExFlags(static_cast<FILE*>(fp), script.c_str(), m_Py_file_input,
static_cast<PyObject*>(moduleDict), static_cast<PyObject*>(moduleDict), 1,
NULL);
++ PyRun_FileExFlags(fp, script.c_str(), m_Py_file_input, moduleDict, moduleDict, 1,
NULL);
+ }
+
+ FILE* CPythonInvoker::PyFile_AsFileWithMode(PyObject* py_file, const char* mode)
+@@ -463,11 +494,9 @@
+ if (IsRunning())
+ {
+ setState(InvokerStateStopping);
+-
+ lock.Leave();
+
+ PyEval_RestoreThread((PyThreadState*)m_threadState);
+- PyThreadState* old = PyThreadState_Swap((PyThreadState*)m_threadState);
+
+ //tell xbmc.Monitor to call onAbortRequested()
+ if (m_addon)
+@@ -481,8 +510,7 @@
+ if (m == NULL || PyObject_SetAttrString(m, "abortRequested",
PyBool_FromLong(1)))
+ CLog::Log(LOGERROR, "CPythonInvoker(%d, %s): failed to set
abortRequested", GetId(), m_sourceFile.c_str());
+
+- PyThreadState_Swap(old);
+- PyEval_ReleaseLock();
++ PyEval_ReleaseThread(m_threadState);
+ }
+ else
+ //Release the lock while waiting for threads to finish
+@@ -506,9 +534,6 @@
+ }
+ }
+
+- // grabbing the PyLock while holding the m_critical is asking for a deadlock
+- PyEval_RestoreThread((PyThreadState*)m_threadState);
+-
+ lock.Enter();
+
+ setState(InvokerStateExecutionDone);
+@@ -521,7 +546,12 @@
+ // so we need to recheck for m_threadState == NULL
+ if (m_threadState != NULL)
+ {
+- PyThreadState* old = PyThreadState_Swap((PyThreadState*)m_threadState);
++ {
++ // grabbing the PyLock while holding the m_critical is asking for a deadlock
++ CSingleExit ex2(m_critical);
++ PyEval_RestoreThread((PyThreadState*)m_threadState);
++ }
++
+ for (PyThreadState* state =
((PyThreadState*)m_threadState)->interp->tstate_head; state; state =
state->next)
+ {
+ // Raise a SystemExit exception in python threads
+@@ -529,14 +559,13 @@
+ state->async_exc = PyExc_SystemExit;
+ Py_XINCREF(state->async_exc);
+ }
+- PyThreadState_Swap(old);
+
+ // If a dialog entered its doModal(), we need to wake it to see the exception
+ pulseGlobalEvent();
+- m_threadState = nullptr;
++
++ PyEval_ReleaseThread(m_threadState);
+ }
+ lock.Leave();
+- PyEval_ReleaseLock();
+
+ setState(InvokerStateFailed);
+ }
+@@ -552,7 +581,7 @@
+ {
+ CLog::Log(LOGDEBUG, "%s(%d, %s)", __FUNCTION__, GetId(),
m_sourceFile.c_str());
+
+- PyEval_RestoreThread((PyThreadState*)m_threadState);
++ PyEval_RestoreThread(m_threadState);
+
+ onDeinitialization();
+
+--- a/xbmc/interfaces/python/PythonInvoker.h 2020-04-26 18:47:40.256796002 -0500
++++ b/xbmc/interfaces/python/PythonInvoker.h 2020-04-26 19:03:32.621042384 -0500
+@@ -36,7 +36,7 @@
+ protected:
+ // implementation of ILanguageInvoker
+ bool execute(const std::string &script, const std::vector<std::string>
&arguments) override;
+- virtual void executeScript(void *fp, const std::string &script, void *module, void
*moduleDict);
++ virtual void executeScript(FILE* fp, const std::string& script, PyObject*
moduleDict);
+ bool stop(bool abort) override;
+ void onExecutionDone() override;
+ void onExecutionFailed() override;
+--- a/xbmc/network/httprequesthandler/python/HTTPPythonWsgiInvoker.cpp 2020-04-26
19:04:43.817791635 -0500
++++ b/xbmc/network/httprequesthandler/python/HTTPPythonWsgiInvoker.cpp 2020-04-26
19:33:51.571751440 -0500
+@@ -113,10 +113,10 @@
+ return m_request;
+ }
+
+-void CHTTPPythonWsgiInvoker::executeScript(void *fp, const std::string &script, void
*module, void *moduleDict)
++void CHTTPPythonWsgiInvoker::executeScript(FILE* fp, const std::string& script,
PyObject* moduleDict)
+ {
+ if (m_request == NULL || m_addon == NULL || m_addon->Type() !=
ADDON::ADDON_WEB_INTERFACE ||
+- fp == NULL || script.empty() || module == NULL || moduleDict == NULL)
++ fp == NULL || script.empty() || moduleDict == NULL)
+ return;
+
+ ADDON::CWebinterface* webinterface =
static_cast<ADDON::CWebinterface*>(m_addon.get());
+--- a/xbmc/network/httprequesthandler/python/HTTPPythonWsgiInvoker.h 2020-02-29
04:56:32.000000000 -0600
++++ b/xbmc/network/httprequesthandler/python/HTTPPythonWsgiInvoker.h 2020-04-26
19:30:04.685477203 -0500
+@@ -34,7 +34,7 @@
+
+ protected:
+ // overrides of CPythonInvoker
+- void executeScript(void *fp, const std::string &script, void *module, void
*moduleDict) override;
++ void executeScript(FILE* fp, const std::string& script, PyObject* moduleDict)
override;
+ std::map<std::string, PythonModuleInitialization> getModules() const override;
+ const char* getInitializationScript() const override;
+
diff --git a/kodi.spec b/kodi.spec
index 8d45658..20508b4 100644
--- a/kodi.spec
+++ b/kodi.spec
@@ -34,7 +34,7 @@
Name: kodi
Version: 18.6
-Release: 2%{?dist}
+Release: 3%{?dist}
Summary: Media center
License: GPLv2+ and GPLv3+ and LGPLv2+ and BSD and MIT
@@ -79,12 +79,20 @@ Patch3: kodi-18-annobin-workaround.patch
#
https://github.com/xbmc/xbmc/issues/16560
Patch4: kodi-18-python3-0001.patch
Patch5: kodi-18-python3-0002.patch
+# apply latest git master work for Python 3 crashing fixes
+Patch6: kodi-18-python3-0003.patch
# Fix missing include (gcc requirement)
-Patch6: kodi-18-assert.patch
+Patch7: kodi-18-assert.patch
# Workaround for brp-mangle-shebangs behavior (RHBZ#1787088)
-Patch7: kodi-18-brp-mangle-shebangs.patch
+Patch8: kodi-18-brp-mangle-shebangs.patch
+
+# libfmt change fixed an issue that broke Kodi, both libfmt and Kodi fixed it, but we can
apply the Kodi-only fix
+#
https://github.com/fmtlib/fmt/issues/1620
+#
https://github.com/xbmc/xbmc/issues/17629
+#
https://github.com/xbmc/xbmc/pull/17683
+Patch9: kodi-18-libfmt.patch
%ifarch x86_64 i686
%global _with_crystalhd 1
@@ -372,10 +380,12 @@ This package contains the Kodi binary for X11 servers.
%if 0%{?fedora} > 31
%patch4 -p1 -b.python3-0001
%patch5 -p1 -b.python3-0002
+%patch6 -p1 -b.python3-0003
%endif
-%patch6 -p1 -b.assert
-%patch7 -p1 -b.brp-mangle-shebangs
+%patch7 -p1 -b.assert
+%patch8 -p1 -b.brp-mangle-shebangs
+%patch9 -p1 -b.libfmt
# Fix up Python shebangs
%if 0%{?fedora} > 31
@@ -556,6 +566,9 @@ rm -f ${RPM_BUILD_ROOT}%{_mandir}/man1/kodi-wiiremote.1
%changelog
+* Sun Apr 26 2020 Michael Cronenworth <mike(a)cchtml.com> - 18.6-3
+- Python 3 and libfmt fixes
+
* Fri Apr 10 2020 Leigh Scott <leigh123linux(a)gmail.com> - 18.6-2
- Rebuild for new libcdio version