安装Xcode
官网,或Apple Store。
安装Homebrew
下载源码
git clone --recursive https://github.com/uglide/RedisDesktopManager.git -b 2020 rdm && cd ./rdm
拷贝Info.plist
cd ./src && cp ./resources/Info.plist.sample ./resources/Info.plist
安装依赖
brew install openssl cmake
注意:python版本为3.7。
#若python3版本为3.8的话,则先解除关联。
brew unlink python@3.8
brew install python@3.7
brew link --force python@3.7
安装python依赖。
pip3 install -t ../bin/osx/release -r py/requirements.txt
安装Qt
我这里安装的是5.13版本,可以从清华大学镜像站进行下载。
注意:Qt在安装过程中一定要添加Qt Charts模块。
使用Qt Createor打开./src/rdm.pro。
使用Release方式构建。
打包.app文件。

关闭下载最新版本提示。

设置中文。

构建。

在/Users/<用户名>/rdm/bin/osx/debug查看Redis Desktop Manager.app。
关联Qt依赖。
cd /Users/<用户名>/rdm/bin/osx/debug
/Users/<用户名>/Qt5.13.2/5.13.2/clang_64/bin/macdeployqt Redis\ Desktop\ Manager.app -qmldir=/Users/<用户名>/rdm/src/qml
此处Qt安装路径使用的是默认路径。
结果:
File exists, skip copy: "Redis Desktop Manager.app/Contents/PlugIns/platforms/libqcocoa.dylib"
File exists, skip copy: "Redis Desktop Manager.app/Contents/PlugIns/printsupport/libcocoaprintersupport.dylib"
File exists, skip copy: "Redis Desktop Manager.app/Contents/PlugIns/styles/libqmacstyle.dylib"
File exists, skip copy: "Redis Desktop Manager.app/Contents/PlugIns/bearer/libqgenericbearer.dylib"
File exists, skip copy: "Redis Desktop Manager.app/Contents/PlugIns/imageformats/libqgif.dylib"
File exists, skip copy: "Redis Desktop Manager.app/Contents/PlugIns/imageformats/libqicns.dylib"
File exists, skip copy: "Redis Desktop Manager.app/Contents/PlugIns/imageformats/libqico.dylib"
File exists, skip copy: "Redis Desktop Manager.app/Contents/PlugIns/imageformats/libqjpeg.dylib"
File exists, skip copy: "Redis Desktop Manager.app/Contents/PlugIns/imageformats/libqmacheif.dylib"
File exists, skip copy: "Redis Desktop Manager.app/Contents/PlugIns/imageformats/libqmacjp2.dylib"
File exists, skip copy: "Redis Desktop Manager.app/Contents/PlugIns/imageformats/libqsvg.dylib"
File exists, skip copy: "Redis Desktop Manager.app/Contents/PlugIns/imageformats/libqtga.dylib"
File exists, skip copy: "Redis Desktop Manager.app/Contents/PlugIns/imageformats/libqtiff.dylib"
File exists, skip copy: "Redis Desktop Manager.app/Contents/PlugIns/imageformats/libqwbmp.dylib"
File exists, skip copy: "Redis Desktop Manager.app/Contents/PlugIns/imageformats/libqwebp.dylib"
File exists, skip copy: "Redis Desktop Manager.app/Contents/PlugIns/iconengines/libqsvgicon.dylib"
File exists, skip copy: "Redis Desktop Manager.app/Contents/PlugIns/virtualkeyboard/libqtvirtualkeyboard_hangul.dylib"
File exists, skip copy: "Redis Desktop Manager.app/Contents/PlugIns/virtualkeyboard/libqtvirtualkeyboard_openwnn.dylib"
File exists, skip copy: "Redis Desktop Manager.app/Contents/PlugIns/virtualkeyboard/libqtvirtualkeyboard_pinyin.dylib"
File exists, skip copy: "Redis Desktop Manager.app/Contents/PlugIns/virtualkeyboard/libqtvirtualkeyboard_tcime.dylib"
File exists, skip copy: "Redis Desktop Manager.app/Contents/PlugIns/virtualkeyboard/libqtvirtualkeyboard_thai.dylib"
File exists, skip copy: "Redis Desktop Manager.app/Contents/PlugIns/platforminputcontexts/libqtvirtualkeyboardplugin.dylib"
WARNING:
WARNING: "Redis Desktop Manager.app/Contents/Resources/qt.conf" already exists, will not overwrite.
WARNING: To make sure the plugins are loaded from the correct location,
WARNING: please make sure qt.conf contains the following lines:
WARNING: [Paths]
WARNING: Plugins = PlugIns
打包dmg
mkdir RedisDesktopManager
cp Redis\ Desktop\ Manager.app RedisDesktopManager
cd RedisDesktopManager
ln -s /Applications/ Applications
打开macOS上的磁盘工具。

