Skip to content

Commit bbf1378

Browse files
committed
rstudio: build as electron app, add darwin support
1 parent eddc8ac commit bbf1378

File tree

4 files changed

+385
-33
lines changed

4 files changed

+385
-33
lines changed

pkgs/applications/editors/rstudio/default.nix

Lines changed: 125 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
zlib,
1111
openssl,
1212
R,
13-
libsForQt5,
13+
fontconfig,
1414
quarto,
1515
libuuid,
1616
hunspellDicts,
@@ -22,16 +22,34 @@
2222
yaml-cpp,
2323
soci,
2424
sqlite,
25+
apple-sdk_11,
26+
xcbuild,
2527
nodejs,
28+
npmHooks,
29+
fetchNpmDeps,
2630
yarn,
2731
yarnConfigHook,
2832
fetchYarnDeps,
33+
zip,
34+
git,
35+
makeWrapper,
36+
electron_33,
2937
server ? false, # build server version
3038
pam,
3139
nixosTests,
3240
}:
3341

3442
let
43+
# Note: we shouldn't use the latest electron here, since the node-abi dependency might
44+
# need to be updated every time the latest electron gets a new abi version number
45+
electron = electron_33;
46+
47+
# unpack tarball containing electron's headers
48+
electron-headers = runCommand "electron-headers" { } ''
49+
mkdir -p $out
50+
tar -C $out --strip-components=1 -xvf ${electron.headers}
51+
'';
52+
3553
mathJaxSrc = fetchzip {
3654
url = "https://s3.amazonaws.com/rstudio-buildtools/mathjax-27.zip";
3755
hash = "sha256-J7SZK/9q3HcXTD7WFHxvh++ttuCd89Vc4SEBrUEU0AI=";
@@ -58,8 +76,9 @@ let
5876
# rstudio assumes quarto bundles pandoc into bin/tools/
5977
quartoWrapper = runCommand "quarto-wrapper" { } ''
6078
mkdir -p $out/bin/tools
61-
ln -s ${lib.getExe quarto} $out/bin/quarto
6279
ln -s ${lib.getExe pandoc} $out/bin/tools/pandoc
80+
ln -s ${lib.getExe quarto} $out/bin/quarto
81+
ln -s ${quarto}/share $out/share
6382
'';
6483
in
6584
stdenv.mkDerivation rec {
@@ -74,7 +93,7 @@ stdenv.mkDerivation rec {
7493
src = fetchFromGitHub {
7594
owner = "rstudio";
7695
repo = "rstudio";
77-
rev = "refs/tags/v${version}";
96+
tag = "v${version}";
7897
hash = "sha256-j258eW1MYQrB6kkpjyolXdNuwQ3zSWv9so4q0QLsZuw=";
7998
};
8099

@@ -86,9 +105,14 @@ stdenv.mkDerivation rec {
86105
nodejs
87106
yarn
88107
yarnConfigHook
108+
zip
109+
git
89110
]
111+
++ lib.optionals stdenv.hostPlatform.isDarwin [ xcbuild ]
90112
++ lib.optionals (!server) [
91-
libsForQt5.wrapQtAppsHook
113+
(nodejs.python.withPackages (ps: [ ps.setuptools ]))
114+
npmHooks.npmConfigHook
115+
makeWrapper
92116
];
93117

94118
buildInputs =
@@ -102,30 +126,30 @@ stdenv.mkDerivation rec {
102126
soci
103127
sqlite.dev
104128
]
129+
++ lib.optionals stdenv.hostPlatform.isDarwin [ apple-sdk_11 ]
105130
++ lib.optionals server [ pam ]
106-
++ lib.optionals (!server) [
107-
libsForQt5.qtbase
108-
libsForQt5.qtxmlpatterns
109-
libsForQt5.qtsensors
110-
libsForQt5.qtwebengine
111-
libsForQt5.qtwebchannel
112-
];
131+
++ lib.optionals (!server) [ fontconfig ];
113132

114133
cmakeFlags =
115134
[
116-
(lib.cmakeFeature "RSTUDIO_TARGET" (if server then "Server" else "Desktop"))
135+
(lib.cmakeFeature "RSTUDIO_TARGET" (if server then "Server" else "Electron"))
117136
(lib.cmakeBool "RSTUDIO_USE_SYSTEM_SOCI" true)
118137
(lib.cmakeBool "RSTUDIO_USE_SYSTEM_BOOST" true)
119138
(lib.cmakeBool "RSTUDIO_USE_SYSTEM_YAML_CPP" true)
120139
(lib.cmakeBool "RSTUDIO_DISABLE_CHECK_FOR_UPDATES" true)
121140
(lib.cmakeBool "QUARTO_ENABLED" true)
122-
(lib.cmakeFeature "CMAKE_INSTALL_PREFIX" "${placeholder "out"}/lib/rstudio")
141+
(lib.cmakeBool "RSTUDIO_CRASHPAD_ENABLED" false) # This is a NOOP except on x86_64-darwin
142+
(lib.cmakeFeature "CMAKE_INSTALL_PREFIX" (
143+
(placeholder "out") + (if stdenv.isDarwin then "/Applications" else "/lib/rstudio")
144+
))
123145
]
124146
++ lib.optionals (!server) [
125-
(lib.cmakeFeature "QT_QMAKE_EXECUTABLE" "${libsForQt5.qmake}/bin/qmake")
126-
(lib.cmakeBool "RSTUDIO_INSTALL_FREEDESKTOP" true)
147+
(lib.cmakeBool "RSTUDIO_INSTALL_FREEDESKTOP" stdenv.hostPlatform.isLinux)
127148
];
128149

150+
# on Darwin, cmake uses find_library to locate R instead of using the PATH
151+
env.NIX_LDFLAGS = "-L${R}/lib/R/lib";
152+
129153
patches = [
130154
# Hack RStudio to only use the input R and provided libclang.
131155
(replaceVars ./r-location.patch {
@@ -139,6 +163,9 @@ stdenv.mkDerivation rec {
139163
./ignore-etc-os-release.patch
140164
./dont-yarn-install.patch
141165
./boost-1.86.patch
166+
./fix-darwin.patch
167+
# needed for rebuilding for later electron versions
168+
./update-nan-and-node-abi.patch
142169
];
143170

144171
postPatch = ''
@@ -158,6 +185,21 @@ stdenv.mkDerivation rec {
158185

159186
dontYarnInstallDeps = true; # will call manually in preConfigure
160187

188+
npmRoot = "src/node/desktop";
189+
190+
# don't build native modules with node headers
191+
npmFlags = [ "--ignore-scripts" ];
192+
193+
npmDeps = fetchNpmDeps {
194+
name = "rstudio-${version}-npm-deps";
195+
inherit src;
196+
patches = [ ./update-nan-and-node-abi.patch ];
197+
postPatch = "cd ${npmRoot}";
198+
hash = "sha256-CtHCN4sWeHNDd59TV/TgTC4d6h7X1Cl4E/aJkAfRk7g=";
199+
};
200+
201+
env.ELECTRON_SKIP_BINARY_DOWNLOAD = "1";
202+
161203
preConfigure = ''
162204
# set up node_modules directory inside quarto so that panmirror can be built
163205
mkdir src/gwt/lib/quarto
@@ -182,26 +224,84 @@ stdenv.mkDerivation rec {
182224
# version in dependencies/common/install-mathjax
183225
ln -s ${mathJaxSrc} dependencies/mathjax-27
184226
185-
# version in CMakeGlobals.txt (RSTUDIO_INSTALLED_NODE_VERSION)
186227
mkdir -p dependencies/common/node
228+
# node used by cmake
229+
# version in CMakeGlobals.txt (RSTUDIO_NODE_VERSION)
230+
ln -s ${nodejs} dependencies/common/node/18.18.2
231+
# node used at runtime
232+
# version in CMakeGlobals.txt (RSTUDIO_INSTALLED_NODE_VERSION)
187233
ln -s ${nodejs} dependencies/common/node/18.20.3
234+
235+
${lib.optionalString (!server) ''
236+
pushd $npmRoot
237+
238+
substituteInPlace package.json \
239+
--replace-fail "npm ci && " ""
240+
241+
# use electron's headers to make node-gyp compile against the electron ABI
242+
export npm_config_nodedir="${electron-headers}"
243+
244+
### override the detected electron version
245+
substituteInPlace node_modules/@electron-forge/core-utils/dist/electron-version.js \
246+
--replace-fail "return version" "return '${electron.version}'"
247+
248+
### create the electron archive to be used by electron-packager
249+
cp -r ${electron.dist} electron-dist
250+
chmod -R u+w electron-dist
251+
252+
pushd electron-dist
253+
zip -0Xqr ../electron.zip .
254+
popd
255+
256+
rm -r electron-dist
257+
258+
# force @electron/packager to use our electron instead of downloading it
259+
substituteInPlace node_modules/@electron/packager/src/index.js \
260+
--replace-fail "await this.getElectronZipPath(downloadOpts)" "'$(pwd)/electron.zip'"
261+
262+
# Work around known nan issue for electron_33 and above
263+
# https://github.com/nodejs/nan/issues/978
264+
substituteInPlace node_modules/nan/nan.h \
265+
--replace-fail '#include "nan_scriptorigin.h"' ""
266+
267+
# now that we patched everything, we still have to run the scripts we ignored with --ignore-scripts
268+
npm rebuild
269+
270+
popd
271+
''}
188272
'';
189273

190274
postInstall = ''
191275
mkdir -p $out/bin
192276
193-
${lib.optionalString server ''
277+
${lib.optionalString (server && stdenv.hostPlatform.isLinux) ''
194278
ln -s $out/lib/rstudio/bin/{crash-handler-proxy,postback,r-ldpath,rpostback,rserver,rserver-pam,rsession,rstudio-server} $out/bin
195279
''}
196280
197-
${lib.optionalString (!server) ''
198-
ln -s $out/lib/rstudio/bin/{diagnostics,rpostback,rstudio} $out/bin
281+
${lib.optionalString (!server && stdenv.hostPlatform.isLinux) ''
282+
# remove unneeded electron files, since we'll wrap the app with our own electron
283+
shopt -s extglob
284+
rm -r $out/lib/rstudio/!(locales|resources|resources.pak)
285+
286+
makeWrapper ${lib.getExe electron} "$out/bin/rstudio" \
287+
--add-flags "$out/lib/rstudio/resources/app/" \
288+
--set-default ELECTRON_FORCE_IS_PACKAGED 1 \
289+
--suffix PATH : ${lib.makeBinPath [ gnumake ]}
290+
291+
ln -s $out/lib/rstudio/resources/app/bin/{diagnostics,rpostback} $out/bin
199292
''}
200-
'';
201293
202-
qtWrapperArgs = lib.optionals (!server) [
203-
"--suffix PATH : ${lib.makeBinPath [ gnumake ]}"
204-
];
294+
${lib.optionalString (server && stdenv.hostPlatform.isDarwin) ''
295+
ln -s $out/Applications/RStudio.app/Contents/MacOS/{crash-handler-proxy,postback,r-ldpath,rpostback,rserver,rserver-pam,rsession,rstudio-server} $out/bin
296+
''}
297+
298+
${lib.optionalString (!server && stdenv.hostPlatform.isDarwin) ''
299+
# electron can't find its files if we use a symlink here
300+
makeWrapper $out/Applications/RStudio.app/Contents/MacOS/RStudio $out/bin/rstudio
301+
302+
ln -s $out/Applications/RStudio.app/Contents/Resources/app/bin/{diagnostics,rpostback} $out/bin
303+
''}
304+
'';
205305

206306
passthru = {
207307
inherit server;
@@ -211,15 +311,15 @@ stdenv.mkDerivation rec {
211311
};
212312

213313
meta = {
214-
broken = (stdenv.hostPlatform.isLinux && stdenv.hostPlatform.isAarch64);
215314
description = "Set of integrated tools for the R language";
216315
homepage = "https://www.rstudio.com/";
217316
license = lib.licenses.agpl3Only;
218317
maintainers = with lib.maintainers; [
219318
ciil
220319
cfhammill
320+
tomasajt
221321
];
222322
mainProgram = "rstudio" + lib.optionalString server "-server";
223-
platforms = lib.platforms.linux;
323+
platforms = lib.platforms.linux ++ lib.platforms.darwin;
224324
};
225325
}

0 commit comments

Comments
 (0)