From b4e0e93b2fa39bb12308825fbc4459df7a932361 Mon Sep 17 00:00:00 2001 From: TomaSajt <62384384+TomaSajt@users.noreply.github.com> Date: Sun, 8 Dec 2024 16:16:21 +0100 Subject: [PATCH] rstudio: build as electron app, add darwin support --- pkgs/applications/editors/rstudio/default.nix | 137 +++++++++++++++--- .../editors/rstudio/fix-darwin-cmake.patch | 72 +++++++++ .../editors/rstudio/r-location.patch | 27 +++- .../rstudio/update-nan-and-node-abi.patch | 72 +++++++++ 4 files changed, 276 insertions(+), 32 deletions(-) create mode 100644 pkgs/applications/editors/rstudio/fix-darwin-cmake.patch create mode 100644 pkgs/applications/editors/rstudio/update-nan-and-node-abi.patch diff --git a/pkgs/applications/editors/rstudio/default.nix b/pkgs/applications/editors/rstudio/default.nix index 4df8046de30fe..2ae1b6bf0f9e1 100644 --- a/pkgs/applications/editors/rstudio/default.nix +++ b/pkgs/applications/editors/rstudio/default.nix @@ -5,11 +5,11 @@ fetchFromGitHub, replaceVars, cmake, - boost, + boost186, zlib, openssl, R, - libsForQt5, + fontconfig, quarto, libuuid, hunspellDicts, @@ -21,16 +21,26 @@ yaml-cpp, soci, sqlite, + apple-sdk_11, + xcbuild, nodejs, + npmHooks, + fetchNpmDeps, yarn, yarnConfigHook, fetchYarnDeps, + zip, + git, + makeWrapper, + electron_32, server ? false, # build server version pam, nixosTests, }: let + electron = electron_32; + mathJaxSrc = fetchzip { url = "https://s3.amazonaws.com/rstudio-buildtools/mathjax-27.zip"; hash = "sha256-J7SZK/9q3HcXTD7WFHxvh++ttuCd89Vc4SEBrUEU0AI="; @@ -66,7 +76,7 @@ stdenv.mkDerivation rec { src = fetchFromGitHub { owner = "rstudio"; repo = "rstudio"; - rev = "refs/tags/v${version}"; + tag = "v${version}"; hash = "sha256-j258eW1MYQrB6kkpjyolXdNuwQ3zSWv9so4q0QLsZuw="; }; @@ -78,14 +88,19 @@ stdenv.mkDerivation rec { nodejs yarn yarnConfigHook + zip + git ] + ++ lib.optionals stdenv.hostPlatform.isDarwin [ xcbuild ] ++ lib.optionals (!server) [ - libsForQt5.wrapQtAppsHook + (nodejs.python.withPackages (ps: [ ps.setuptools ])) + npmHooks.npmConfigHook + makeWrapper ]; buildInputs = [ - boost + boost186 zlib openssl R @@ -94,30 +109,32 @@ stdenv.mkDerivation rec { soci sqlite.dev ] + ++ lib.optionals stdenv.hostPlatform.isDarwin [ apple-sdk_11 ] ++ lib.optionals server [ pam ] - ++ lib.optionals (!server) [ - libsForQt5.qtbase - libsForQt5.qtxmlpatterns - libsForQt5.qtsensors - libsForQt5.qtwebengine - libsForQt5.qtwebchannel - ]; + ++ lib.optionals (!server) [ fontconfig ]; cmakeFlags = [ - (lib.cmakeFeature "RSTUDIO_TARGET" (if server then "Server" else "Desktop")) + (lib.cmakeFeature "RSTUDIO_TARGET" (if server then "Server" else "Electron")) (lib.cmakeBool "RSTUDIO_USE_SYSTEM_SOCI" true) (lib.cmakeBool "RSTUDIO_USE_SYSTEM_BOOST" true) (lib.cmakeBool "RSTUDIO_USE_SYSTEM_YAML_CPP" true) (lib.cmakeBool "RSTUDIO_DISABLE_CHECK_FOR_UPDATES" true) (lib.cmakeBool "QUARTO_ENABLED" true) - (lib.cmakeFeature "CMAKE_INSTALL_PREFIX" "${placeholder "out"}/lib/rstudio") + (lib.cmakeBool "RSTUDIO_CRASHPAD_ENABLED" false) # need to explicitly disable for x86_64-darwin + (lib.cmakeFeature "CMAKE_INSTALL_PREFIX" ( + (placeholder "out") + (if stdenv.isDarwin then "/Applications" else "/lib/rstudio") + )) ] ++ lib.optionals (!server) [ - (lib.cmakeFeature "QT_QMAKE_EXECUTABLE" "${libsForQt5.qmake}/bin/qmake") - (lib.cmakeBool "RSTUDIO_INSTALL_FREEDESKTOP" true) + (lib.cmakeFeature "NODEJS" (lib.getExe' nodejs "node")) + (lib.cmakeFeature "NPM" (lib.getExe' nodejs "npm")) + (lib.cmakeBool "RSTUDIO_INSTALL_FREEDESKTOP" stdenv.hostPlatform.isLinux) ]; + # on Darwin, cmake uses find_library to locate R instead of using the PATH + env.NIX_LDFLAGS = "-L${R}/lib/R/lib"; + patches = [ # Hack RStudio to only use the input R and provided libclang. (replaceVars ./r-location.patch { @@ -132,6 +149,9 @@ stdenv.mkDerivation rec { ./dont-yarn-install.patch ./dont-assume-pandoc-in-quarto.patch ./boost-1.86.patch + ./fix-darwin-cmake.patch + # needed for rebuilding for later electron versions + ./update-nan-and-node-abi.patch ]; postPatch = '' @@ -151,6 +171,21 @@ stdenv.mkDerivation rec { dontYarnInstallDeps = true; # will call manually in preConfigure + npmRoot = "src/node/desktop"; + + # don't build native modules with node headers + npmFlags = [ "--ignore-scripts" ]; + + npmDeps = fetchNpmDeps { + name = "rstudio-${version}-npm-deps"; + inherit src; + patches = [ ./update-nan-and-node-abi.patch ]; + postPatch = "cd ${npmRoot}"; + hash = "sha256-CtHCN4sWeHNDd59TV/TgTC4d6h7X1Cl4E/aJkAfRk7g="; + }; + + env.ELECTRON_SKIP_BINARY_DOWNLOAD = "1"; + preConfigure = '' # set up node_modules directory inside quarto so that panmirror can be built mkdir src/gwt/lib/quarto @@ -182,23 +217,76 @@ stdenv.mkDerivation rec { # version in CMakeGlobals.txt (RSTUDIO_INSTALLED_NODE_VERSION) mkdir -p dependencies/common/node ln -s ${nodejs} dependencies/common/node/18.20.3 + + ${lib.optionalString (!server) '' + pushd $npmRoot + + substituteInPlace package.json \ + --replace-fail "npm ci && " "" + + ### use our electron's headers as our nodedir so that electron-rebuild can rebuild for our electron's ABI + # note: because we're not specifying the `--version` flag to pass to electron-rebuild in forge.config.js, + # it thinks it's compiling for the ABI version chosen by upstream, but this doesn't seem to cause any issues + mkdir -p electron-headers + tar xf ${electron.headers} -C electron-headers --strip-components=1 + export npm_config_nodedir="$(pwd)/electron-headers" + + ### override the detected electron version + substituteInPlace node_modules/@electron-forge/core-utils/dist/electron-version.js \ + --replace-fail "return version" "return '${electron.version}'" + + ### create the electron archive to be used by electron-packager + cp -r ${electron.dist} electron-dist + chmod -R u+w electron-dist + + pushd electron-dist + zip -0Xqr ../electron.zip . + popd + + rm -r electron-dist + + # force @electron/packager to use our electron instead of downloading it + substituteInPlace node_modules/@electron/packager/src/index.js \ + --replace-fail "await this.getElectronZipPath(downloadOpts)" "'$(pwd)/electron.zip'" + + # now that we patched everything, we still have to run the scripts we ignored with --ignore-scripts + npm rebuild + + popd + ''} ''; postInstall = '' mkdir -p $out/bin - ${lib.optionalString server '' + ${lib.optionalString (server && stdenv.hostPlatform.isLinux) '' ln -s $out/lib/rstudio/bin/{crash-handler-proxy,postback,r-ldpath,rpostback,rserver,rserver-pam,rsession,rstudio-server} $out/bin ''} - ${lib.optionalString (!server) '' - ln -s $out/lib/rstudio/bin/{diagnostics,rpostback,rstudio} $out/bin + ${lib.optionalString (!server && stdenv.hostPlatform.isLinux) '' + # remove unneeded electron files, since we'll wrap the app with our own electron + shopt -s extglob + rm -r $out/lib/rstudio/!(locales|resources|resources.pak) + + makeWrapper ${lib.getExe electron} "$out/bin/rstudio" \ + --add-flags "$out/lib/rstudio/resources/app/" \ + --set-default ELECTRON_FORCE_IS_PACKAGED 1 \ + --suffix PATH : ${lib.makeBinPath [ gnumake ]} + + ln -s $out/lib/rstudio/resources/app/bin/{diagnostics,rpostback} $out/bin ''} - ''; - qtWrapperArgs = lib.optionals (!server) [ - "--suffix PATH : ${lib.makeBinPath [ gnumake ]}" - ]; + ${lib.optionalString (server && stdenv.hostPlatform.isDarwin) '' + ln -s $out/Applications/RStudio.app/Contents/MacOS/{crash-handler-proxy,postback,r-ldpath,rpostback,rserver,rserver-pam,rsession,rstudio-server} $out/bin + ''} + + ${lib.optionalString (!server && stdenv.hostPlatform.isDarwin) '' + # electron can't find its files if we use a symlink here + makeWrapper $out/Applications/RStudio.app/Contents/MacOS/RStudio $out/bin/rstudio + + ln -s $out/Applications/RStudio.app/Contents/Resources/app/bin/{diagnostics,rpostback} $out/bin + ''} + ''; passthru = { inherit server; @@ -215,8 +303,9 @@ stdenv.mkDerivation rec { maintainers = with lib.maintainers; [ ciil cfhammill + tomasajt ]; mainProgram = "rstudio" + lib.optionalString server "-server"; - platforms = lib.platforms.linux; + platforms = lib.platforms.linux ++ lib.platforms.darwin; }; } diff --git a/pkgs/applications/editors/rstudio/fix-darwin-cmake.patch b/pkgs/applications/editors/rstudio/fix-darwin-cmake.patch new file mode 100644 index 0000000000000..012467e54ce5d --- /dev/null +++ b/pkgs/applications/editors/rstudio/fix-darwin-cmake.patch @@ -0,0 +1,72 @@ +diff --git a/CMakeGlobals.txt b/CMakeGlobals.txt +index 8868c4426e..19b2fa0892 100644 +--- a/CMakeGlobals.txt ++++ b/CMakeGlobals.txt +@@ -345,6 +345,7 @@ if (APPLE) + set(RSTUDIO_INSTALL_SUPPORTING RStudio.app/Contents/Resources/app) + # handles Quarto share when not stored alongside bin + set(RSTUDIO_INSTALL_RESOURCES RStudio.app/Contents/Resources) ++ set(RSTUDIO_INSTALL_ELECTRON .) + else() + set(RSTUDIO_INSTALL_BIN RStudio.app/Contents/MacOS) + set(RSTUDIO_INSTALL_SUPPORTING RStudio.app/Contents/Resources) +diff --git a/src/cpp/CMakeLists.txt b/src/cpp/CMakeLists.txt +index b47f04f84d..928165d612 100644 +--- a/src/cpp/CMakeLists.txt ++++ b/src/cpp/CMakeLists.txt +@@ -240,7 +240,7 @@ endif() + # determine whether we should statically link boost. we always do this + # unless we are building a non-packaged build on linux (in which case + # boost dynamic libraries are presumed to be installed on the system ldpath) +-if(APPLE OR WIN32 OR RSTUDIO_PACKAGE_BUILD) ++if(WIN32 OR RSTUDIO_PACKAGE_BUILD) + set(Boost_USE_STATIC_LIBS ON) + endif() + +@@ -475,7 +475,7 @@ endif() + + # find SOCI libraries + if(UNIX) +- if(NOT APPLE AND RSTUDIO_USE_SYSTEM_SOCI) ++ if(RSTUDIO_USE_SYSTEM_SOCI) + find_library(SOCI_CORE_LIB NAMES "libsoci_core.a" "soci_core" REQUIRED) + find_library(SOCI_SQLITE_LIB NAMES "libsoci_sqlite3.a" "soci_sqlite3" REQUIRED) + if(RSTUDIO_PRO_BUILD) +diff --git a/src/node/desktop/CMakeLists.txt b/src/node/desktop/CMakeLists.txt +index 2f4d9290e6..7ef0c0ea07 100644 +--- a/src/node/desktop/CMakeLists.txt ++++ b/src/node/desktop/CMakeLists.txt +@@ -119,7 +119,9 @@ if (APPLE) + # configure Info.plist + configure_file (${CMAKE_CURRENT_SOURCE_DIR}/Info.plist.in + ${CMAKE_CURRENT_SOURCE_DIR}/Info.plist) ++endif() + ++if (false) + # copy sources to build directory. note that the build directory cannot + # be the "true" CMake directory as some files are resolved relative to + # the desktop project's relative path in the application structure +@@ -230,16 +232,21 @@ if(WIN32) + PROGRAMS ${CMAKE_INSTALL_SYSTEM_RUNTIME_LIBS} + DESTINATION "${RSTUDIO_INSTALL_BIN}") + +-elseif(LINUX) ++elseif(LINUX OR APPLE) + + if(UNAME_M STREQUAL aarch64) + set(ELECTRON_ARCH arm64) + else() + set(ELECTRON_ARCH x64) + endif() ++ if(APPLE) ++ set(ELECTRON_PLATFORM darwin) ++ else() ++ set(ELECTRON_PLATFORM linux) ++ endif() + + install( +- DIRECTORY "${ELECTRON_BUILD_DIR}/out/RStudio-linux-${ELECTRON_ARCH}/" ++ DIRECTORY "${ELECTRON_BUILD_DIR}/out/RStudio-${ELECTRON_PLATFORM}-${ELECTRON_ARCH}/" + DIRECTORY_PERMISSIONS + OWNER_READ OWNER_WRITE OWNER_EXECUTE + GROUP_READ GROUP_EXECUTE diff --git a/pkgs/applications/editors/rstudio/r-location.patch b/pkgs/applications/editors/rstudio/r-location.patch index 44e54b36e0c45..d00308a103517 100644 --- a/pkgs/applications/editors/rstudio/r-location.patch +++ b/pkgs/applications/editors/rstudio/r-location.patch @@ -1,23 +1,23 @@ diff --git a/src/cpp/core/r_util/REnvironmentPosix.cpp b/src/cpp/core/r_util/REnvironmentPosix.cpp -index dbc9a9a1..9a526a86 100644 +index a4e964d49d..512707801b 100644 --- a/src/cpp/core/r_util/REnvironmentPosix.cpp +++ b/src/cpp/core/r_util/REnvironmentPosix.cpp -@@ -107,12 +107,9 @@ FilePath systemDefaultRScript(std::string* pErrMsg) +@@ -108,13 +108,7 @@ FilePath systemDefaultRScript(std::string* pErrMsg) { // check fallback paths std::vector rScriptPaths = { - "/usr/bin/R", - "/usr/local/bin/R", - "/opt/local/bin/R", -+ "@R@/bin/R" - #ifdef __APPLE__ +- #ifdef __APPLE__ - "/opt/homebrew/bin/R", - "/Library/Frameworks/R.framework/Resources/bin/R", -+ "@R@/bin/R", - #endif +- #endif ++ "@R@/bin/R" }; -@@ -225,8 +222,7 @@ FilePath systemDefaultRScript(std::string* pErrMsg) + return scanForRScript(rScriptPaths, pErrMsg); +@@ -226,8 +220,7 @@ FilePath systemDefaultRScript(std::string* pErrMsg) // scan in standard locations as a fallback std::string scanErrMsg; std::vector rScriptPaths; @@ -27,4 +27,15 @@ index dbc9a9a1..9a526a86 100644 FilePath scriptPath = scanForRScript(rScriptPaths, &scanErrMsg); if (scriptPath.isEmpty()) { - +diff --git a/src/node/desktop/src/main/detect-r.ts b/src/node/desktop/src/main/detect-r.ts +index 5b768b7cbf..c0efeac0fe 100644 +--- a/src/node/desktop/src/main/detect-r.ts ++++ b/src/node/desktop/src/main/detect-r.ts +@@ -305,6 +305,7 @@ writeLines(sep = "\x1F", c( + } + + export function scanForR(): Expected { ++ return ok('@R@/bin/R'); + // if the RSTUDIO_WHICH_R environment variable is set, use that + // note that this does not pick up variables set in a user's bash profile, for example + const rstudioWhichR = getenv('RSTUDIO_WHICH_R'); diff --git a/pkgs/applications/editors/rstudio/update-nan-and-node-abi.patch b/pkgs/applications/editors/rstudio/update-nan-and-node-abi.patch new file mode 100644 index 0000000000000..7ba18405769a1 --- /dev/null +++ b/pkgs/applications/editors/rstudio/update-nan-and-node-abi.patch @@ -0,0 +1,72 @@ +diff --git a/src/node/desktop/package-lock.json b/src/node/desktop/package-lock.json +index 01c11edaf4..5e356470be 100644 +--- a/src/node/desktop/package-lock.json ++++ b/src/node/desktop/package-lock.json +@@ -19,7 +19,7 @@ + "line-reader": "0.4.0", + "lodash.debounce": "4.0.8", + "net-ipc": "2.1.0", +- "node-abi": "3.52.0", ++ "node-abi": "^3.71.0", + "node-addon-api": "7.0.0", + "node-system-fonts": "1.0.1", + "properties-reader": "2.3.0", +@@ -63,7 +63,7 @@ + "json-schema-to-typescript": "13.1.1", + "lint-staged": "15.2.0", + "mocha": "10.2.0", +- "nan": "2.18.0", ++ "nan": "^2.22.0", + "node-loader": "2.0.0", + "nyc": "15.1.0", + "prettier": "3.1.1", +@@ -9768,9 +9768,10 @@ + } + }, + "node_modules/nan": { +- "version": "2.18.0", +- "resolved": "https://registry.npmjs.org/nan/-/nan-2.18.0.tgz", +- "integrity": "sha512-W7tfG7vMOGtD30sHoZSSc/JVYiyDPEyQVso/Zz+/uQd0B0L46gtC+pHha5FFMRpil6fm/AoEcRWyOVi4+E/f8w==" ++ "version": "2.22.0", ++ "resolved": "https://registry.npmjs.org/nan/-/nan-2.22.0.tgz", ++ "integrity": "sha512-nbajikzWTMwsW+eSsNm3QwlOs7het9gGJU5dDZzRTQGk03vyBOauxgI4VakDzE0PtsGTmXPsXTbbjVhRwR5mpw==", ++ "license": "MIT" + }, + "node_modules/nanoid": { + "version": "3.3.3", +@@ -9863,9 +9864,10 @@ + } + }, + "node_modules/node-abi": { +- "version": "3.52.0", +- "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.52.0.tgz", +- "integrity": "sha512-JJ98b02z16ILv7859irtXn4oUaFWADtvkzy2c0IAatNVX2Mc9Yoh8z6hZInn3QwvMEYhHuQloYi+TTQy67SIdQ==", ++ "version": "3.71.0", ++ "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.71.0.tgz", ++ "integrity": "sha512-SZ40vRiy/+wRTf21hxkkEjPJZpARzUMVcJoQse2EF8qkUWbbO2z7vd5oA/H6bVH6SZQ5STGcu0KRDS7biNRfxw==", ++ "license": "MIT", + "dependencies": { + "semver": "^7.3.5" + }, +diff --git a/src/node/desktop/package.json b/src/node/desktop/package.json +index 059c39f9d7..ca9066077d 100644 +--- a/src/node/desktop/package.json ++++ b/src/node/desktop/package.json +@@ -59,7 +59,7 @@ + "json-schema-to-typescript": "13.1.1", + "lint-staged": "15.2.0", + "mocha": "10.2.0", +- "nan": "2.18.0", ++ "nan": "^2.22.0", + "node-loader": "2.0.0", + "nyc": "15.1.0", + "process": "0.11.10", +@@ -83,7 +83,7 @@ + "line-reader": "0.4.0", + "lodash.debounce": "4.0.8", + "net-ipc": "2.1.0", +- "node-abi": "3.52.0", ++ "node-abi": "^3.71.0", + "node-addon-api": "7.0.0", + "node-system-fonts": "1.0.1", + "properties-reader": "2.3.0",