11{
22 lib ,
33 stdenv ,
4+ runCommand ,
45 fetchzip ,
56 fetchFromGitHub ,
67 replaceVars ,
910 zlib ,
1011 openssl ,
1112 R ,
12- libsForQt5 ,
13+ fontconfig ,
1314 quarto ,
1415 libuuid ,
1516 hunspellDicts ,
2122 yaml-cpp ,
2223 soci ,
2324 sqlite ,
25+ apple-sdk_11 ,
26+ xcbuild ,
2427 nodejs ,
28+ npmHooks ,
29+ fetchNpmDeps ,
2530 yarn ,
2631 yarnConfigHook ,
2732 fetchYarnDeps ,
33+ zip ,
34+ git ,
35+ makeWrapper ,
36+ electron_33 ,
2837 server ? false , # build server version
2938 pam ,
3039 nixosTests ,
3140} :
3241
3342let
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+
3453 mathJaxSrc = fetchzip {
3554 url = "https://s3.amazonaws.com/rstudio-buildtools/mathjax-27.zip" ;
3655 hash = "sha256-J7SZK/9q3HcXTD7WFHxvh++ttuCd89Vc4SEBrUEU0AI=" ;
5372 d : ! ( lib . hasAttr "dictFileName" d && lib . elem d . dictFileName ( map ( d : d . dictFileName ) largeDicts ) )
5473 ) hunspellDictionaries ;
5574 dictionaries = largeDicts ++ otherDicts ;
75+
76+ # rstudio assumes quarto bundles pandoc into bin/tools/
77+ quartoWrapper = runCommand "quarto-wrapper" { } ''
78+ mkdir -p $out/bin/tools
79+ 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
82+ '' ;
5683in
5784stdenv . mkDerivation rec {
5885 pname = "RStudio" ;
@@ -66,7 +93,7 @@ stdenv.mkDerivation rec {
6693 src = fetchFromGitHub {
6794 owner = "rstudio" ;
6895 repo = "rstudio" ;
69- rev = "refs/tags/ v${ version } " ;
96+ tag = "v${ version } " ;
7097 hash = "sha256-j258eW1MYQrB6kkpjyolXdNuwQ3zSWv9so4q0QLsZuw=" ;
7198 } ;
7299
@@ -78,9 +105,14 @@ stdenv.mkDerivation rec {
78105 nodejs
79106 yarn
80107 yarnConfigHook
108+ zip
109+ git
81110 ]
111+ ++ lib . optionals stdenv . hostPlatform . isDarwin [ xcbuild ]
82112 ++ lib . optionals ( ! server ) [
83- libsForQt5 . wrapQtAppsHook
113+ ( nodejs . python . withPackages ( ps : [ ps . setuptools ] ) )
114+ npmHooks . npmConfigHook
115+ makeWrapper
84116 ] ;
85117
86118 buildInputs =
@@ -94,30 +126,30 @@ stdenv.mkDerivation rec {
94126 soci
95127 sqlite . dev
96128 ]
129+ ++ lib . optionals stdenv . hostPlatform . isDarwin [ apple-sdk_11 ]
97130 ++ lib . optionals server [ pam ]
98- ++ lib . optionals ( ! server ) [
99- libsForQt5 . qtbase
100- libsForQt5 . qtxmlpatterns
101- libsForQt5 . qtsensors
102- libsForQt5 . qtwebengine
103- libsForQt5 . qtwebchannel
104- ] ;
131+ ++ lib . optionals ( ! server ) [ fontconfig ] ;
105132
106133 cmakeFlags =
107134 [
108- ( lib . cmakeFeature "RSTUDIO_TARGET" ( if server then "Server" else "Desktop " ) )
135+ ( lib . cmakeFeature "RSTUDIO_TARGET" ( if server then "Server" else "Electron " ) )
109136 ( lib . cmakeBool "RSTUDIO_USE_SYSTEM_SOCI" true )
110137 ( lib . cmakeBool "RSTUDIO_USE_SYSTEM_BOOST" true )
111138 ( lib . cmakeBool "RSTUDIO_USE_SYSTEM_YAML_CPP" true )
112139 ( lib . cmakeBool "RSTUDIO_DISABLE_CHECK_FOR_UPDATES" true )
113140 ( lib . cmakeBool "QUARTO_ENABLED" true )
114- ( 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+ ) )
115145 ]
116146 ++ lib . optionals ( ! server ) [
117- ( lib . cmakeFeature "QT_QMAKE_EXECUTABLE" "${ libsForQt5 . qmake } /bin/qmake" )
118- ( lib . cmakeBool "RSTUDIO_INSTALL_FREEDESKTOP" true )
147+ ( lib . cmakeBool "RSTUDIO_INSTALL_FREEDESKTOP" stdenv . hostPlatform . isLinux )
119148 ] ;
120149
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+
121153 patches = [
122154 # Hack RStudio to only use the input R and provided libclang.
123155 ( replaceVars ./r-location.patch {
@@ -130,8 +162,10 @@ stdenv.mkDerivation rec {
130162 ./fix-resources-path.patch
131163 ./ignore-etc-os-release.patch
132164 ./dont-yarn-install.patch
133- ./dont-assume-pandoc-in-quarto.patch
134165 ./boost-1.86.patch
166+ ./fix-darwin.patch
167+ # needed for rebuilding for later electron versions
168+ ./update-nan-and-node-abi.patch
135169 ] ;
136170
137171 postPatch = ''
@@ -151,6 +185,21 @@ stdenv.mkDerivation rec {
151185
152186 dontYarnInstallDeps = true ; # will call manually in preConfigure
153187
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+
154203 preConfigure = ''
155204 # set up node_modules directory inside quarto so that panmirror can be built
156205 mkdir src/gwt/lib/quarto
@@ -170,35 +219,89 @@ stdenv.mkDerivation rec {
170219 done
171220 done
172221
173- ln -s ${ quarto } dependencies/quarto
222+ ln -s ${ quartoWrapper } dependencies/quarto
174223
175224 # version in dependencies/common/install-mathjax
176225 ln -s ${ mathJaxSrc } dependencies/mathjax-27
177226
178- # version in CMakeGlobals.txt (PANDOC_VERSION)
179- mkdir -p dependencies/pandoc/2.18
180- ln -s ${ lib . getBin pandoc } /bin/* dependencies/pandoc/2.18
181-
182- # version in CMakeGlobals.txt (RSTUDIO_INSTALLED_NODE_VERSION)
183227 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)
184233 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+ '' }
185272 '' ;
186273
187274 postInstall = ''
188275 mkdir -p $out/bin
189276
190- ${ lib . optionalString server ''
277+ ${ lib . optionalString ( server && stdenv . hostPlatform . isLinux ) ''
191278 ln -s $out/lib/rstudio/bin/{crash-handler-proxy,postback,r-ldpath,rpostback,rserver,rserver-pam,rsession,rstudio-server} $out/bin
192279 '' }
193280
194- ${ lib . optionalString ( ! server ) ''
195- 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
196292 '' }
197- '' ;
198293
199- qtWrapperArgs = lib . optionals ( ! server ) [
200- "--suffix PATH : ${ lib . makeBinPath [ gnumake ] } "
201- ] ;
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+ '' ;
202305
203306 passthru = {
204307 inherit server ;
@@ -208,15 +311,15 @@ stdenv.mkDerivation rec {
208311 } ;
209312
210313 meta = {
211- broken = ( stdenv . hostPlatform . isLinux && stdenv . hostPlatform . isAarch64 ) ;
212314 description = "Set of integrated tools for the R language" ;
213315 homepage = "https://www.rstudio.com/" ;
214316 license = lib . licenses . agpl3Only ;
215317 maintainers = with lib . maintainers ; [
216318 ciil
217319 cfhammill
320+ tomasajt
218321 ] ;
219322 mainProgram = "rstudio" + lib . optionalString server "-server" ;
220- platforms = lib . platforms . linux ;
323+ platforms = lib . platforms . linux ++ lib . platforms . darwin ;
221324 } ;
222325}
0 commit comments