summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--guix-science/packages/rstudio.scm23
-rw-r--r--patches/rstudio-server-multi-version/0001-handleClientInit-Store-R-versions-in-sessionInfo.patch75
-rw-r--r--patches/rstudio-server-multi-version/0002-sessionProcessConfig-Configure-R-version-from-active.patch76
-rw-r--r--patches/rstudio-server-multi-version/0003-NewProjectWizard-Unhide-version-selector-widget.patch27
-rw-r--r--patches/rstudio-server-multi-version/0004-handleConnection-Switch-R-version-when-switching-pro.patch88
-rw-r--r--patches/rstudio-server-multi-version/0005-Add-version-switcher-widget-to-toolbar.patch287
-rw-r--r--patches/rstudio-server-multi-version/0006-Look-at-.local-share-rstudio-r-versions-for-custom-R.patch92
-rw-r--r--patches/rstudio-server-multi-version/0007-detectRLocationsUsingR-Restore-R_HOME-at-the-end.patch36
8 files changed, 704 insertions, 0 deletions
diff --git a/guix-science/packages/rstudio.scm b/guix-science/packages/rstudio.scm
index e044e2f..d687c78 100644
--- a/guix-science/packages/rstudio.scm
+++ b/guix-science/packages/rstudio.scm
@@ -280,6 +280,29 @@ be run as a server, enabling multiple users to access the RStudio IDE using a
280web browser.") 280web browser.")
281 (license license:agpl3))) 281 (license license:agpl3)))
282 282
283(define-public rstudio-server-multi-version
284 (package
285 (inherit rstudio-server)
286 (name "rstudio-server-multi-version")
287 (source
288 (origin
289 (inherit (package-source rstudio-server))
290 (patches
291 (search-patches
292 "rstudio-server-1.4.1106-unbundle.patch"
293 "rstudio-server-1.4.1103-soci-searchpath.patch"
294 "patches/rstudio-server-multi-version/0001-handleClientInit-Store-R-versions-in-sessionInfo.patch"
295 "patches/rstudio-server-multi-version/0002-sessionProcessConfig-Configure-R-version-from-active.patch"
296 "patches/rstudio-server-multi-version/0003-NewProjectWizard-Unhide-version-selector-widget.patch"
297 "patches/rstudio-server-multi-version/0004-handleConnection-Switch-R-version-when-switching-pro.patch"
298 "patches/rstudio-server-multi-version/0005-Add-version-switcher-widget-to-toolbar.patch"
299 "patches/rstudio-server-multi-version/0006-Look-at-.local-share-rstudio-r-versions-for-custom-R.patch"
300 "patches/rstudio-server-multi-version/0007-detectRLocationsUsingR-Restore-R_HOME-at-the-end.patch"))))
301 (description "This fork of RStudio allows users to switch to
302different versions of R from the toolbar or project settings. R
303versions can be recorded in @file{/etc/rstudio/r-versions} and in the
304user's @file{~/.local/share/rstudio/r-versions}.")))
305
283(define-public rstudio 306(define-public rstudio
284 (package (inherit rstudio-server) 307 (package (inherit rstudio-server)
285 (name "rstudio") 308 (name "rstudio")
diff --git a/patches/rstudio-server-multi-version/0001-handleClientInit-Store-R-versions-in-sessionInfo.patch b/patches/rstudio-server-multi-version/0001-handleClientInit-Store-R-versions-in-sessionInfo.patch
new file mode 100644
index 0000000..7e64e06
--- /dev/null
+++ b/patches/rstudio-server-multi-version/0001-handleClientInit-Store-R-versions-in-sessionInfo.patch
@@ -0,0 +1,75 @@
1From 6a8348988fcd961a67d709aa37c5defd7159b27b Mon Sep 17 00:00:00 2001
2From: Ricardo Wurmus <rekado@elephly.net>
3Date: Fri, 18 Dec 2020 10:26:44 +0100
4Subject: [PATCH 1/7] handleClientInit: Store R versions in sessionInfo.
5
6This unlocks the multi-version feature needed to initialize version
7selection widgets.
8
9* src/cpp/session/SessionClientInit.cpp (handleClientInit): Read
10version entries from /etc/rstudio/r-versions via the RVersionsScanner.
11Add discovered entries to a JSON array at the sessionInfo object
12r_versions_info->available_r_versions.
13---
14 src/cpp/session/SessionClientInit.cpp | 31 ++++++++++++++++++++++-----
15 1 file changed, 26 insertions(+), 5 deletions(-)
16
17diff --git a/src/cpp/session/SessionClientInit.cpp b/src/cpp/session/SessionClientInit.cpp
18index e4b3b7dd71..52523fc896 100644
19--- a/src/cpp/session/SessionClientInit.cpp
20+++ b/src/cpp/session/SessionClientInit.cpp
21@@ -2,6 +2,7 @@
22 * SessionClientInit.hpp
23 *
24 * Copyright (C) 2021 by RStudio, PBC
25+ * Copyright (C) 2020 Ricardo Wurmus
26 *
27 * Unless you have received this program directly from RStudio pursuant
28 * to the terms of a commercial license agreement with RStudio, then
29@@ -62,6 +63,8 @@
30 #include <core/http/CSRFToken.hpp>
31 #include <core/system/Environment.hpp>
32
33+#include <server_core/RVersionsScanner.hpp>
34+
35 #include <session/SessionConsoleProcess.hpp>
36 #include <session/SessionClientEventService.hpp>
37 #include <session/SessionHttpConnection.hpp>
38@@ -507,11 +510,29 @@ void handleClientInit(const boost::function<void()>& initFunction,
39
40 sessionInfo["multi_session"] = options.multiSession();
41
42- json::Object rVersionsJson;
43- rVersionsJson["r_version"] = module_context::rVersion();
44- rVersionsJson["r_version_label"] = module_context::rVersionLabel();
45- rVersionsJson["r_home_dir"] = module_context::rHomeDir();
46- sessionInfo["r_versions_info"] = rVersionsJson;
47+ // Read versions from /etc/rstudio/r-versions
48+ json::Array availableRVersionsJson;
49+ std::vector<r_util::RVersion> versions = RVersionsScanner().getRVersions();
50+ for (r_util::RVersion& rEntry : versions)
51+ {
52+ json::Object rVersionJson;
53+ rVersionJson["version"] = rEntry.number();
54+ rVersionJson["label"] = rEntry.label();
55+ rVersionJson["r_home"] = rEntry.homeDir().getAbsolutePath();
56+
57+ availableRVersionsJson.push_back(rVersionJson);
58+ }
59+
60+ json::Object rVersionsInfo;
61+ rVersionsInfo["available_r_versions"] = availableRVersionsJson;
62+ rVersionsInfo["r_version"] = module_context::rVersion();
63+ rVersionsInfo["r_version_label"] = module_context::rVersionLabel();
64+ rVersionsInfo["r_home"] = module_context::rHomeDir();
65+ rVersionsInfo["default_r_version"] = module_context::rVersion();
66+ rVersionsInfo["default_r_version_label"] = module_context::rVersionLabel();
67+ rVersionsInfo["default_r_home_dir"] = module_context::rHomeDir();
68+ rVersionsInfo["restore_project_r_version"] = true;
69+ sessionInfo["r_versions_info"] = rVersionsInfo;
70
71 sessionInfo["show_user_home_page"] = options.showUserHomePage();
72 sessionInfo["user_home_page_url"] = json::Value();
73--
742.31.1
75
diff --git a/patches/rstudio-server-multi-version/0002-sessionProcessConfig-Configure-R-version-from-active.patch b/patches/rstudio-server-multi-version/0002-sessionProcessConfig-Configure-R-version-from-active.patch
new file mode 100644
index 0000000..5105001
--- /dev/null
+++ b/patches/rstudio-server-multi-version/0002-sessionProcessConfig-Configure-R-version-from-active.patch
@@ -0,0 +1,76 @@
1From 6172ab0908e8b80bd5838ed4336335d224b80941 Mon Sep 17 00:00:00 2001
2From: Ricardo Wurmus <rekado@elephly.net>
3Date: Fri, 18 Dec 2020 10:32:01 +0100
4Subject: [PATCH 2/7] sessionProcessConfig: Configure R version from active
5 session.
6
7When switching projects the R version is recorded in the active
8session. Before launching a new rsession process R environment
9variables need to be set to select the variant of libR.so that should
10be used to launch the embedded R.
11
12* src/cpp/server/ServerSessionManager.cpp (sessionProcessConfig):
13Configure R environment using the R version that has been recorded in
14the active session.
15---
16 src/cpp/server/ServerSessionManager.cpp | 26 ++++++++++++++++++++++++-
17 1 file changed, 25 insertions(+), 1 deletion(-)
18
19diff --git a/src/cpp/server/ServerSessionManager.cpp b/src/cpp/server/ServerSessionManager.cpp
20index 6e37578cab..86d212779a 100644
21--- a/src/cpp/server/ServerSessionManager.cpp
22+++ b/src/cpp/server/ServerSessionManager.cpp
23@@ -2,6 +2,7 @@
24 * ServerSessionManager.cpp
25 *
26 * Copyright (C) 2021 by RStudio, PBC
27+ * Copyright (C) 2020-2021 Ricardo Wurmus
28 *
29 * Unless you have received this program directly from RStudio pursuant
30 * to the terms of a commercial license agreement with RStudio, then
31@@ -21,10 +22,14 @@
32
33 #include <shared_core/SafeConvert.hpp>
34 #include <core/system/Process.hpp>
35+#include <shared_core/system/User.hpp>
36 #include <core/system/PosixUser.hpp>
37 #include <core/system/Environment.hpp>
38 #include <core/json/JsonRpc.hpp>
39
40+#include <core/r_util/RActiveSessions.hpp>
41+#include <core/r_util/RUserData.hpp>
42+
43 #include <monitor/MonitorClient.hpp>
44 #include <session/SessionConstants.hpp>
45
46@@ -156,7 +161,26 @@ core::system::ProcessConfig sessionProcessConfig(
47 std::copy(extraArgs.begin(), extraArgs.end(), std::back_inserter(args));
48
49 // append R environment variables
50- r_util::RVersion rVersion = r_environment::rVersion();
51+ // Get the active session (e.g. after switching projects) and set R
52+ // variables according to the configured R version.
53+ core::system::User user;
54+ core::system::User::getUserFromIdentifier(context.username, user);
55+ FilePath userScratchPath_ = core::system::xdg::userDataDir(context.username);
56+
57+ r_util::ActiveSessions activeSessions(userScratchPath_);
58+ std::vector<boost::shared_ptr<r_util::ActiveSession> > sessions =
59+ activeSessions.list(user.getHomePath(), false);
60+
61+ r_util::RVersion rVersion;
62+ if (sessions.size() > 0) {
63+ // Get the R version from the active user session. This will
64+ // have been set earlier when the user switched to a project.
65+ std::string errorMsg;
66+ bool success = r_environment::detectRVersion(FilePath((*sessions.front()).rVersionHome()).completePath("bin/R"),
67+ &rVersion, &errorMsg);
68+ } else {
69+ rVersion = r_environment::rVersion();
70+ }
71 core::system::Options rEnvVars = rVersion.environment();
72 environment.insert(environment.end(), rEnvVars.begin(), rEnvVars.end());
73
74--
752.31.1
76
diff --git a/patches/rstudio-server-multi-version/0003-NewProjectWizard-Unhide-version-selector-widget.patch b/patches/rstudio-server-multi-version/0003-NewProjectWizard-Unhide-version-selector-widget.patch
new file mode 100644
index 0000000..c8f78a5
--- /dev/null
+++ b/patches/rstudio-server-multi-version/0003-NewProjectWizard-Unhide-version-selector-widget.patch
@@ -0,0 +1,27 @@
1From 28a8054858657014cb83703be222a06f3aa06119 Mon Sep 17 00:00:00 2001
2From: Ricardo Wurmus <rekado@elephly.net>
3Date: Fri, 18 Dec 2020 10:35:39 +0100
4Subject: [PATCH 3/7] NewProjectWizard: Unhide version selector widget.
5
6*
7src/gwt/src/org/rstudio/studio/client/projects/ui/newproject/NewProjectWizard.java:
8Do not hide the widget.
9---
10 .../studio/client/projects/ui/newproject/NewProjectWizard.java | 1 -
11 1 file changed, 1 deletion(-)
12
13diff --git a/src/gwt/src/org/rstudio/studio/client/projects/ui/newproject/NewProjectWizard.java b/src/gwt/src/org/rstudio/studio/client/projects/ui/newproject/NewProjectWizard.java
14index 08ece8ab26..3a19f4eeab 100644
15--- a/src/gwt/src/org/rstudio/studio/client/projects/ui/newproject/NewProjectWizard.java
16+++ b/src/gwt/src/org/rstudio/studio/client/projects/ui/newproject/NewProjectWizard.java
17@@ -70,7 +70,6 @@ public class NewProjectWizard extends Wizard<NewProjectInput,NewProjectResult>
18 rVersionSelector_.setRVersion(rVersion);
19 addLeftWidget(rVersionSelector_);
20 rVersionSelector_.getElement().getStyle().setMarginRight(8, Unit.PX);
21- rVersionSelector_.setVisible(false);
22 }
23
24 openInNewWindow_ = new CheckBox("Open in new session");
25--
262.31.1
27
diff --git a/patches/rstudio-server-multi-version/0004-handleConnection-Switch-R-version-when-switching-pro.patch b/patches/rstudio-server-multi-version/0004-handleConnection-Switch-R-version-when-switching-pro.patch
new file mode 100644
index 0000000..0fbeb86
--- /dev/null
+++ b/patches/rstudio-server-multi-version/0004-handleConnection-Switch-R-version-when-switching-pro.patch
@@ -0,0 +1,88 @@
1From a41c5e9ce141713b12ad552205c27db41193c477 Mon Sep 17 00:00:00 2001
2From: Ricardo Wurmus <rekado@elephly.net>
3Date: Sat, 19 Dec 2020 00:57:26 +0100
4Subject: [PATCH 4/7] handleConnection: Switch R version when switching
5 projects.
6
7* src/cpp/session/SessionHttpMethods.cpp (handleConnection): Switch to
8the recorded R version when switching to a project; when closing a
9project switch back to the default.
10---
11 src/cpp/session/SessionHttpMethods.cpp | 32 ++++++++++++++++---
12 .../studio/client/projects/Projects.java | 3 +-
13 2 files changed, 30 insertions(+), 5 deletions(-)
14
15diff --git a/src/cpp/session/SessionHttpMethods.cpp b/src/cpp/session/SessionHttpMethods.cpp
16index 2fd08c028b..3e66f13dd7 100644
17--- a/src/cpp/session/SessionHttpMethods.cpp
18+++ b/src/cpp/session/SessionHttpMethods.cpp
19@@ -2,6 +2,7 @@
20 * SessionHttpMethods.hpp
21 *
22 * Copyright (C) 2021 by RStudio, PBC
23+ * Copyright (C) 2020 Ricardo Wurmus
24 *
25 * Unless you have received this program directly from RStudio pursuant
26 * to the terms of a commercial license agreement with RStudio, then
27@@ -667,6 +668,33 @@ void handleConnection(boost::shared_ptr<HttpConnection> ptrConnection,
28 activeSession().setWorkingDir(projDir);
29 }
30
31+ RVersionSettings verSettings(
32+ options().userScratchPath(),
33+ FilePath(options().getOverlayOption(
34+ kSessionSharedStoragePath)));
35+
36+ // Get the project's R version and set it for the active session.
37+ if ((switchToProject != kProjectNone) &&
38+ (verSettings.restoreProjectRVersion()))
39+ {
40+ FilePath projFile = resolveAliasedPath(switchToProject);
41+ std::string projDir = createAliasedPath(projFile.getParent());
42+ std::string version, rHome, label;
43+ verSettings.readProjectLastRVersion(projDir,
44+ module_context::sharedProjectScratchPath(),
45+ &version,
46+ &rHome,
47+ &label);
48+ activeSession().setRVersion(version, rHome, label);
49+ }
50+ // Reset version to default.
51+ else
52+ {
53+ activeSession().setRVersion(verSettings.defaultRVersion(),
54+ verSettings.defaultRVersionHome(),
55+ verSettings.defaultRVersionLabel());
56+ }
57+
58 if (options().switchProjectsWithUrl())
59 {
60 r_util::SessionScope scope;
61@@ -721,10 +749,6 @@ void handleConnection(boost::shared_ptr<HttpConnection> ptrConnection,
62 {
63 FilePath projFile = resolveAliasedPath(switchToProject);
64 std::string projDir = createAliasedPath(projFile.getParent());
65- RVersionSettings verSettings(
66- options().userScratchPath(),
67- FilePath(options().getOverlayOption(
68- kSessionSharedStoragePath)));
69 verSettings.setProjectLastRVersion(projDir,
70 module_context::sharedProjectScratchPath(),
71 version,
72diff --git a/src/gwt/src/org/rstudio/studio/client/projects/Projects.java b/src/gwt/src/org/rstudio/studio/client/projects/Projects.java
73index 89c3eb04b9..4ebeb6881d 100644
74--- a/src/gwt/src/org/rstudio/studio/client/projects/Projects.java
75+++ b/src/gwt/src/org/rstudio/studio/client/projects/Projects.java
76@@ -1131,7 +1131,8 @@ public class Projects implements OpenProjectFileHandler,
77 {
78 // perform quit
79 applicationQuit_.performQuit(null, saveChanges,
80- input.getProjectFile().getPath());
81+ input.getProjectFile().getPath(),
82+ input.getRVersion());
83 }
84 };
85
86--
872.31.1
88
diff --git a/patches/rstudio-server-multi-version/0005-Add-version-switcher-widget-to-toolbar.patch b/patches/rstudio-server-multi-version/0005-Add-version-switcher-widget-to-toolbar.patch
new file mode 100644
index 0000000..da4f79f
--- /dev/null
+++ b/patches/rstudio-server-multi-version/0005-Add-version-switcher-widget-to-toolbar.patch
@@ -0,0 +1,287 @@
1From 3a70a198af9771dc080ab3d790cab38d47196042 Mon Sep 17 00:00:00 2001
2From: Ricardo Wurmus <rekado@elephly.net>
3Date: Sat, 19 Dec 2020 23:33:16 +0100
4Subject: [PATCH 5/7] Add version switcher widget to toolbar.
5
6---
7 .../org/rstudio/core/client/ElementIds.java | 2 +
8 .../studio/client/RStudioGinjector.java | 2 +
9 .../client/application/ui/GlobalToolbar.java | 4 +
10 .../application/ui/RVersionPopupMenu.java | 162 ++++++++++++++++++
11 .../workbench/commands/Commands.cmd.xml | 11 ++
12 .../client/workbench/commands/Commands.java | 13 ++
13 6 files changed, 194 insertions(+)
14 create mode 100644 src/gwt/src/org/rstudio/studio/client/application/ui/RVersionPopupMenu.java
15
16diff --git a/src/gwt/src/org/rstudio/core/client/ElementIds.java b/src/gwt/src/org/rstudio/core/client/ElementIds.java
17index 9a62c2f5b1..7ef305d552 100644
18--- a/src/gwt/src/org/rstudio/core/client/ElementIds.java
19+++ b/src/gwt/src/org/rstudio/core/client/ElementIds.java
20@@ -295,6 +295,8 @@ public class ElementIds
21 public final static String PROJECT_MENUBUTTON_TOOLBAR_SUFFIX = "toolbar";
22 public final static String PROJECT_MENUBUTTON_MENUBAR_SUFFIX = "menubar";
23
24+ public final static String VERSION_MENUBUTTON = "version_menubutton";
25+
26 // BuildPane
27 public final static String BUILD_MORE_MENUBUTTON = "build_more_menubutton";
28 public final static String BUILD_BOOKDOWN_MENUBUTTON = "build_bookdown_menubutton";
29diff --git a/src/gwt/src/org/rstudio/studio/client/RStudioGinjector.java b/src/gwt/src/org/rstudio/studio/client/RStudioGinjector.java
30index 5ee1e0b025..1517834382 100644
31--- a/src/gwt/src/org/rstudio/studio/client/RStudioGinjector.java
32+++ b/src/gwt/src/org/rstudio/studio/client/RStudioGinjector.java
33@@ -41,6 +41,7 @@ import org.rstudio.studio.client.application.AriaLiveService;
34 import org.rstudio.studio.client.application.events.EventBus;
35 import org.rstudio.studio.client.application.ui.AboutDialog;
36 import org.rstudio.studio.client.application.ui.ProjectPopupMenu;
37+import org.rstudio.studio.client.application.ui.RVersionPopupMenu;
38 import org.rstudio.studio.client.application.ui.addins.AddinsToolbarButton;
39 import org.rstudio.studio.client.application.ui.impl.DesktopApplicationHeader;
40 import org.rstudio.studio.client.application.ui.impl.WebApplicationHeader;
41@@ -226,6 +227,7 @@ public interface RStudioGinjector extends Ginjector
42 void injectMembers(CodeFilesList codeFilesList);
43 void injectMembers(ToolbarPopupMenu toolbarPopupMenu);
44 void injectMembers(ProjectPopupMenu projectPopupMenu);
45+ void injectMembers(RVersionPopupMenu rVersionPopupMenu);
46 void injectMembers(ClearAllDialog clearAllDialog);
47 void injectMembers(TextEditingTargetPresentationHelper presHelper);
48 void injectMembers(TextEditingTargetRMarkdownHelper rmarkdownHelper);
49diff --git a/src/gwt/src/org/rstudio/studio/client/application/ui/GlobalToolbar.java b/src/gwt/src/org/rstudio/studio/client/application/ui/GlobalToolbar.java
50index a2bb16f115..ed5f3ed4a8 100644
51--- a/src/gwt/src/org/rstudio/studio/client/application/ui/GlobalToolbar.java
52+++ b/src/gwt/src/org/rstudio/studio/client/application/ui/GlobalToolbar.java
53@@ -236,6 +236,10 @@ public class GlobalToolbar extends Toolbar
54 ProjectPopupMenu projectMenu = new ProjectPopupMenu(
55 sessionInfo, commands_, ElementIds.PROJECT_MENUBUTTON_TOOLBAR_SUFFIX);
56 addRightWidget(projectMenu.getToolbarButton());
57+
58+ RVersionPopupMenu rVersionMenu = new RVersionPopupMenu(
59+ sessionInfo, commands_);
60+ addRightWidget(rVersionMenu.getToolbarButton());
61 }
62 }
63
64diff --git a/src/gwt/src/org/rstudio/studio/client/application/ui/RVersionPopupMenu.java b/src/gwt/src/org/rstudio/studio/client/application/ui/RVersionPopupMenu.java
65new file mode 100644
66index 0000000000..ca66b2f53f
67--- /dev/null
68+++ b/src/gwt/src/org/rstudio/studio/client/application/ui/RVersionPopupMenu.java
69@@ -0,0 +1,162 @@
70+/*
71+ * RVersionPopupMenu.java
72+ *
73+ * Copyright (C) 2020 Ricardo Wurmus
74+ *
75+ * Unless you have received this program directly from RStudio pursuant
76+ * to the terms of a commercial license agreement with RStudio, then
77+ * this program is licensed to you under the terms of version 3 of the
78+ * GNU Affero General Public License. This program is distributed WITHOUT
79+ * ANY EXPRESS OR IMPLIED WARRANTY, INCLUDING THOSE OF NON-INFRINGEMENT,
80+ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Please refer to the
81+ * AGPL (http://www.gnu.org/licenses/agpl-3.0.txt) for more details.
82+ *
83+ */
84+package org.rstudio.studio.client.application.ui;
85+
86+import org.rstudio.core.client.ElementIds;
87+import org.rstudio.core.client.command.AppCommand;
88+import org.rstudio.core.client.resources.ImageResource2x;
89+import org.rstudio.core.client.theme.res.ThemeResources;
90+import org.rstudio.core.client.theme.res.ThemeStyles;
91+import org.rstudio.core.client.widget.ToolbarButton;
92+import org.rstudio.core.client.widget.ToolbarMenuButton;
93+import org.rstudio.core.client.widget.ToolbarPopupMenu;
94+import org.rstudio.studio.client.RStudioGinjector;
95+import org.rstudio.studio.client.application.Desktop;
96+import org.rstudio.studio.client.application.events.SwitchToRVersionEvent;
97+import org.rstudio.studio.client.application.events.EventBus;
98+import org.rstudio.studio.client.application.model.RVersionSpec;
99+import org.rstudio.studio.client.application.model.RVersionsInfo;
100+import org.rstudio.studio.client.server.ServerError;
101+import org.rstudio.studio.client.server.ServerRequestCallback;
102+import org.rstudio.studio.client.workbench.commands.Commands;
103+import org.rstudio.studio.client.workbench.model.Session;
104+import org.rstudio.studio.client.workbench.model.SessionInfo;
105+
106+import com.google.gwt.core.client.GWT;
107+import com.google.gwt.core.client.JsArray;
108+import com.google.gwt.user.client.Event;
109+import com.google.gwt.user.client.ui.MenuItem;
110+import com.google.inject.Inject;
111+
112+public class RVersionPopupMenu extends ToolbarPopupMenu
113+{
114+ /**
115+ *
116+ * @param sessionInfo
117+ * @param commands
118+ */
119+ public RVersionPopupMenu(SessionInfo sessionInfo, Commands commands)
120+ {
121+ RStudioGinjector.INSTANCE.injectMembers(this);
122+
123+ commands_ = commands;
124+
125+ RVersionsInfo rVersionsInfo = sessionInfo.getRVersionsInfo();
126+ availableVersions_ = rVersionsInfo.getAvailableRVersions();
127+ rVersion_ = RVersionSpec.create(rVersionsInfo.getRVersion(),
128+ rVersionsInfo.getRVersionHome(),
129+ rVersionsInfo.getRVersionLabel());
130+ }
131+
132+ @Inject
133+ void initialize(EventBus events,
134+ Session session)
135+ {
136+ events_ = events;
137+ }
138+
139+ public ToolbarButton getToolbarButton()
140+ {
141+ String buttonText = "R version";
142+
143+ if (toolbarButton_ == null)
144+ {
145+ toolbarButton_ = new ToolbarMenuButton(
146+ buttonText,
147+ ToolbarButton.NoTitle,
148+ null,
149+ this,
150+ true);
151+ ElementIds.assignElementId(toolbarButton_, ElementIds.VERSION_MENUBUTTON);
152+ }
153+
154+ toolbarButton_.setTitle(buttonText);
155+ return toolbarButton_;
156+ }
157+
158+ @Override
159+ protected ToolbarMenuBar createMenuBar()
160+ {
161+ return new RVersionPopupMenuBar();
162+ }
163+
164+ private class RVersionPopupMenuBar extends ToolbarMenuBar
165+ {
166+ public RVersionPopupMenuBar()
167+ {
168+ super(true);
169+ }
170+ }
171+
172+ @Override
173+ public void getDynamicPopupMenu(final DynamicPopupMenuCallback callback)
174+ {
175+ rebuildMenu(null, callback);
176+ }
177+
178+ private void rebuildMenu(final JsArray<RVersionSpec> versions,
179+ DynamicPopupMenuCallback callback)
180+ {
181+ // clean out existing entries
182+ clearItems();
183+
184+ // ensure the menu doesn't get too narrow
185+ addSeparator(225);
186+
187+ // add as many MRU items as is appropriate for our screen size and number
188+ // of available versions
189+ AppCommand[] versionCommands = new AppCommand[] {
190+ commands_.projectVersion0(),
191+ commands_.projectVersion1(),
192+ commands_.projectVersion2(),
193+ commands_.projectVersion3(),
194+ commands_.projectVersion4(),
195+ commands_.projectVersion5(),
196+ commands_.projectVersion6(),
197+ commands_.projectVersion7(),
198+ commands_.projectVersion8(),
199+ commands_.projectVersion9()
200+ };
201+
202+ for (int i = 0; i < Math.min(versionCommands.length, MAX_VERSIONS); i++)
203+ {
204+ addItem(versionCommands[i].createMenuItem(false));
205+ }
206+
207+ for (int i = 0; i < Math.min(availableVersions_.length(),
208+ MAX_VERSIONS); i ++)
209+ {
210+ final RVersionSpec version = availableVersions_.get(i);
211+ String menuHtml = AppCommand.formatMenuLabel(
212+ null, version.getVersion() + " (" + version.getLabel() + ")", false, null);
213+ addItem(new MenuItem(menuHtml, true, () ->
214+ {
215+ events_.fireEvent(new SwitchToRVersionEvent(version));
216+ }));
217+ }
218+
219+ callback.onPopupMenu(this);
220+ }
221+
222+ private static final Resources RESOURCES = GWT.create(Resources.class);
223+
224+ private static final int MAX_VERSIONS = 10;
225+ private JsArray<RVersionSpec> availableVersions_;
226+ private final RVersionSpec rVersion_;
227+ private ToolbarMenuButton toolbarButton_ = null;
228+
229+ private final Commands commands_;
230+ private EventBus events_;
231+}
232diff --git a/src/gwt/src/org/rstudio/studio/client/workbench/commands/Commands.cmd.xml b/src/gwt/src/org/rstudio/studio/client/workbench/commands/Commands.cmd.xml
233index 8f53957924..47c5fbb89a 100644
234--- a/src/gwt/src/org/rstudio/studio/client/workbench/commands/Commands.cmd.xml
235+++ b/src/gwt/src/org/rstudio/studio/client/workbench/commands/Commands.cmd.xml
236@@ -1312,6 +1312,17 @@ well as menu structures (for main menu and popup menus).
237 desc="Edit options for the current project"
238 windowMode="main"/>
239
240+ <cmd id="projectVersion0" visible="false" rebindable="false" windowMode="main"/>
241+ <cmd id="projectVersion1" visible="false" rebindable="false" windowMode="main"/>
242+ <cmd id="projectVersion2" visible="false" rebindable="false" windowMode="main"/>
243+ <cmd id="projectVersion3" visible="false" rebindable="false" windowMode="main"/>
244+ <cmd id="projectVersion4" visible="false" rebindable="false" windowMode="main"/>
245+ <cmd id="projectVersion5" visible="false" rebindable="false" windowMode="main"/>
246+ <cmd id="projectVersion6" visible="false" rebindable="false" windowMode="main"/>
247+ <cmd id="projectVersion7" visible="false" rebindable="false" windowMode="main"/>
248+ <cmd id="projectVersion8" visible="false" rebindable="false" windowMode="main"/>
249+ <cmd id="projectVersion9" visible="false" rebindable="false" windowMode="main"/>
250+
251 <cmd id="projectSweaveOptions"
252 menuLabel=""
253 buttonLabel=""
254diff --git a/src/gwt/src/org/rstudio/studio/client/workbench/commands/Commands.java b/src/gwt/src/org/rstudio/studio/client/workbench/commands/Commands.java
255index 803f79ecc9..1c84e2c039 100644
256--- a/src/gwt/src/org/rstudio/studio/client/workbench/commands/Commands.java
257+++ b/src/gwt/src/org/rstudio/studio/client/workbench/commands/Commands.java
258@@ -2,6 +2,7 @@
259 * Commands.java
260 *
261 * Copyright (C) 2021 by RStudio, PBC
262+ * Copyright (C) 2020 Ricardo Wurmus
263 *
264 * Unless you have received this program directly from RStudio pursuant
265 * to the terms of a commercial license agreement with RStudio, then
266@@ -231,6 +232,18 @@ public abstract class
267 public abstract AppCommand projectSweaveOptions();
268 public abstract AppCommand setWorkingDirToProjectDir();
269
270+ // R Versions
271+ public abstract AppCommand projectVersion0();
272+ public abstract AppCommand projectVersion1();
273+ public abstract AppCommand projectVersion2();
274+ public abstract AppCommand projectVersion3();
275+ public abstract AppCommand projectVersion4();
276+ public abstract AppCommand projectVersion5();
277+ public abstract AppCommand projectVersion6();
278+ public abstract AppCommand projectVersion7();
279+ public abstract AppCommand projectVersion8();
280+ public abstract AppCommand projectVersion9();
281+
282 // Console
283 public abstract AppCommand consoleClear();
284 public abstract AppCommand interruptR();
285--
2862.31.1
287
diff --git a/patches/rstudio-server-multi-version/0006-Look-at-.local-share-rstudio-r-versions-for-custom-R.patch b/patches/rstudio-server-multi-version/0006-Look-at-.local-share-rstudio-r-versions-for-custom-R.patch
new file mode 100644
index 0000000..48150c3
--- /dev/null
+++ b/patches/rstudio-server-multi-version/0006-Look-at-.local-share-rstudio-r-versions-for-custom-R.patch
@@ -0,0 +1,92 @@
1From e309b35a2f59a268442de8e1684ddd72fc9e5166 Mon Sep 17 00:00:00 2001
2From: Ricardo Wurmus <rekado@elephly.net>
3Date: Sun, 20 Dec 2020 00:25:42 +0100
4Subject: [PATCH 6/7] Look at ~/.local/share/rstudio/r-versions for custom R
5 versions.
6
7---
8 src/cpp/server_core/RVersionsScanner.cpp | 20 +++++++++++++++++++
9 .../include/server_core/RVersionsScanner.hpp | 1 +
10 src/cpp/session/SessionClientInit.cpp | 8 +++++++-
11 3 files changed, 28 insertions(+), 1 deletion(-)
12
13diff --git a/src/cpp/server_core/RVersionsScanner.cpp b/src/cpp/server_core/RVersionsScanner.cpp
14index af5974c24d..7504fbb1ff 100644
15--- a/src/cpp/server_core/RVersionsScanner.cpp
16+++ b/src/cpp/server_core/RVersionsScanner.cpp
17@@ -2,6 +2,7 @@
18 * RVersionsScanner.cpp
19 *
20 * Copyright (C) 2021 by RStudio, PBC
21+ * Copyright (C) 2020 Ricardo Wurmus
22 *
23 * Unless you have received this program directly from RStudio pursuant
24 * to the terms of a commercial license agreement with RStudio, then
25@@ -138,6 +139,11 @@ void RVersionsScanner::setFallbackVersion()
26 }
27
28 std::vector<r_util::RVersion> RVersionsScanner::getRVersions()
29+{
30+ return RVersionsScanner::getRVersions(FilePath(""));
31+}
32+
33+std::vector<r_util::RVersion> RVersionsScanner::getRVersions(FilePath userFile)
34 {
35 if (!cachedVersions_.empty())
36 return cachedVersions_;
37@@ -173,6 +179,20 @@ std::vector<r_util::RVersion> RVersionsScanner::getRVersions()
38 }
39 }
40
41+ if (!userFile.isEmpty() && (userFile.exists()))
42+ {
43+ std::string contents;
44+ Error error = core::readStringFromFile(userFile, &contents, string_utils::LineEndingPosix);
45+ if (!error)
46+ {
47+ parseRVersionsFile(userFile, contents, &rHomeDirs, &rEntries);
48+ }
49+ else
50+ {
51+ LOG_ERROR(error);
52+ }
53+ }
54+
55 // scan for available R versions
56 using namespace r_util;
57 std::vector<r_util::RVersion> versions = r_util::enumerateRVersions(
58diff --git a/src/cpp/server_core/include/server_core/RVersionsScanner.hpp b/src/cpp/server_core/include/server_core/RVersionsScanner.hpp
59index 840429edf5..3cf9b7d79e 100644
60--- a/src/cpp/server_core/include/server_core/RVersionsScanner.hpp
61+++ b/src/cpp/server_core/include/server_core/RVersionsScanner.hpp
62@@ -45,6 +45,7 @@ public:
63 // scans for r versions and returns any that were found
64 // subsequent calls return cached versions found in initial scan
65 std::vector<r_util::RVersion> getRVersions();
66+ std::vector<r_util::RVersion> getRVersions(core::FilePath userFile);
67
68 bool detectRVersion(const core::FilePath& rScriptPath,
69 core::r_util::RVersion* pVersion,
70diff --git a/src/cpp/session/SessionClientInit.cpp b/src/cpp/session/SessionClientInit.cpp
71index 52523fc896..34dad727c4 100644
72--- a/src/cpp/session/SessionClientInit.cpp
73+++ b/src/cpp/session/SessionClientInit.cpp
74@@ -511,8 +511,14 @@ void handleClientInit(const boost::function<void()>& initFunction,
75 sessionInfo["multi_session"] = options.multiSession();
76
77 // Read versions from /etc/rstudio/r-versions
78+ // ...and read from a user-controlled file
79+ FilePath userRDirsPath =
80+ options.userScratchPath().completePath("r-versions");
81+
82 json::Array availableRVersionsJson;
83- std::vector<r_util::RVersion> versions = RVersionsScanner().getRVersions();
84+ std::vector<r_util::RVersion> versions =
85+ RVersionsScanner().getRVersions(userRDirsPath);
86+
87 for (r_util::RVersion& rEntry : versions)
88 {
89 json::Object rVersionJson;
90--
912.31.1
92
diff --git a/patches/rstudio-server-multi-version/0007-detectRLocationsUsingR-Restore-R_HOME-at-the-end.patch b/patches/rstudio-server-multi-version/0007-detectRLocationsUsingR-Restore-R_HOME-at-the-end.patch
new file mode 100644
index 0000000..96eb6aa
--- /dev/null
+++ b/patches/rstudio-server-multi-version/0007-detectRLocationsUsingR-Restore-R_HOME-at-the-end.patch
@@ -0,0 +1,36 @@
1From 36ed7d1c0036d40798a3ab5045f365e8e33692f3 Mon Sep 17 00:00:00 2001
2From: Ricardo Wurmus <rekado@elephly.net>
3Date: Sun, 20 Dec 2020 14:49:51 +0100
4Subject: [PATCH 7/7] detectRLocationsUsingR: Restore R_HOME at the end.
5
6This is not needed when detectRLocationsUsingR is only called by the
7server process; for the multi-version feature, however, the rsession
8process is calling it, and its R_HOME may not be unset.
9---
10 src/cpp/core/r_util/REnvironmentPosix.cpp | 3 +++
11 1 file changed, 3 insertions(+)
12
13diff --git a/src/cpp/core/r_util/REnvironmentPosix.cpp b/src/cpp/core/r_util/REnvironmentPosix.cpp
14index 83b60d7a1b..a909631e25 100644
15--- a/src/cpp/core/r_util/REnvironmentPosix.cpp
16+++ b/src/cpp/core/r_util/REnvironmentPosix.cpp
17@@ -489,6 +489,7 @@ bool detectRLocationsUsingR(const std::string& rScriptPath,
18 // (the normal semantics of invoking the R script are that it overwrites
19 // R_HOME and prints a warning -- this warning is co-mingled with the
20 // output of R and messes up our parsing)
21+ std::string oldRHome = core::system::getenv("R_HOME");
22 core::system::setenv("R_HOME", "");
23
24 // if no R path was specified for a module, the module binary path MUST be specified
25@@ -598,6 +599,8 @@ bool detectRLocationsUsingR(const std::string& rScriptPath,
26 return false;
27 }
28
29+ // restore R_HOME
30+ core::system::setenv("R_HOME", oldRHome);
31 return true;
32 }
33 #endif
34--
352.31.1
36