Merge pull request #95831 from cfhammill/cfh/add-rstudio-server

rstudio-server: Initial commit for rstudio-server and associated wrapper
This commit is contained in:
Justin Bedő 2022-01-17 12:59:58 +11:00 committed by GitHub
commit dfa93a4725
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 338 additions and 138 deletions

View file

@ -1950,6 +1950,12 @@
githubId = 543423;
name = "Alex Wied";
};
cfhammill = {
email = "cfhammill@gmail.com";
github = "cfhammill";
githubId = 7467038;
name = "Chris Hammill";
};
cfouche = {
email = "chaddai.fouche@gmail.com";
github = "Chaddai";

View file

@ -165,6 +165,14 @@
<link xlink:href="options.html#opt-services.timetagger.enable">services.timetagger</link>.
</para>
</listitem>
<listitem>
<para>
<link xlink:href="https://www.rstudio.com/products/rstudio/#rstudio-server">rstudio-server</link>,
a browser-based version of the RStudio IDE for the R
programming language. Available as
<link xlink:href="options.html#opt-services.rstudio-server.enable">services.rstudio-server</link>.
</para>
</listitem>
</itemizedlist>
</section>
<section xml:id="sec-release-22.05-incompatibilities">

View file

@ -50,6 +50,8 @@ In addition to numerous new and upgraded packages, this release has the followin
- [timetagger](https://timetagger.app), an open source time-tracker with an intuitive user experience and powerful reporting. [services.timetagger](options.html#opt-services.timetagger.enable).
- [rstudio-server](https://www.rstudio.com/products/rstudio/#rstudio-server), a browser-based version of the RStudio IDE for the R programming language. Available as [services.rstudio-server](options.html#opt-services.rstudio-server.enable).
## Backward Incompatibilities {#sec-release-22.05-incompatibilities}
- `pkgs.ghc` now refers to `pkgs.targetPackages.haskellPackages.ghc`.

View file

@ -353,6 +353,7 @@ in
distcc = 321;
webdav = 322;
pipewire = 323;
rstudio-server = 324;
# When adding a uid, make sure it doesn't match an existing gid. And don't use uids above 399!
@ -660,6 +661,7 @@ in
distcc = 321;
webdav = 322;
pipewire = 323;
rstudio-server = 324;
# When adding a gid, make sure it doesn't match an existing
# uid. Users and groups with the same name should have equal

View file

@ -394,6 +394,7 @@
./services/development/hoogle.nix
./services/development/jupyter/default.nix
./services/development/jupyterhub/default.nix
./services/development/rstudio-server/default.nix
./services/development/lorri.nix
./services/display-managers/greetd.nix
./services/editors/emacs.nix

View file

@ -0,0 +1,107 @@
{ config, lib, pkgs, ... }:
with lib;
let
cfg = config.services.rstudio-server;
rserver-conf = builtins.toFile "rserver.conf" ''
server-working-dir=${cfg.serverWorkingDir}
www-address=${cfg.listenAddr}
${cfg.rserverExtraConfig}
'';
rsession-conf = builtins.toFile "rsession.conf" ''
${cfg.rsessionExtraConfig}
'';
in
{
meta.maintainers = with maintainers; [ jbedo cfhammill ];
options.services.rstudio-server = {
enable = mkEnableOption "RStudio server";
serverWorkingDir = mkOption {
type = types.str;
default = "/var/lib/rstudio-server";
description = ''
Default working directory for server (server-working-dir in rserver.conf).
'';
};
listenAddr = mkOption {
type = types.str;
default = "127.0.0.1";
description = ''
Address to listen on (www-address in rserver.conf).
'';
};
package = mkOption {
type = types.package;
default = pkgs.rstudio-server;
defaultText = literalExpression "pkgs.rstudio-server";
example = literalExpression "pkgs.rstudioServerWrapper.override { packages = [ pkgs.rPackages.ggplot2 ]; }";
description = ''
Rstudio server package to use. Can be set to rstudioServerWrapper to provide packages.
'';
};
rserverExtraConfig = mkOption {
type = types.str;
default = "";
description = ''
Extra contents for rserver.conf.
'';
};
rsessionExtraConfig = mkOption {
type = types.str;
default = "";
description = ''
Extra contents for resssion.conf.
'';
};
};
config = mkIf cfg.enable
{
systemd.services.rstudio-server = {
description = "Rstudio server";
after = [ "network.target" ];
wantedBy = [ "multi-user.target" ];
restartTriggers = [ rserver-conf rsession-conf ];
serviceConfig = {
Restart = "on-failure";
Type = "forking";
ExecStart = "${cfg.package}/bin/rserver";
StateDirectory = "rstudio-server";
RuntimeDirectory = "rstudio-server";
};
};
environment.etc = {
"rstudio/rserver.conf".source = rserver-conf;
"rstudio/rsession.conf".source = rsession-conf;
"pam.d/rstudio".source = "/etc/pam.d/login";
};
environment.systemPackages = [ cfg.package ];
users = {
users.rstudio-server = {
uid = config.ids.uids.rstudio-server;
description = "rstudio-server";
group = "rstudio-server";
};
groups.rstudio-server = {
gid = config.ids.gids.rstudio-server;
};
};
};
}

View file

@ -0,0 +1,30 @@
import ./make-test-python.nix ({ pkgs, ... }:
{
name = "rstudio-server-test";
meta.maintainers = with pkgs.lib.maintainers; [ jbedo cfhammill ];
nodes.machine = { config, lib, pkgs, ... }: {
services.rstudio-server.enable = true;
};
nodes.customPackageMachine = { config, lib, pkgs, ... }: {
services.rstudio-server = {
enable = true;
package = pkgs.rstudioServerWrapper.override { packages = [ pkgs.rPackages.ggplot2 ]; };
};
};
users.testuser = {
uid = 1000;
group = "testgroup";
};
groups.testgroup.gid = 1000;
testScript = ''
machine.wait_for_unit("rstudio-server.service")
machine.succeed("curl -f -vvv -s http://127.0.0.1:8787")
customPackageMachine.wait_for_unit("rstudio-server.service")
customPackageMachine.succeed("curl -f -vvv -s http://127.0.0.1:8787")
'';
})

View file

@ -1,4 +1,5 @@
{ lib
, stdenv
, mkDerivation
, fetchurl
, fetchpatch
@ -30,6 +31,9 @@
, nodejs
, mkYarnModules
, qmake
, server ? false # build server version
, sqlite
, pam
}:
let
@ -61,149 +65,165 @@ let
panmirrorModules = mkYarnModules {
inherit pname version;
packageJSON = ./package.json;
yarnLock = ./yarn.lock;
yarnLock = ./yarn.lock;
yarnNix = ./yarndeps.nix;
};
description = "Set of integrated tools for the R language";
in
mkDerivation rec {
inherit pname version src RSTUDIO_VERSION_MAJOR RSTUDIO_VERSION_MINOR RSTUDIO_VERSION_PATCH;
(if server then stdenv.mkDerivation else mkDerivation)
(rec {
inherit pname version src RSTUDIO_VERSION_MAJOR RSTUDIO_VERSION_MINOR RSTUDIO_VERSION_PATCH;
nativeBuildInputs = [
cmake
unzip
ant
jdk
makeWrapper
pandoc
nodejs
copyDesktopItems
];
nativeBuildInputs = [
cmake
unzip
ant
jdk
makeWrapper
pandoc
nodejs
] ++ lib.optional (!server) [
copyDesktopItems
];
buildInputs = [
boost
zlib
openssl
R
qtbase
qtxmlpatterns
qtsensors
qtwebengine
qtwebchannel
libuuid
libyamlcpp
soci
postgresql
];
buildInputs = [
boost
zlib
openssl
R
libuuid
libyamlcpp
soci
postgresql
] ++ (if server then [
sqlite.dev
pam
] else [
qtbase
qtxmlpatterns
qtsensors
qtwebengine
qtwebchannel
]);
cmakeFlags = [
"-DRSTUDIO_TARGET=Desktop"
"-DCMAKE_BUILD_TYPE=Release"
"-DQT_QMAKE_EXECUTABLE=${qmake}/bin/qmake"
"-DRSTUDIO_USE_SYSTEM_SOCI=ON"
"-DRSTUDIO_USE_SYSTEM_BOOST=ON"
"-DRSTUDIO_USE_SYSTEM_YAML_CPP=ON"
"-DPANDOC_VERSION=${pandoc.version}"
"-DCMAKE_INSTALL_PREFIX=${placeholder "out"}/lib/rstudio"
];
cmakeFlags = [
"-DRSTUDIO_TARGET=${if server then "Server" else "Desktop"}"
"-DCMAKE_BUILD_TYPE=Release"
"-DRSTUDIO_USE_SYSTEM_SOCI=ON"
"-DRSTUDIO_USE_SYSTEM_BOOST=ON"
"-DRSTUDIO_USE_SYSTEM_YAML_CPP=ON"
"-DPANDOC_VERSION=${pandoc.version}"
"-DCMAKE_INSTALL_PREFIX=${placeholder "out"}/lib/rstudio"
] ++ lib.optional (!server) [
"-DQT_QMAKE_EXECUTABLE=${qmake}/bin/qmake"
];
# Hack RStudio to only use the input R and provided libclang.
patches = [
./r-location.patch
./clang-location.patch
# postFetch doesn't work with this | error: unexpected end-of-file
# replacing /usr/bin/node is done in postPatch
# https://src.fedoraproject.org/rpms/rstudio/tree/rawhide
(fetchpatch {
name = "system-node.patch";
url = "https://src.fedoraproject.org/rpms/rstudio/raw/5bda2e290c9e72305582f2011040938d3e356906/f/0004-use-system-node.patch";
sha256 = "sha256-P1Y07RB/ceFNa749nyBUWSE41eiiZgt43zVcmahvfZM=";
})
];
# Hack RStudio to only use the input R and provided libclang.
patches = [
./r-location.patch
./clang-location.patch
# postFetch doesn't work with this | error: unexpected end-of-file
# replacing /usr/bin/node is done in postPatch
# https://src.fedoraproject.org/rpms/rstudio/tree/rawhide
(fetchpatch {
name = "system-node.patch";
url = "https://src.fedoraproject.org/rpms/rstudio/raw/5bda2e290c9e72305582f2011040938d3e356906/f/0004-use-system-node.patch";
sha256 = "sha256-P1Y07RB/ceFNa749nyBUWSE41eiiZgt43zVcmahvfZM=";
})
];
postPatch = ''
substituteInPlace src/cpp/core/r_util/REnvironmentPosix.cpp --replace '@R@' ${R}
postPatch = ''
substituteInPlace src/cpp/core/r_util/REnvironmentPosix.cpp --replace '@R@' ${R}
substituteInPlace src/cpp/CMakeLists.txt \
--replace 'SOCI_LIBRARY_DIR "/usr/lib"' 'SOCI_LIBRARY_DIR "${soci}/lib"'
substituteInPlace src/cpp/CMakeLists.txt \
--replace 'SOCI_LIBRARY_DIR "/usr/lib"' 'SOCI_LIBRARY_DIR "${soci}/lib"'
substituteInPlace src/gwt/build.xml \
--replace '/usr/bin/node' '${nodejs}/bin/node'
substituteInPlace src/gwt/build.xml \
--replace '/usr/bin/node' '${nodejs}/bin/node'
substituteInPlace src/cpp/core/libclang/LibClang.cpp \
--replace '@libclang@' ${llvmPackages.libclang.lib} \
--replace '@libclang.so@' ${llvmPackages.libclang.lib}/lib/libclang.so
substituteInPlace src/cpp/core/libclang/LibClang.cpp \
--replace '@libclang@' ${llvmPackages.libclang.lib} \
--replace '@libclang.so@' ${llvmPackages.libclang.lib}/lib/libclang.so
substituteInPlace src/cpp/session/include/session/SessionConstants.hpp \
--replace "bin/pandoc" "${pandoc}/bin/pandoc"
'';
substituteInPlace src/cpp/session/include/session/SessionConstants.hpp \
--replace "bin/pandoc" "${pandoc}/bin/pandoc"
'';
hunspellDictionaries = with lib; filter isDerivation (unique (attrValues hunspellDicts));
# These dicts contain identically-named dict files, so we only keep the
# -large versions in case of clashes
largeDicts = with lib; filter (d: hasInfix "-large-wordlist" d) hunspellDictionaries;
otherDicts = with lib; filter
(d: !(hasAttr "dictFileName" d &&
elem d.dictFileName (map (d: d.dictFileName) largeDicts)))
hunspellDictionaries;
dictionaries = largeDicts ++ otherDicts;
hunspellDictionaries = with lib; filter isDerivation (unique (attrValues hunspellDicts));
# These dicts contain identically-named dict files, so we only keep the
# -large versions in case of clashes
largeDicts = with lib; filter (d: hasInfix "-large-wordlist" d) hunspellDictionaries;
otherDicts = with lib; filter
(d: !(hasAttr "dictFileName" d &&
elem d.dictFileName (map (d: d.dictFileName) largeDicts)))
hunspellDictionaries;
dictionaries = largeDicts ++ otherDicts;
preConfigure = ''
mkdir dependencies/dictionaries
for dict in ${builtins.concatStringsSep " " dictionaries}; do
for i in "$dict/share/hunspell/"*; do
ln -s $i dependencies/dictionaries/
preConfigure = ''
mkdir dependencies/dictionaries
for dict in ${builtins.concatStringsSep " " dictionaries}; do
for i in "$dict/share/hunspell/"*; do
ln -s $i dependencies/dictionaries/
done
done
done
unzip -q ${mathJaxSrc} -d dependencies/mathjax-27
unzip -q ${mathJaxSrc} -d dependencies/mathjax-27
mkdir -p dependencies/pandoc/${pandoc.version}
cp ${pandoc}/bin/pandoc dependencies/pandoc/${pandoc.version}/pandoc
mkdir -p dependencies/pandoc/${pandoc.version}
cp ${pandoc}/bin/pandoc dependencies/pandoc/${pandoc.version}/pandoc
cp -r ${rsconnectSrc} dependencies/rsconnect
( cd dependencies && ${R}/bin/R CMD build -d --no-build-vignettes rsconnect )
cp -r ${rsconnectSrc} dependencies/rsconnect
( cd dependencies && ${R}/bin/R CMD build -d --no-build-vignettes rsconnect )
cp -r "${panmirrorModules}" src/gwt/panmirror/src/editor/node_modules
'';
cp -r "${panmirrorModules}" src/gwt/panmirror/src/editor/node_modules
'';
postInstall = ''
mkdir -p $out/share/icons/hicolor/48x48/apps $out/bin
ln $out/lib/rstudio/rstudio.png $out/share/icons/hicolor/48x48/apps
postInstall = ''
mkdir -p $out/bin $out/share
for f in {diagnostics,rpostback,rstudio}; do
ln -s $out/lib/rstudio/bin/$f $out/bin
done
${lib.optionalString (!server) ''
mkdir -p $out/share/icons/hicolor/48x48/apps
ln $out/lib/rstudio/rstudio.png $out/share/icons/hicolor/48x48/apps
''}
for f in .gitignore .Rbuildignore LICENSE README; do
find . -name $f -delete
done
rm -r $out/lib/rstudio/{INSTALL,COPYING,NOTICE,README.md,SOURCE,VERSION}
rm -r $out/lib/rstudio/bin/{pandoc/pandoc,pandoc}
'';
for f in {${if server
then "crash-handler-proxy,postback,r-ldpath,rpostback,rserver,rserver-pam,rsession,rstudio-server"
else "diagnostics,rpostback,rstudio"}}; do
ln -s $out/lib/rstudio/bin/$f $out/bin
done
qtWrapperArgs = [
"--suffix PATH : ${lib.makeBinPath [ gnumake ]}"
];
for f in .gitignore .Rbuildignore LICENSE README; do
find . -name $f -delete
done
rm -r $out/lib/rstudio/{INSTALL,COPYING,NOTICE,README.md,SOURCE,VERSION}
rm -r $out/lib/rstudio/bin/{pandoc/pandoc,pandoc}
'';
desktopItems = [
(makeDesktopItem {
name = "${pname}";
exec = "rstudio %F";
icon = "rstudio";
desktopName = "RStudio";
genericName = "IDE";
comment = meta.description;
categories = "Development;";
mimeType = "text/x-r-source;text/x-r;text/x-R;text/x-r-doc;text/x-r-sweave;text/x-r-markdown;text/x-r-html;text/x-r-presentation;application/x-r-data;application/x-r-project;text/x-r-history;text/x-r-profile;text/x-tex;text/x-markdown;text/html;text/css;text/javascript;text/x-chdr;text/x-csrc;text/x-c++hdr;text/x-c++src;";
})
];
meta = with lib; {
inherit description;
homepage = "https://www.rstudio.com/";
license = licenses.agpl3Only;
maintainers = with maintainers; [ ciil cfhammill ];
platforms = platforms.linux;
};
meta = with lib; {
description = "Set of integrated tools for the R language";
homepage = "https://www.rstudio.com/";
license = licenses.agpl3Only;
maintainers = with maintainers; [ ciil ];
platforms = platforms.linux;
};
}
passthru = { inherit server; };
} // lib.optionalAttrs (!server) {
qtWrapperArgs = [
"--suffix PATH : ${lib.makeBinPath [ gnumake ]}"
];
desktopItems = [
(makeDesktopItem {
name = pname;
exec = "rstudio %F";
icon = "rstudio";
desktopName = "RStudio";
genericName = "IDE";
comment = description;
categories = "Development;";
mimeType = "text/x-r-source;text/x-r;text/x-R;text/x-r-doc;text/x-r-sweave;text/x-r-markdown;text/x-r-html;text/x-r-presentation;application/x-r-data;application/x-r-project;text/x-r-history;text/x-r-profile;text/x-tex;text/x-markdown;text/html;text/css;text/javascript;text/x-chdr;text/x-csrc;text/x-c++hdr;text/x-c++src;";
})
];
})

View file

@ -1,16 +1,23 @@
{ lib, runCommand, R, rstudio, wrapQtAppsHook, recommendedPackages, packages, qtbase }:
{ lib
, runCommand
, R
, rstudio
, makeWrapper
, wrapQtAppsHook
, recommendedPackages
, packages
, fontconfig
}:
let
qtVersion = with lib.versions; "${major qtbase.version}.${minor qtbase.version}";
in
runCommand (rstudio.name + "-wrapper") {
runCommand (rstudio.name + "-wrapper")
{
preferLocalBuild = true;
allowSubstitutes = false;
nativeBuildInputs = [wrapQtAppsHook];
nativeBuildInputs = [ (if rstudio.server then makeWrapper else wrapQtAppsHook) ];
dontWrapQtApps = true;
buildInputs = [R rstudio] ++ recommendedPackages ++ packages;
buildInputs = [ R rstudio ] ++ recommendedPackages ++ packages;
# rWrapper points R to a specific set of packages by using a wrapper
# (as in https://nixos.org/nixpkgs/manual/#r-packages) which sets
@ -22,14 +29,27 @@ runCommand (rstudio.name + "-wrapper") {
# uses R_PROFILE_USER to load this code at startup in RStudio.
fixLibsR = "fix_libs.R";
}
''
mkdir $out
ln -s ${rstudio}/share $out
echo "# Autogenerated by wrapper-rstudio.nix from R_LIBS_SITE" > $out/$fixLibsR
echo -n ".libPaths(c(.libPaths(), \"" >> $out/$fixLibsR
echo -n $R_LIBS_SITE | sed -e 's/:/", "/g' >> $out/$fixLibsR
echo -n "\"))" >> $out/$fixLibsR
echo >> $out/$fixLibsR
makeQtWrapper ${rstudio}/bin/rstudio $out/bin/rstudio \
--set R_PROFILE_USER $out/$fixLibsR
''
(
''
mkdir -p $out/bin
ln -s ${rstudio}/share $out
echo "# Autogenerated by wrapper-rstudio.nix from R_LIBS_SITE" > $out/$fixLibsR
echo -n ".libPaths(c(.libPaths(), \"" >> $out/$fixLibsR
echo -n $R_LIBS_SITE | sed -e 's/:/", "/g' >> $out/$fixLibsR
echo -n "\"))" >> $out/$fixLibsR
echo >> $out/$fixLibsR
'' +
(if
rstudio.server then ''
makeWrapper ${rstudio}/bin/rsession $out/bin/rsession \
--set R_PROFILE_USER $out/$fixLibsR --set FONTCONFIG_FILE ${fontconfig.out}/etc/fonts/fonts.conf
makeWrapper ${rstudio}/bin/rserver $out/bin/rserver \
--add-flags --rsession-path=$out/bin/rsession
''
else
''
makeQtWrapper ${rstudio}/bin/rstudio $out/bin/rstudio \
--set R_PROFILE_USER $out/$fixLibsR
'')
)

View file

@ -20754,6 +20754,8 @@ with pkgs;
packages = [];
};
rstudioServerWrapper = rstudioWrapper.override { rstudio = rstudio-server; };
rPackages = dontRecurseIntoAttrs (callPackage ../development/r-modules {
overrides = (config.rPackageOverrides or (_: {})) pkgs;
});
@ -28384,6 +28386,8 @@ with pkgs;
jdk = jdk8;
};
rstudio-server = rstudio.override { server = true; };
rsync = callPackage ../applications/networking/sync/rsync (config.rsync or {});
rrsync = callPackage ../applications/networking/sync/rsync/rrsync.nix {};