ACloudViewer  3.9.4
A Modern Library for 3D Data Processing
load_plugins.cpp
Go to the documentation of this file.
1 // ----------------------------------------------------------------------------
2 // - CloudViewer: www.cloudViewer.org -
3 // ----------------------------------------------------------------------------
4 // Copyright (c) 2018-2024 www.cloudViewer.org
5 // SPDX-License-Identifier: MIT
6 // ----------------------------------------------------------------------------
7 
8 #include <QCoreApplication>
9 #include <QDir>
10 #include <QDirIterator>
11 #include <QString>
12 #include <QtGlobal>
13 
14 #include "casters.h"
15 
16 #include <pybind11/numpy.h>
17 #include <pybind11/stl_bind.h>
18 
19 namespace py = pybind11;
20 
21 #if defined(Q_OS_WIN32)
22 const char *MODULE_EXTENSION = ".pyd";
23 
24 QString getPluginsWrappersPath()
25 {
26 #ifdef PYCC_STAND_ALONE
27  py::module_ sysconfig = py::module_::import("sysconfig");
28  const QString site_packages = sysconfig.attr("get_path")("purelib").cast<QString>();
29  return QString("%1\\pycc\\plugins-python").arg(site_packages);
30 #else
31  QDir appDir = QCoreApplication::applicationDirPath();
32  QString pluginPath = QString("%1\\plugins-python").arg(appDir.absolutePath());
33  return pluginPath;
34 #endif
35 }
36 #elif defined(Q_OS_MACOS)
37 const char *MODULE_EXTENSION = ".so";
38 
39 QString getPluginsWrappersPath()
40 {
41 #ifdef PYCC_STAND_ALONE
42  py::module_ sys = py::module_::import("sys");
43  const QString envPrefix = sys.attr("prefix").cast<QString>();
44  QString pluginPath = QString("%1/lib/plugins-python").arg(envPrefix);
45 #else
46  // This point to $InstallFolder/ACloudViewer.app/Contents/MacOS
47  // We store python plugin wrappers in
48  // $InstallFolder/ACloudViewer.app/Contents/cvPythonPlugins
49  QDir appDir = QCoreApplication::applicationDirPath();
50  appDir.cdUp();
51  QString pluginPath = QString("%1/cvPythonPlugins").arg(appDir.absolutePath());
52 #endif
53  return pluginPath;
54 }
55 #else
56 const char *MODULE_EXTENSION = ".so";
57 
58 QString guessPlatlibdir()
59 {
60 #if PY_MAJOR_VERSION >= 3 && PY_MINOR_VERSION >= 9
61  // TODO it might be interesting to get the lib64 / lib from a config.h generated by cmake
62  // instead of calling python
63  py::module_ sys = py::module_::import("sys");
64  const QString platlibdir = sys.attr("platlibdir").cast<QString>();
65 #else
66  const QString platlibdir = "lib";
67 #endif
68  return platlibdir;
69 }
70 
72 {
73 #ifdef PYCC_STAND_ALONE
74  // We put the python wrapper in $venvPrefix/lib(64)/plugins-python
75  py::module_ sys = py::module_::import("sys");
76  const QString envPrefix = sys.attr("prefix").cast<QString>();
77  const QString platlibdir = guessPlatlibdir();
78  QString pluginPath = QString("%1/%2/plugins-python").arg(envPrefix, platlibdir);
79 #else // PYCC_STAND_ALONE
80 
81  QDir theDir = QCoreApplication::applicationDirPath();
82  if (theDir.dirName() == "bin")
83  {
84  theDir.cdUp();
85  }
86 
87  QString pluginPath = QString("%1/plugins-python").arg(theDir.absolutePath());
88 #endif // PYCC_STAND_ALONE
89  return pluginPath;
90 }
91 #endif // Q_OS_WIN32
92 
93 void load_pluginWrappers(py::module_ &m)
94 {
95  py::module_ pluginWrappers = m.def_submodule("plugins");
96  pluginWrappers.doc() = "Module we wrap around some ACloudViewer plugins";
97 
98  const QString pluginPath = getPluginsWrappersPath();
99 
100  QDir pluginPathDir(pluginPath);
101  if (!pluginPathDir.exists())
102  {
103  return;
104  }
105 
106  py::print(
107  "[PythonRuntime] Start load plugin python wrapper from: '", pluginPath.toStdString(), "'");
108  py::module_::import("sys").attr("path").attr("append")(pluginPath);
109  QDirIterator iter(pluginPathDir);
110  while (iter.hasNext())
111  {
112  iter.next();
113  QFileInfo entry = iter.fileInfo();
114  QString fileName = entry.fileName();
115 
116  if (!fileName.endsWith(MODULE_EXTENSION))
117  {
118  continue;
119  }
120 
121  // Our wrappers are wheels with name like "pluginName.cpython.etc.etc.so"
122  const std::string stdFileName = fileName.split('.')[0].toStdString();
123  try
124  {
125  const py::module mod = py::module_::import(stdFileName.c_str());
126  const std::string pluginName = mod.attr("plugin_name").cast<std::string>();
127  pluginWrappers.attr(pluginName.c_str()) = mod;
128  py::print("[PythonRuntime] Plugin python wrapper found: '", pluginName, "'");
129  }
130  catch (const std::exception &e)
131  {
132  py::print("Failed to load plugin '", stdFileName, "': ", e.what());
133  continue;
134  }
135  }
136 }
QString getPluginsWrappersPath()
void load_pluginWrappers(py::module_ &m)
QString guessPlatlibdir()
const char * MODULE_EXTENSION