Make RustRover work with Rust from Nixpkgs

Initial commit

Changed files
+316
examples
src
+9
README.md
··· 1 + # Force RustRover to semi-work with Rust installs from Nixpkgs 2 + 3 + - Tested with a toolchain from Nixpkgs, see examples/nixpkgs.nix and Fenix, see examples/fenix.nix 4 + - Works by creating a toolchain in your `.idea` folder 5 + 6 + TODO: 7 + 8 + - [ ] A proper API for adding native libraries for linking 9 + - [ ] An example of using in Flakes (without relying on `toString ./.` being the impure project root)
+125
default.nix
··· 1 + { 2 + lib, 3 + mkShell, 4 + writeShellScriptBin, 5 + runCommand, 6 + buildEnv, 7 + coreutils, 8 + nushell, 9 + 10 + rustc, 11 + cargo, 12 + rustc-unwrapped, 13 + withNonRustupSysroot ? true, 14 + rustLibSrc ? null, 15 + addToToolchain ? [ ], 16 + 17 + projectRoot, 18 + }: 19 + args: 20 + assert withNonRustupSysroot -> rustLibSrc != null; 21 + assert withNonRustupSysroot -> rustc-unwrapped != null; 22 + let 23 + sysrootCacheDir = '' 24 + SYSROOT_HASH=$(echo "${rustc-unwrapped}" | ${coreutils}/bin/md5sum | ${coreutils}/bin/cut -d' ' -f1) 25 + CACHE_DIR="${projectRoot}/.idea/nix-rust-sysroot/$SYSROOT_HASH" 26 + ''; 27 + 28 + createFakeSysrootNonRustup = writeShellScriptBin "create-fake-sysroot" '' 29 + ${sysrootCacheDir} 30 + if [ ! -d "$CACHE_DIR" ]; then 31 + mkdir -p "$CACHE_DIR" 32 + 33 + cp -r ${rustc-unwrapped}/* "$CACHE_DIR/" 34 + chmod -R u+w "$CACHE_DIR" 35 + 36 + mkdir -p "$CACHE_DIR/lib/rustlib" 37 + if [ ! -d "$CACHE_DIR/lib/rustlib/src" ]; then 38 + cp -r ${rustc-unwrapped}/lib/rustlib/rustc-src "$CACHE_DIR/lib/rustlib/src" 39 + chmod -R u+w "$CACHE_DIR/lib/rustlib/src" 40 + 41 + rm -rf "$CACHE_DIR/lib/rustlib/src/rust/library" 42 + cp -r ${rustLibSrc} "$CACHE_DIR/lib/rustlib/src/rust/library" 43 + chmod -R u+w "$CACHE_DIR/lib/rustlib/src/rust/library" 44 + fi 45 + fi 46 + 47 + echo "$CACHE_DIR" 48 + ''; 49 + 50 + createFakeSysrootRustup = writeShellScriptBin "create-fake-sysroot" '' 51 + ${sysrootCacheDir} 52 + if [ ! -d "$CACHE_DIR" ]; then 53 + mkdir -p "$CACHE_DIR" 54 + cp -r ${rustc}/* "$CACHE_DIR/" 55 + fi 56 + 57 + echo "$CACHE_DIR" 58 + ''; 59 + 60 + createFakeSysroot = 61 + if withNonRustupSysroot then createFakeSysrootNonRustup else createFakeSysrootRustup; 62 + 63 + rustcWrapper = writeShellScriptBin "rustc" '' 64 + if [ "$1" = "--print" ] && [ "$2" = "sysroot" ]; then 65 + exec ${lib.getExe createFakeSysroot} 66 + else 67 + exec ${rustc}/bin/rustc "$@" 68 + fi 69 + ''; 70 + 71 + rustcWithRRLib = runCommand "rustc-wrapper-${rustc.version or "unknown"}" { } '' 72 + mkdir -p $out/bin 73 + ln -s ${rustc}/bin/* $out/bin 74 + rm $out/bin/rustc 75 + ln -s ${rustcWrapper}/bin/rustc $out/bin/rustc 76 + ${lib.concatMapStrings (output: "ln -s ${rustc.${output}} \$${output}\n") ( 77 + lib.remove "out" rustc.outputs 78 + )} 79 + ''; 80 + 81 + cargoWrapper = writeShellScriptBin "cargo" '' 82 + export PATH="${rustc}/bin:$PATH" 83 + args=() 84 + for arg in "$@"; do 85 + if [[ "$arg" != +* ]]; then 86 + args+=("$arg") 87 + fi 88 + done 89 + exec ${cargo}/bin/cargo "''${args[@]}" 90 + ''; 91 + 92 + baseToolchain = buildEnv { 93 + name = (args.name or "rr") + "-base-toolchain"; 94 + paths = [ 95 + (lib.hiPrio cargoWrapper) 96 + rustcWithRRLib 97 + ] 98 + ++ addToToolchain; 99 + }; 100 + 101 + fakeRustup = writeShellScriptBin "rustup" '' 102 + export PATH="${baseToolchain}/bin:$PATH" 103 + exec ${lib.getExe nushell} ${./src/rustup.nu} $@ 104 + ''; 105 + 106 + toolchain = buildEnv { 107 + name = (args.name or "rr") + "-toolchain"; 108 + 109 + paths = [ 110 + baseToolchain 111 + fakeRustup 112 + ]; 113 + }; 114 + in 115 + mkShell ( 116 + args 117 + // { 118 + packages = (args.packages or [ ]) ++ [ toolchain ]; 119 + 120 + shellHook = (args.shellHook or "") + '' 121 + mkdir -p .idea 122 + ln -snf ${toolchain} .idea/rust-toolchain 123 + ''; 124 + } 125 + )
+22
examples/fenix.nix
··· 1 + let 2 + pkgs = import <nixpkgs> { }; 3 + fenix = import <fenix> { inherit pkgs; }; 4 + 5 + inherit (fenix.stable) toolchain; 6 + mkShellRR = pkgs.callPackage ../. { 7 + withNonRustupSysroot = false; 8 + 9 + rustc = toolchain; 10 + cargo = toolchain; 11 + 12 + projectRoot = toString ./.; 13 + }; 14 + in 15 + mkShellRR { 16 + packages = [ pkgs.taplo ]; 17 + 18 + shellHook = '' 19 + echo "Welcome to the RustRover Fenix Shell!" 20 + echo "Add .idea/rust-toolchain/bin to RustRover to use this toolchain" 21 + ''; 22 + }
+21
examples/nixpkgs.nix
··· 1 + let 2 + pkgs = import <nixpkgs> { }; 3 + mkShellRR = pkgs.callPackage ../. { 4 + inherit (pkgs.rustPlatform) rustLibSrc; 5 + 6 + addToToolchain = [ 7 + pkgs.clippy 8 + pkgs.rustfmt 9 + ]; 10 + 11 + projectRoot = toString ./.; 12 + }; 13 + in 14 + mkShellRR { 15 + packages = [ pkgs.taplo ]; 16 + 17 + shellHook = '' 18 + echo "Welcome to the RustRover Nixpkgs Shell!" 19 + echo "Add .idea/rust-toolchain/bin to RustRover to use this toolchain" 20 + ''; 21 + }
+139
src/rustup.nu
··· 1 + #!/usr/bin/env nu 2 + 3 + def get_target [] { 4 + rustc -vV | lines | where ($it | str starts-with "host:") | first | split row ":" | get 1 | str trim 5 + } 6 + 7 + def get_toolchain_name [] { 8 + $"stable-(get_target)" 9 + } 10 + 11 + def main [ 12 + --verbose (-v) 13 + --quiet (-q) 14 + --version (-V) 15 + --help (-h) 16 + ...args 17 + ] { 18 + if $version { 19 + print "rustup 1.28.2 (1970-01-01)" 20 + return 21 + } 22 + 23 + if $help or ($args | is-empty) { 24 + print "rustup 1.28.2 (1970-01-01)" 25 + print "" 26 + print "The Rust toolchain installer" 27 + print "" 28 + print "Usage: rustup[EXE] [OPTIONS] [+toolchain] [COMMAND]" 29 + print "" 30 + print "Commands:" 31 + print " toolchain Install, uninstall, or list toolchains" 32 + print " default Set the default toolchain" 33 + print " show Show the active and installed toolchains or profiles" 34 + print " update Update Rust toolchains and rustup" 35 + print " check Check for updates to Rust toolchains and rustup" 36 + print " target Modify a toolchain's supported targets" 37 + print " component Modify a toolchain's installed components" 38 + print " override Modify toolchain overrides for directories" 39 + print " run Run a command with an environment configured for a given toolchain" 40 + print " which Display which binary will be run for a given command" 41 + print " doc Open the documentation for the current toolchain" 42 + print " man View the man page for a given command" 43 + print " self Modify the rustup installation" 44 + print " set Alter rustup settings" 45 + print " completions Generate tab-completion scripts for your shell" 46 + print " help Print this message or the help of the given subcommand(s)" 47 + print "" 48 + print "Arguments:" 49 + print " [+toolchain] Release channel (e.g. +stable) or custom toolchain to set override" 50 + print "" 51 + print "Options:" 52 + print " -v, --verbose Set log level to 'DEBUG' if 'RUSTUP_LOG' is unset" 53 + print " -q, --quiet Disable progress output, set log level to 'WARN' if 'RUSTUP_LOG' is unset" 54 + print " -h, --help Print help" 55 + print " -V, --version Print version" 56 + return 57 + } 58 + 59 + let command = ($args | first) 60 + let rest = ($args | skip 1) 61 + 62 + match $command { 63 + "show" => { 64 + if ($rest | is-empty) or (($rest | first) == "active-toolchain") { 65 + let toolchain = (get_toolchain_name) 66 + print $"($toolchain) \(default\)" 67 + } else { 68 + print $"installed toolchains" 69 + print "--------------------" 70 + print "" 71 + let toolchain = (get_toolchain_name) 72 + print $"($toolchain) \(default\)" 73 + } 74 + } 75 + "which" => { 76 + if ($rest | length) > 0 { 77 + let cmd = ($rest | first) 78 + try { 79 + which $cmd | get path.0 80 + } catch { 81 + print $"error: toolchain '(get_toolchain_name)' does not have binary '($cmd)'" 82 + } 83 + } 84 + } 85 + "run" => { 86 + if ($rest | length) > 1 { 87 + let rest = ($rest | skip 1) 88 + let cmd = ($rest | first) 89 + let cmd_args = ($rest | skip 1) 90 + ^$cmd ...$cmd_args 91 + } 92 + } 93 + "default" => { 94 + if ($rest | is-empty) { 95 + let toolchain = (get_toolchain_name) 96 + print $"($toolchain) \(default\)" 97 + } else { 98 + print $"info: default toolchain set to '($rest | first)'" 99 + } 100 + } 101 + "toolchain" => { 102 + if ($rest | is-empty) or (($rest | first) == "list") { 103 + let toolchain = (get_toolchain_name) 104 + print $"($toolchain) \(default\)" 105 + } 106 + } 107 + "target" => { 108 + if ($rest | length) > 0 { 109 + let subcmd = ($rest | first) 110 + if $subcmd == "list" { 111 + if ($rest | length) > 1 and (($rest | get 1) == "--installed") { 112 + print (get_target) 113 + } else { 114 + print (get_target) 115 + } 116 + } 117 + } 118 + } 119 + "component" => { 120 + if ($rest | length) > 0 { 121 + let subcmd = ($rest | first) 122 + if $subcmd == "list" { 123 + if ($rest | length) > 1 and (($rest | get 1) == "--installed") { 124 + print "rust-std" 125 + print "rustc" 126 + print "cargo" 127 + } else { 128 + print "rust-std (installed)" 129 + print "rustc (installed)" 130 + print "cargo (installed)" 131 + } 132 + } 133 + } 134 + } 135 + _ => { 136 + print $"error: unknown command: '($command)'" 137 + } 138 + } 139 + }