diff --git a/NIX.md b/NIX.md new file mode 100644 index 0000000000..edbe405dc5 --- /dev/null +++ b/NIX.md @@ -0,0 +1,104 @@ +# Building and Running Theseus with Nix + +Theseus provides a Nix shell ([shell.nix](./shell.nix)) that provides a build environment for building, developing, and running Theseus in QEMU. It can bring in all the dependencies needed, as described in the [README](./README.md), including the correct build of Rust nightly (see [Theseus, Nix, and Rust Toolchains](#theseus_nix_and_rust_toolchains)). The shell also includes some optional arguments you can use to optimize the dependency imports as well as other things. See [Nix Shell](#nix_shell) for more. + +## Quick Start + +To enter the Nix shell, simply use `nix-shell` while in the Theseus source. + +```sh +nix-shell # [./shell.nix] +``` + +From here you should be able to build, run, and develop any part of Theseus: + +```sh +# Equivilant to `make iso` but builds with all features enabled. See Makefile. +make +``` + +```sh +make iso +``` + +```sh +# Build and run Theseus with full features in QEMU. +make run +``` + +## Nix Shell + +### Args + +The following are some of the more relevant arguments for the shell. Some additional arguments exist, but are only listed and documented in [shell.nix](./shell.nix) itself. They are omitted here for the sake of not cluttering this documentation for less advanced Nix users. + +#### Dependency-Related Options + +- `run` - Whether or not to prepare for running Theseus in QEMU. This can be highly useful when you don't plan to run Theseus in QEMU, as QEMU is a very heavy dependency. The default is `true`. +- `extraRustDevelopmentTools` - Brings additional development-related tools, not in the standard Theseus build toolchain, into the provided toolchain. Currently, this only includes rust-analyzer. The default is `true`. +- `extraRustToolchainComponents` - Extra, custom components to add to the provided toolchain. +- `rustToolchain` - Override with a completely custom Rust toolcahin derivation. Not recommended. +- `bootloader` - Chooses which bootloader(s) the shell should prepare for usage. + Possible values are: + - "any" - All bootloaders are made available to use, and the `bootloader` env var is not set. + - "grub" - GRUB's dependancies are added, and the `bootloader` env var is set to + "grub" if `setMakeBootloaderPreference` is `true`. + - "limine" - The shell preforms the setup stated in the README, cloning limine and resetting + to the needed commit. Additionally, the `bootloader` env var is set to "limine" if + `setMakeBootloaderPreference` is `true`. + The default is `"grub"`. + - `setMakeBootloaderPreference` - If false, overrides the behavior of `bootloader` to _not_ + set the `bootloader` environment variable. The default is `true`. + +#### Limine-Related Options + +These options are specific to the usage of the Limine bootloader. If the above arguments do not create a shell in which Limine is set as a dependency, these options have no effect. See [README.md](./README.md) about Limine and its usage in Theseus. + +- `preserveOldLimine` - Before downloading Limine, check if './limine-prebuilt' already exists. If so, skip downloading Limine for the already-existing Limine to be used. The default is `true`. +- `requiredLimineCommit` - If Limine is downloaded, this specifies the commit that Limine should be checked out to. The default is what is specified in README.md and changing this is not recommeded. It is merely set as an arg rather than a constant for convenience. + +### Examples + +Default shell (with QEMU for running, and both GRUB and Limine availbale to use): + +```sh +nix-shell +``` + +Create build/dev environment without QEMU dependency (avoids downloading qemu_kvm but disables running): + +```sh +nix-shell --arg run 'false' +``` + +Use the Limine bootloader (will fully set up for Limine usage): + +```sh +nix-shell --arg bootloader '"limine"' +# Should now use Limine by default: +make run +``` + +### Targetting Other Architectures + +TODO + +### Development Shell for Debugging + +TODO + +## Theseus, Nix, and Rust Toolchains + +Theseus's Rust codebase and the locked versions of its dependencies rely on not only nightly Rust, but a specific nightly build. This is not an issue for systems using rustup, however, Nix's package manager is at its core and not only is using something like rustup looked down on, but it may not even work on NixOS. Normally, when Theseus is built using cargo, it will see the rust-toolchain.toml file and use rustup to install that toolchain to use instead. Nix, however, only provides the latest _stable_ Rust in Nixpkgs, and the wrapped toolchain components that come with it do not do this. This is an obvious problem. + +Luckily, though, there is an easy solution: [Oxalica's rust-overlay](https://github.com/oxalica/rust-overlay/tree/master). Using this, we can easily produce a working, NixOS-compatible Rust toolchain from a Rust toolchain file, such as the one that provides the toolchain definition for Theseus, ([rust-toolchain.toml](./rust-toolchain.toml)). + +This toolchain is provided by the [rust-toolchain.nix](./rust-toolchain.nix) file. You can import it directly to produce a Rust toolchain for Theseus but its main use is in the Theseus Nix shell. + +## Nix Derivation for Theseus + +A derivation for Theseus is in the works. + +## Flake Support + +TODO diff --git a/README.md b/README.md index ac748ca685..0dcea00602 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,7 @@ We are continually working to improve the OS, including its fault recovery abili # Quick start -On Linux (Debian-like distros), do the following: +On Linux (Debian-like distros, non-Nix), do the following: 1. Obtain the Theseus repository (with all submodules): ``` git clone --recurse-submodules --depth 1 https://github.com/theseus-os/Theseus.git @@ -39,6 +39,22 @@ On Linux (Debian-like distros), do the following: ``` To exit QEMU, press Ctrl + A, then X. +On Linux (using Nix): +1. Follow step 1 of the above to obtain the Theseus source. +2. Cd into the source: + ```sh + cd Theseus + ``` +3. Enter the Nix shell from ./shell.nix: + ```sh + nix-shell + ``` +4. Build and run (like for ordinary Linux): + ```sh + make run + ``` +**Note:** Installing rustup is not necessary, and should not be done on Nix-based platforms, especially via the traditional rustup installation method. The Nix shell provided by [shell.nix](./shell.nix) will provide a functional, and even somewhat modular, Rust toolchain, fit for building and/or developing Theseus. See [NIX.md](./NIX.md) for more information. + See below for more detailed instructions. @@ -50,7 +66,8 @@ git submodule update --init --recursive Currently, we support building Theseus on the following platforms: * Linux, 64-bit Debian-based distributions like Ubuntu, tested on Ubuntu 16.04, 18.04, 20.04. - - Arch Linux and Fedora have also been reported to work correctly. + - Arch Linux and Fedora have also been reported to work correctly. + - NixOS support is experimental, however, success has been verified for basic builds (with both GRUB and Limine), using the provided Nix shell [./shell.nix](./shell.nix). * Windows, using the Windows Subsystem for Linux (WSL), tested on the Ubuntu version of WSL and WSL2. * MacOS, tested on versions High Sierra (v10.13), Catalina (v10.15), and Ventura (v13.5). * Docker, atop any host OS that can run a Docker container. @@ -58,6 +75,8 @@ Currently, we support building Theseus on the following platforms: ## Setting up the build environment +**Note:** For Linux-Nix users, including NixOS, see the [NIX.md](./NIX.md) file for build instructions. For MacOS Nix users, out-of-the-box support is a major TODO. For WSL users, you may need to follow the extra, WSL-specific instructions below. + First, install Rust by following the [setup instructions here](https://www.rust-lang.org/en-US/install.html). On Linux, just run: ```sh curl https://sh.rustup.rs -sSf | sh @@ -169,6 +188,8 @@ make run bootloader=limine ``` Feel free to try newer versions, however they may not work. +### Using Nix +The above step is part of the Nix shell's setup when `bootloader` == `"limine"`. See [NIX.md](./NIX.md) for more. ## Targeting ARMv8 (aarch64) Support for Theseus on aarch64 is an ongoing effort, but most of the core subsystems are complete. diff --git a/rust-toolchain.nix b/rust-toolchain.nix new file mode 100644 index 0000000000..37336b6ba6 --- /dev/null +++ b/rust-toolchain.nix @@ -0,0 +1,20 @@ +{ + pkgs ? import { + overlays = [ + (import (builtins.fetchTarball "https://github.com/oxalica/rust-overlay/archive/master.tar.gz")) + ]; + }, + lib ? pkgs.lib, + withDevPkgs ? true, + additionalComponents ? [ ], +}: +let + toolchainFile = ./rust-toolchain.toml; + toolchainDesc = (builtins.fromTOML (builtins.readFile toolchainFile)).toolchain; + finalToolchain = toolchainDesc // { + components = toolchainDesc.components ++ lib.lists.optionals withDevPkgs [ + "rust-analyzer" + ] ++ additionalComponents; + }; +in +(pkgs.rust-bin.fromRustupToolchain finalToolchain) diff --git a/shell.nix b/shell.nix new file mode 100644 index 0000000000..6c1c1edbfe --- /dev/null +++ b/shell.nix @@ -0,0 +1,96 @@ +{ + # Nix options: + pkgs ? import { + overlays = [ + (import (builtins.fetchTarball "https://github.com/oxalica/rust-overlay/archive/master.tar.gz")) + ]; + }, + lib ? pkgs.lib, + + # Dependency options: + + # qemu_kvm is a heavy package so we should probably allow users to avoid it if they want to. + run ? true, + # Adds extra components to the defaulttoolchain for developing Theseus. See rust-toolchain.nix and + # NIX.md. + extraRustDevelopmentTools ? true, + # Adds a custom set of components to the default toolchain. + extraRustToolchainComponents ? [ ], + # Allows you to completely override the default toolchain. + rustToolchain ? import ./rust-toolchain.nix { + inherit pkgs; + withDevPkgs = extraRustDevelopmentTools; + additionalComponents = extraRustToolchainComponents; + }, + # The prefered bootloader. This controls both dependencies in terms of bootloaders as well as the + # `bootloader` env var, which sets which bootloader make will use if an explicit arg is not specified + # to make at runtime. + bootloader ? "grub", + # By default, this shell exports the `bootloader` env var as mentioned for the above arg. If set to + # `false`, it will _not_ set the `bootloader` env var. + setMakeBootloaderPreference ? true, + + # Limine-Specific options: + + # Whether or not to preserve old Limine clones when entering the shell. + preserveOldLimine ? true, + # This should generially not be overriden as Theseus relies on a specific commit of Limine and other + # commits do not have guarenteed support. Use at your own risk. + requiredLimineCommit ? "3f6a330", +}: +let + inherit (lib.strings) optionalString; + + gitOutputHandler = "while read line; do echo \" > $line\"; done"; + + bootloaderPackageMap = rec { + any = grub ++ limine; + grub = [ pkgs.grub2 ]; + limine = [ ]; # This is provided by manually cloning. + }; +in +assert pkgs.lib.asserts.assertMsg + (builtins.hasAttr bootloader bootloaderPackageMap) + "\"${bootloader}\" is not a valid value for argument \"bootloader\". Please see the Theseus \ + Nix shell documentation in `Theseus/NIX.md`."; +pkgs.mkShell { + nativeBuildInputs = with pkgs; [ + # See NIX.md and rust-toolchain.nix. + rustToolchain + # Misc dependancies mentioned in the README + gnumake gcc nasm pkg-config mtools xorriso wget + ] ++ ( + lib.lists.optionals run [ pkgs.qemu_kvm ] + ) ++ bootloaderPackageMap.${bootloader}; + + shellHook = let + limineGit = gitArgs: "${pkgs.git}/bin/git -C limine-prebuilt ${gitArgs} |& ${gitOutputHandler}"; + prepareLimine = if preserveOldLimine + && builtins.pathExists ./limine-prebuilt + && (builtins.readFileType ./limine-prebuilt) == "directory" + then '' + echo "Detected possible old Limine clone, $(realpath limine-prebuilt). Using that." + '' else '' + ${optionalString (builtins.pathExists ./limine-prebuilt) '' + echo "Removing old ./limine-prebuilt..." + rm -rf limine-prebuilt + ''} + echo "Cloning Limine..." + ${pkgs.git}/bin/git clone https://github.com/limine-bootloader/limine.git limine-prebuilt \ + |& ${gitOutputHandler} + echo "Checking out required Limine commit (${requiredLimineCommit})..." + ${limineGit "checkout ${requiredLimineCommit}"} + ''; + in + '' + ${optionalString (bootloader == "limine" || bootloader == "any") '' + echo "Detected using Limine bootloader. Preparing Limine..." + # TODO: Firgure out a way to do this using Nix with Limine instead of doing this cloning the repo + # thing here. + ${prepareLimine} + ''} + ${optionalString (bootloader != "any" && setMakeBootloaderPreference) '' + export bootloader=${bootloader} + ''} + ''; +}