[education/rkward] rkward/rbackend: Full proof of concept allowing to load libR.so via dlopen()
Thomas Friedrichsmeier
null at kde.org
Fri May 10 17:04:24 BST 2024
Git commit f99be88120cffd7f68403c5e9d96c2ec2119b4bc by Thomas Friedrichsmeier.
Committed on 05/05/2024 at 11:57.
Pushed by tfry into branch 'master'.
Full proof of concept allowing to load libR.so via dlopen()
M +11 -2 rkward/rbackend/CMakeLists.txt
M +12 -8 rkward/rbackend/rkrapi.cpp
M +2 -3 rkward/rbackend/rkrapi.h
M +23 -5 rkward/rbackend/rkrbackendprotocol_backend.cpp
M +3 -0 rkward/rbackend/rkwarddevice/CMakeLists.txt
M +1 -1 rkward/rbackend/rkwarddevice/rkgraphicsdevice_stubs.cpp
https://invent.kde.org/education/rkward/-/commit/f99be88120cffd7f68403c5e9d96c2ec2119b4bc
diff --git a/rkward/rbackend/CMakeLists.txt b/rkward/rbackend/CMakeLists.txt
index dbca2a3b7..98342bee4 100644
--- a/rkward/rbackend/CMakeLists.txt
+++ b/rkward/rbackend/CMakeLists.txt
@@ -8,6 +8,7 @@ IF(NOT WIN32)
FIND_PACKAGE(Threads)
ENDIF(NOT WIN32)
+SET(DLOPEN_RLIB 1) # TODO: Allow configuration
ADD_SUBDIRECTORY( rpackages )
ADD_SUBDIRECTORY( rkwarddevice )
@@ -44,12 +45,20 @@ SET (
ADD_LIBRARY(rbackend STATIC ${rbackend_frontend_SRCS} ${rbackend_shared_SRCS})
TARGET_LINK_LIBRARIES(rbackend rkgraphicsdevice.frontend Qt6::Widgets KF6::TextEditor KF6::WindowSystem)
+IF(${DLOPEN_RLIB})
+TARGET_COMPILE_DEFINITIONS(rbackend PUBLIC RK_DLOPEN_LIBRSO)
+ENDIF()
ADD_DEFINITIONS (-DRKWARD_BACKEND_PATH="${KDE_INSTALL_FULL_LIBEXECDIR}")
LINK_DIRECTORIES(${R_SHAREDLIBDIR})
+
ADD_EXECUTABLE(rkward.rbackend ${rbackend_backend_SRCS} ${rbackend_shared_SRCS})
ECM_MARK_NONGUI_EXECUTABLE(rkward.rbackend)
-TARGET_LINK_LIBRARIES(rkward.rbackend rkgraphicsdevice.backend ${R_USED_LIBS} ${CMAKE_THREAD_LIBS_INIT} Qt6::Network Qt6::Core KF6::I18n)
+TARGET_LINK_LIBRARIES(rkward.rbackend rkgraphicsdevice.backend ${CMAKE_THREAD_LIBS_INIT} Qt6::Network Qt6::Core KF6::I18n)
+IF(NOT ${DLOPEN_RLIB})
+TARGET_LINK_LIBRARIES(rkward.rbackend rkgraphicsdevice.backend ${R_USED_LIBS})
+ENDIF()
+
IF(WIN32)
# 64MB stack size is what R itself is build with on Windows, while by default the stack size would be 1MB
# Not sure, if 64MB is actually needed, but 1MB does cause trouble e.g. with some shiny apps.
@@ -76,7 +85,7 @@ ADD_DEPENDENCIES(rkward.rbackend R_lib)
ENDIF(MSVC)
IF(WIN32)
- # on Widows, we install to the rbackend subdirectory, because 1) LIBEXEC_INSTALL_DIR == BIN_INSTALL_DIR and 2) we don't want the backend to pick up
+ # on Windows, we install to the rbackend subdirectory, because 1) LIBEXEC_INSTALL_DIR == BIN_INSTALL_DIR and 2) we don't want the backend to pick up
# all the KDE library versions, first, when loading DLLs
INSTALL(TARGETS rkward.rbackend DESTINATION ${KDE_INSTALL_BINDIR}/rbackend)
ELSE(WIN32)
diff --git a/rkward/rbackend/rkrapi.cpp b/rkward/rbackend/rkrapi.cpp
index 48aa14587..50a6ec515 100644
--- a/rkward/rbackend/rkrapi.cpp
+++ b/rkward/rbackend/rkrapi.cpp
@@ -7,16 +7,20 @@ SPDX-License-Identifier: GPL-2.0-or-later
#include "rkrapi.h"
+#include <dlfcn.h>
+
+#include "../debug.h"
+
void RFn::init(void *dllinfo) {
// TODO: This is just proof/test of concept code!
- auto dummy_rfn = new RFn();
- auto meta = dummy_rfn->metaObject();
- for (int i = 0; i < meta->propertyCount(); ++i) {
- auto prop = meta->property(i+meta->propertyOffset());
+ auto rfn = new RFn(); // we need a dummy object, even if we are only interested in the static members
+ auto meta = rfn->metaObject();
+ RK_DEBUG(RBACKEND, DL_DEBUG, "Loading %d symbols from R lib %p", meta->propertyCount() - meta->propertyOffset(), dllinfo);
+ for (int i = meta->propertyOffset(); i < meta->propertyCount(); ++i) {
+ auto prop = meta->property(i);
auto name = prop.name();
- //RFn::Rf_warning("%d %s %d %d", i, name, prop.isReadable(), prop.isWritable());
- //RFn::Rf_warning("%d", prop.write(dummy_rfn, prop.read(dummy_rfn))); //dummy_rfn->setProperty(name, QVariant::fromValue(nullptr))); // NOTE: Qt refuses to write nullptr as value!
+ auto symb = dlsym(dllinfo, name);
+ RK_DEBUG(RBACKEND, DL_DEBUG, "Lookup of symbol %s in %p: %p", name, dllinfo, symb);
+ prop.write(rfn, QVariant::fromValue(symb)); // NOTE: Qt refuses to write nullptr as value, but that's already the initial value of each member
}
- dummy_rfn->setProperty("Rf_warning", QVariant::fromValue((void*) RFn::Rf_error));
- RFn::Rf_warning("warning as error");
}
diff --git a/rkward/rbackend/rkrapi.h b/rkward/rbackend/rkrapi.h
index 9a4260b96..7693e2cb7 100644
--- a/rkward/rbackend/rkrapi.h
+++ b/rkward/rbackend/rkrapi.h
@@ -76,14 +76,12 @@ extern "C" {
// some functions we need that are not declared
extern "C" void run_Rmainloop(void);
-#define RK_DLOPEN_LIBRSO
#ifdef RK_DLOPEN_LIBRSO
-
// Using Qt Meta-Property system for introspection, in order to automate the dlsym-calls.
// Only the _set ## X function is actually used (for initialization). The _get ## X functions are just to keep the MOC happy.
// The actual access happens directly via the member (X)
#define IMPORT_R_API(X) Q_PROPERTY(void* X READ _get ## X WRITE _set ## X) \
- public: static inline decltype(::X) *X = &::X; \
+ public: static inline decltype(::X) *X = nullptr; \
void _set ## X (void* v) { X = (decltype(X)) v; } \
void* _get ## X () { return (void*) X; }
#define ROb(X) *(RFn::X)
@@ -289,6 +287,7 @@ IMPORT_R_API(UserBreak);
IMPORT_R_API(R_GE_clipPathFillRule);
IMPORT_R_API(R_GE_maskType);
#endif
+
public:
static void init(void* dllinfo);
};
diff --git a/rkward/rbackend/rkrbackendprotocol_backend.cpp b/rkward/rbackend/rkrbackendprotocol_backend.cpp
index ee5abde4d..2314c3032 100644
--- a/rkward/rbackend/rkrbackendprotocol_backend.cpp
+++ b/rkward/rbackend/rkrbackendprotocol_backend.cpp
@@ -1,6 +1,6 @@
/*
rkrbackendprotocol - This file is part of RKWard (https://rkward.kde.org). Created: Thu Nov 04 2010
-SPDX-FileCopyrightText: 2010-2013 by Thomas Friedrichsmeier <thomas.friedrichsmeier at kdemail.net>
+SPDX-FileCopyrightText: 2010-2024 by Thomas Friedrichsmeier <thomas.friedrichsmeier at kdemail.net>
SPDX-FileContributor: The RKWard Team <rkward-devel at kde.org>
SPDX-License-Identifier: GPL-2.0-or-later
*/
@@ -15,14 +15,16 @@ SPDX-License-Identifier: GPL-2.0-or-later
#include <QThread>
#include <QLocalSocket>
#include <QMutex>
+#include <QUuid> // mis-used as a random-string generator
+#include <QDir>
+#include <QUrl>
-#include "rktransmitter.h"
#include <iostream>
+#include <dlfcn.h>
#include "rkbackendtransmitter.h"
-#include <QUuid> // mis-used as a random-string generator
-#include <QDir>
-#include <QUrl>
+#include "rktransmitter.h"
+#include "rkrapi.h"
#ifdef Q_OS_MACOS
#include <CoreFoundation/CoreFoundation.h>
@@ -109,6 +111,22 @@ SPDX-License-Identifier: GPL-2.0-or-later
// this token is sent both via stdout and the local socket connection. The frontend simply compares both values.
QString token = QUuid::createUuid ().toString ();
+ // TODO: Should rather take the libname from CMake
+ // maybe we also want to accept an absolute path specified on command line from the frontend
+#ifdef RK_DLOPEN_LIBRSO
+#ifdef Q_OS_WIN
+# define RLIBNAME "R.dll"
+#else
+# define RLIBNAME "libR.so"
+#endif
+//#if defined(RTLD_DEEPBIND)
+// RK_DEBUG(RBACKEND, DL_DEBUG, "Now loading R lib, dynamically (deepbind)");
+// RFn::init(dlopen(RLIBNAME, RTLD_NOW | RTLD_DEEPBIND));
+//#else
+ RK_DEBUG(RBACKEND, DL_DEBUG, "Now loading R lib, dynamically (local)");
+ RFn::init(dlopen(RLIBNAME, RTLD_NOW | RTLD_LOCAL));
+//#endif
+#endif
RKRBackendTransmitter transmitter (servername, token);
RKRBackendProtocolBackend::p_transmitter = &transmitter;
RKRBackendProtocolBackend backend (data_dir, rkd_server_name);
diff --git a/rkward/rbackend/rkwarddevice/CMakeLists.txt b/rkward/rbackend/rkwarddevice/CMakeLists.txt
index a5f7000fa..90687e278 100644
--- a/rkward/rbackend/rkwarddevice/CMakeLists.txt
+++ b/rkward/rbackend/rkwarddevice/CMakeLists.txt
@@ -22,3 +22,6 @@ TARGET_LINK_LIBRARIES(rkgraphicsdevice.frontend Qt6::Widgets Qt6::Core Qt6::Netw
ADD_LIBRARY(rkgraphicsdevice.backend STATIC ${rkgraphicsdevice_backend_SRCS})
TARGET_LINK_LIBRARIES(rkgraphicsdevice.backend Qt6::Core Qt6::Network)
+IF(${DLOPEN_RLIB})
+TARGET_COMPILE_DEFINITIONS(rkgraphicsdevice.backend PUBLIC RK_DLOPEN_LIBRSO)
+ENDIF()
diff --git a/rkward/rbackend/rkwarddevice/rkgraphicsdevice_stubs.cpp b/rkward/rbackend/rkwarddevice/rkgraphicsdevice_stubs.cpp
index 93569fc7b..06467ad46 100644
--- a/rkward/rbackend/rkwarddevice/rkgraphicsdevice_stubs.cpp
+++ b/rkward/rbackend/rkwarddevice/rkgraphicsdevice_stubs.cpp
@@ -872,7 +872,7 @@ void RKD_UseGroup(SEXP ref, SEXP trans, pDevDesc dev) {
bool have_trans = (trans != ROb(R_NilValue));
double matrix[6];
if (have_trans) {
- for (int i = 0; i < 6; ++i) matrix[i] = REAL(trans)[i]; // order in cairo terms: xx, xy, x0, yx, yy, y0
+ for (int i = 0; i < 6; ++i) matrix[i] = RFn::REAL(trans)[i]; // order in cairo terms: xx, xy, x0, yx, yy, y0
}
{
More information about the rkward-tracker
mailing list