选择刚才创建的文件夹(RedisDesktopManager)。
结果:
遇到的坑
python版本错误
参照官网安装的是python3.8,结果报错。
Undefined symbols for architecture x86_64:
"_PyBool_FromLong", referenced from:
PyObjectConverter::fromBoolean(bool) in qpython.o
_object* convert<QVariant, _object*, QVariantConverter, PyObjectConverter>(QVariant) in qpython.o
PyObjectConverter::fromBoolean(bool) in qpython_priv.o
_object* convert<QVariant, _object*, QVariantConverter, PyObjectConverter>(QVariant) in qpython_priv.o
"_PyBool_Type", referenced from:
PyObjectConverter::type(_object* const&) in qpython.o
PyObjectConverter::type(_object* const&) in qpython_priv.o
"_PyByteArray_FromStringAndSize", referenced from:
pyotherside_qrc_get_file_contents(_object*, _object*) in qpython_priv.o
"_PyBytes_AsString", referenced from:
QVariant convert<_object*, QVariant, PyObjectConverter, QVariantConverter>(_object*) in qpython.o
PyObjectConverter::string(_object*&) in qpython.o
PyObjectConverter::bytes(_object*&) in qpython.o
qstring_from_pyobject_arg(_object*) in qpython_priv.o
PyObjectConverter::string(_object*&) in qpython_priv.o
PyObjectConverter::bytes(_object*&) in qpython_priv.o
QVariant convert<_object*, QVariant, PyObjectConverter, QVariantConverter>(_object*) in qpython_priv.o
...
"_PyBytes_FromStringAndSize", referenced from:
PyObjectConverter::fromBytes(QByteArray const&) in qpython.o
_object* convert<QVariant, _object*, QVariantConverter, PyObjectConverter>(QVariant) in qpython.o
PyObjectConverter::fromBytes(QByteArray const&) in qpython_priv.o
_object* convert<QVariant, _object*, QVariantConverter, PyObjectConverter>(QVariant) in qpython_priv.o
"_PyBytes_Size", referenced from:
QVariant convert<_object*, QVariant, PyObjectConverter, QVariantConverter>(_object*) in qpython.o
PyObjectConverter::bytes(_object*&) in qpython.o
PyObjectConverter::bytes(_object*&) in qpython_priv.o
QVariant convert<_object*, QVariant, PyObjectConverter, QVariantConverter>(_object*) in qpython_priv.o
"_PyCallable_Check", referenced from:
QPythonPriv::call(_object*, QString, QVariant, QVariant*) in qpython_priv.o
"_PyCapsule_Import", referenced from:
QVariant convert<_object*, QVariant, PyObjectConverter, QVariantConverter>(_object*) in qpython.o
_object* convert<QVariant, _object*, QVariantConverter, PyObjectConverter>(QVariant) in qpython.o
qstring_from_pyobject_arg(_object*) in qpython_priv.o
_object* convert<QVariant, _object*, QVariantConverter, PyObjectConverter>(QVariant) in qpython_priv.o
QVariant convert<_object*, QVariant, PyObjectConverter, QVariantConverter>(_object*) in qpython_priv.o
"_PyDict_GetItemString", referenced from:
QPythonPriv::QPythonPriv() in qpython_priv.o
"_PyDict_New", referenced from:
PyObjectConverter::newDict() in qpython.o
_object* convert<QVariant, _object*, QVariantConverter, PyObjectConverter>(QVariant) in qpython.o
QPythonPriv::QPythonPriv() in qpython_priv.o
PyObjectConverter::newDict() in qpython_priv.o
_object* convert<QVariant, _object*, QVariantConverter, PyObjectConverter>(QVariant) in qpython_priv.o
"_PyDict_Next", referenced from:
PyObjectDictIterator::next(_object**, _object**) in qpython.o
PyObjectDictIterator::next(_object**, _object**) in qpython_priv.o
"_PyDict_SetItem", referenced from:
PyObjectDictBuilder::set(_object*, _object*) in qpython.o
PyObjectDictBuilder::set(_object*, _object*) in qpython_priv.o
"_PyDict_SetItemString", referenced from:
QPython::importNames_sync(QString, QVariant) in qpython.o
QPython::importModule_sync(QString) in qpython.o
QPythonPriv::QPythonPriv() in qpython_priv.o
"_PyErr_Clear", referenced from:
QPythonPriv::importFromQRC(char const*, QString const&) in qpython_priv.o
"_PyErr_Fetch", referenced from:
QPythonPriv::formatExc() in qpython_priv.o
"_PyErr_Format", referenced from:
pyotherside_QObject_repr(_object*) in qpython_priv.o
pyotherside_QObject_getattro(_object*, _object*) in qpython_priv.o
pyotherside_QObject_setattro(_object*, _object*, _object*) in qpython_priv.o
pyotherside_QObjectMethod_repr(_object*) in qpython_priv.o
pyotherside_QObjectMethod_call(_object*, _object*, _object*) in qpython_priv.o
"_PyErr_NormalizeException", referenced from:
QPythonPriv::formatExc() in qpython_priv.o
"_PyErr_Occurred", referenced from:
PyObjectListIterator::~PyObjectListIterator() in qpython.o
PyObjectListIterator::~PyObjectListIterator() in qpython.o
PyObjectListIterator::~PyObjectListIterator() in qpython_priv.o
PyObjectListIterator::~PyObjectListIterator() in qpython_priv.o
"_PyErr_Print", referenced from:
QPythonPriv::formatExc() in qpython_priv.o
"_PyErr_SetString", referenced from:
qstring_from_pyobject_arg(_object*) in qpython_priv.o
pyotherside_qrc_get_file_contents(_object*, _object*) in qpython_priv.o
pyotherside_qrc_list_dir(_object*, _object*) in qpython_priv.o
"_PyEval_GetBuiltins", referenced from:
QPythonPriv::QPythonPriv() in qpython_priv.o
"_PyEval_InitThreads", referenced from:
QPythonPriv::QPythonPriv() in qpython_priv.o
"_PyEval_RestoreThread", referenced from:
QPythonPriv::~QPythonPriv() in qpython_priv.o
"_PyEval_SaveThread", referenced from:
QPythonPriv::QPythonPriv() in qpython_priv.o
"_PyExc_AttributeError", referenced from:
pyotherside_QObject_getattro(_object*, _object*) in qpython_priv.o
pyotherside_QObject_setattro(_object*, _object*, _object*) in qpython_priv.o
"_PyExc_ReferenceError", referenced from:
pyotherside_QObject_getattro(_object*, _object*) in qpython_priv.o
pyotherside_QObject_setattro(_object*, _object*, _object*) in qpython_priv.o
pyotherside_QObjectMethod_call(_object*, _object*, _object*) in qpython_priv.o
"_PyExc_RuntimeError", referenced from:
pyotherside_QObjectMethod_call(_object*, _object*, _object*) in qpython_priv.o
"_PyExc_TypeError", referenced from:
pyotherside_QObject_repr(_object*) in qpython_priv.o
pyotherside_QObject_getattro(_object*, _object*) in qpython_priv.o
pyotherside_QObject_setattro(_object*, _object*, _object*) in qpython_priv.o
pyotherside_QObjectMethod_repr(_object*) in qpython_priv.o
pyotherside_QObjectMethod_call(_object*, _object*, _object*) in qpython_priv.o
"_PyExc_ValueError", referenced from:
qstring_from_pyobject_arg(_object*) in qpython_priv.o
pyotherside_qrc_get_file_contents(_object*, _object*) in qpython_priv.o
pyotherside_qrc_list_dir(_object*, _object*) in qpython_priv.o
pyotherside_QObject_getattro(_object*, _object*) in qpython_priv.o
pyotherside_QObject_setattro(_object*, _object*, _object*) in qpython_priv.o
pyotherside_QObjectMethod_call(_object*, _object*, _object*) in qpython_priv.o
"_PyFloat_AsDouble", referenced from:
QVariant convert<_object*, QVariant, PyObjectConverter, QVariantConverter>(_object*) in qpython.o
PyObjectConverter::floating(_object*&) in qpython.o
PyObjectConverter::floating(_object*&) in qpython_priv.o
QVariant convert<_object*, QVariant, PyObjectConverter, QVariantConverter>(_object*) in qpython_priv.o
"_PyFloat_FromDouble", referenced from:
PyObjectConverter::fromFloating(double) in qpython.o
_object* convert<QVariant, _object*, QVariantConverter, PyObjectConverter>(QVariant) in qpython.o
PyObjectConverter::fromFloating(double) in qpython_priv.o
_object* convert<QVariant, _object*, QVariantConverter, PyObjectConverter>(QVariant) in qpython_priv.o
"_PyFloat_Type", referenced from:
PyObjectConverter::type(_object* const&) in qpython.o
PyObjectConverter::type(_object* const&) in qpython_priv.o
"_PyGILState_Ensure", referenced from:
QPython::addImportPath(QString) in qpython.o
QPython::importNames_sync(QString, QVariant) in qpython.o
QPython::importModule_sync(QString) in qpython.o
QPython::evaluate(QString) in qpython.o
QPython::call_internal(QVariant, QVariant, bool) in qpython.o
QPython::getattr(QVariant, QString) in qpython.o
QPython::pythonVersion() in qpython.o
...
"_PyGILState_Release", referenced from:
QPython::addImportPath(QString) in qpython.o
QPython::importNames_sync(QString, QVariant) in qpython.o
QPython::importModule_sync(QString) in qpython.o
QPython::evaluate(QString) in qpython.o
QPython::call_internal(QVariant, QVariant, bool) in qpython.o
QPython::getattr(QVariant, QString) in qpython.o
QPython::pythonVersion() in qpython.o
...
"_PyImport_AppendInittab", referenced from:
QPythonPriv::QPythonPriv() in qpython_priv.o
"_PyImport_ExecCodeModule", referenced from:
QPythonPriv::importFromQRC(char const*, QString const&) in qpython_priv.o
"_PyImport_ImportModule", referenced from:
QPython::importNames_sync(QString, QVariant) in qpython.o
QPython::importModule_sync(QString) in qpython.o
QPythonPriv::QPythonPriv() in qpython_priv.o
"_PyImport_ImportModuleLevel", referenced from:
QPython::importModule_sync(QString) in qpython.o
"_PyIter_Next", referenced from:
PyObjectListIterator::next(_object**) in qpython.o
PyObjectListIterator::next(_object**) in qpython_priv.o
"_PyList_Append", referenced from:
PyObjectListBuilder::append(_object*) in qpython.o
PyObjectListBuilder::append(_object*) in qpython_priv.o
"_PyList_AsTuple", referenced from:
QPythonPriv::call(_object*, QString, QVariant, QVariant*) in qpython_priv.o
"_PyList_Insert", referenced from:
QPython::addImportPath(QString) in qpython.o
"_PyList_New", referenced from:
QPython::importModule_sync(QString) in qpython.o
PyObjectConverter::newList() in qpython.o
_object* convert<QVariant, _object*, QVariantConverter, PyObjectConverter>(QVariant) in qpython.o
PyObjectConverter::newList() in qpython_priv.o
_object* convert<QVariant, _object*, QVariantConverter, PyObjectConverter>(QVariant) in qpython_priv.o
"_PyLong_AsLongLong", referenced from:
QVariant convert<_object*, QVariant, PyObjectConverter, QVariantConverter>(_object*) in qpython.o
PyObjectConverter::integer(_object*&) in qpython.o
PyObjectConverter::integer(_object*&) in qpython_priv.o
QVariant convert<_object*, QVariant, PyObjectConverter, QVariantConverter>(_object*) in qpython_priv.o
"_PyLong_FromLong", referenced from:
PyObjectConverter::fromInteger(long long) in qpython.o
_object* convert<QVariant, _object*, QVariantConverter, PyObjectConverter>(QVariant) in qpython.o
PyObjectConverter::fromInteger(long long) in qpython_priv.o
_object* convert<QVariant, _object*, QVariantConverter, PyObjectConverter>(QVariant) in qpython_priv.o
"_PyMapping_Check", referenced from:
pyotherside_QObjectMethod_call(_object*, _object*, _object*) in qpython_priv.o
QPythonPriv::importFromQRC(char const*, QString const&) in qpython_priv.o
"_PyMapping_GetItemString", referenced from:
QPythonPriv::importFromQRC(char const*, QString const&) in qpython_priv.o
"_PyMapping_Size", referenced from:
pyotherside_QObjectMethod_call(_object*, _object*, _object*) in qpython_priv.o
"_PyMem_RawFree", referenced from:
QPythonPriv::QPythonPriv() in qpython_priv.o
"_PyModule_AddIntConstant", referenced from:
_PyOtherSide_init in qpython_priv.o
"_PyModule_AddObject", referenced from:
_PyOtherSide_init in qpython_priv.o
"_PyModule_AddStringConstant", referenced from:
_PyOtherSide_init in qpython_priv.o
"_PyModule_Create2", referenced from:
_PyOtherSide_init in qpython_priv.o
"_PyObject_Call", referenced from:
QPythonPriv::closing() in qpython_priv.o
QPythonPriv::call(_object*, QString, QVariant, QVariant*) in qpython_priv.o
"_PyObject_CallMethod", referenced from:
QPythonPriv::formatExc() in qpython_priv.o
"_PyObject_GetAttrString", referenced from:
QPython::importNames_sync(QString, QVariant) in qpython.o
QPython::getattr(QVariant, QString) in qpython.o
"_PyObject_GetIter", referenced from:
QVariant convert<_object*, QVariant, PyObjectConverter, QVariantConverter>(_object*) in qpython.o
PyObjectConverter::list(_object*&) in qpython.o
PyObjectConverter::list(_object*&) in qpython_priv.o
QVariant convert<_object*, QVariant, PyObjectConverter, QVariantConverter>(_object*) in qpython_priv.o
"_PyObject_Repr", referenced from:
QPython::call_internal(QVariant, QVariant, bool) in qpython.o
"_PyObject_Str", referenced from:
QPythonPriv::formatExc() in qpython_priv.o
"_PyRun_StringFlags", referenced from:
QPythonPriv::eval(QString) in qpython_priv.o
"_PySet_Type", referenced from:
PyObjectConverter::type(_object* const&) in qpython.o
PyObjectConverter::type(_object* const&) in qpython_priv.o
"_PySys_GetObject", referenced from:
QPython::addImportPath(QString) in qpython.o
QPython::pythonVersion() in qpython.o
QPythonPriv::importFromQRC(char const*, QString const&) in qpython_priv.o
"_PySys_SetArgvEx", referenced from:
QPythonPriv::QPythonPriv() in qpython_priv.o
"_PyTuple_GetItem", referenced from:
QPython::pythonVersion() in qpython.o
"_PyTuple_New", referenced from:
QPythonPriv::closing() in qpython_priv.o
"_PyTuple_Size", referenced from:
QPython::pythonVersion() in qpython.o
"_PyType_GenericNew", referenced from:
_PyOtherSide_init in qpython_priv.o
"_PyType_IsSubtype", referenced from:
QVariant convert<_object*, QVariant, PyObjectConverter, QVariantConverter>(_object*) in qpython.o
PyObjectConverter::type(_object* const&) in qpython.o
PyObjectConverter::qObject(_object*&) in qpython.o
pyotherside_QObject_repr(_object*) in qpython_priv.o
pyotherside_QObject_getattro(_object*, _object*) in qpython_priv.o
pyotherside_QObject_setattro(_object*, _object*, _object*) in qpython_priv.o
pyotherside_QObjectMethod_repr(_object*) in qpython_priv.o
...
"_PyType_Ready", referenced from:
_PyOtherSide_init in qpython_priv.o
"_PyUnicode_AsUTF8String", referenced from:
QVariant convert<_object*, QVariant, PyObjectConverter, QVariantConverter>(_object*) in qpython.o
PyObjectConverter::string(_object*&) in qpython.o
qstring_from_pyobject_arg(_object*) in qpython_priv.o
PyObjectConverter::string(_object*&) in qpython_priv.o
QVariant convert<_object*, QVariant, PyObjectConverter, QVariantConverter>(_object*) in qpython_priv.o
"_PyUnicode_FromFormat", referenced from:
pyotherside_QObject_repr(_object*) in qpython_priv.o
pyotherside_QObjectMethod_repr(_object*) in qpython_priv.o
"_PyUnicode_FromString", referenced from:
QPython::addImportPath(QString) in qpython.o
PyObjectConverter::fromString(char const*) in qpython.o
_object* convert<QVariant, _object*, QVariantConverter, PyObjectConverter>(QVariant) in qpython.o
QPythonPriv::formatExc() in qpython_priv.o
PyObjectConverter::fromString(char const*) in qpython_priv.o
_object* convert<QVariant, _object*, QVariantConverter, PyObjectConverter>(QVariant) in qpython_priv.o
"_PyUnicode_Join", referenced from:
QPythonPriv::formatExc() in qpython_priv.o
"_Py_CompileStringExFlags", referenced from:
QPythonPriv::importFromQRC(char const*, QString const&) in qpython_priv.o
"_Py_DecodeLocale", referenced from:
QPythonPriv::QPythonPriv() in qpython_priv.o
"_Py_Finalize", referenced from:
QPythonPriv::~QPythonPriv() in qpython_priv.o
"_Py_InitializeEx", referenced from:
QPythonPriv::QPythonPriv() in qpython_priv.o
"__PyObject_New", referenced from:
PyObjectConverter::fromQObject(QObjectRef const&) in qpython.o
_object* convert<QVariant, _object*, QVariantConverter, PyObjectConverter>(QVariant) in qpython.o
pyotherside_QObject_getattro(_object*, _object*) in qpython_priv.o
PyObjectConverter::fromQObject(QObjectRef const&) in qpython_priv.o
_object* convert<QVariant, _object*, QVariantConverter, PyObjectConverter>(QVariant) in qpython_priv.o
"__PyObject_NextNotImplemented", referenced from:
PyObjectConverter::type(_object* const&) in qpython.o
PyObjectConverter::type(_object* const&) in qpython_priv.o
"__Py_Dealloc", referenced from:
QVariant convert<_object*, QVariant, PyObjectConverter, QVariantConverter>(_object*) in qpython.o
PyObjectConverter::string(_object*&) in qpython.o
PyObjectConverter::~PyObjectConverter() in qpython.o
PyObjectConverter::~PyObjectConverter() in qpython.o
PyObjectListBuilder::append(_object*) in qpython.o
PyObjectDictBuilder::set(_object*, _object*) in qpython.o
PyObjectListIterator::~PyObjectListIterator() in qpython.o
...
"__Py_FalseStruct", referenced from:
pyotherside_qrc_is_file(_object*, _object*) in qpython_priv.o
pyotherside_qrc_is_dir(_object*, _object*) in qpython_priv.o
"__Py_NoneStruct", referenced from:
PyObjectConverter::type(_object* const&) in qpython.o
PyObjectConverter::fromTime(ConverterTime) in qpython.o
PyObjectConverter::fromDateTime(ConverterDateTime) in qpython.o
PyObjectConverter::none() in qpython.o
_object* convert<QVariant, _object*, QVariantConverter, PyObjectConverter>(QVariant) in qpython.o
pyotherside_send(_object*, _object*) in qpython_priv.o
pyotherside_atexit(_object*, _object*) in qpython_priv.o
...
"__Py_TrueStruct", referenced from:
QVariant convert<_object*, QVariant, PyObjectConverter, QVariantConverter>(_object*) in qpython.o
PyObjectConverter::boolean(_object*&) in qpython.o
pyotherside_qrc_is_file(_object*, _object*) in qpython_priv.o
pyotherside_qrc_is_dir(_object*, _object*) in qpython_priv.o
PyObjectConverter::boolean(_object*&) in qpython_priv.o
QVariant convert<_object*, QVariant, PyObjectConverter, QVariantConverter>(_object*) in qpython_priv.o
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
make: *** [../bin/osx/debug/Redis Desktop Manager] Error 1
07:21:43: 进程"/usr/bin/make"退出,退出代码 2 。
Error while building/deploying project rdm (kit: Desktop Qt 5.13.2 clang 64bit)
When executing step "Make"
解决方法:
brew unlink python@3.8
brew install python@3.7
brew link --force python@3.7