--- title: "c - Using rix to build project specific environments" output: rmarkdown::html_vignette vignette: > %\VignetteIndexEntry{c-using-rix-to-build-project-specific-environments} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- ```{r, include = FALSE} knitr::opts_chunk$set( collapse = TRUE, comment = "#>" ) ``` ```{r, include=FALSE} library(rix) ``` ## Project-specific Nix environments Now that you have the required software installed, it’s to time learn more about declaring and using reproducible inveronments. The ideal workflow when using `{rix}` is to create a new, separate environment at the start of a project. Let's say that you wish to analyse some data set, and need `{dplyr}` and `{ggplot2}`. Let's also suppose that you use VS Code as your IDE (there will be more discussion on editors in the vignette `vignette("e-interactive-use")` but for now, let’s assume that you use VS Code). With the `rix::rix()` function, you can easily generate the right `default.nix` file. You need to provide the following inputs to `rix()`: - `r_ver`: the version of R required. Use "latest" for the latest version; - `r_pkgs`: the required R packages. For example "dplyr" (more on this in the vignette `vignette("d1-installing-r-packages-in-a-nix-environment")`); - `system_pkgs`: the required system packages, if needed. For example "quarto", or a Python interpreter (more on this in the vignette `vignette("d2-installing-system-tools-and-texlive-packages-in-a-nix-environment")`); - `git_pkgs`: list of git packages to add (more on this in the vignette `vignette("d1-installing-r-packages-in-a-nix-environment")`); - `ide`: the integrated development editor to use (more on this in the vignette `vignette("e-interactive-use")`) - `path`: the path where to save the `default.nix` file. - `overwrite`: whether to overwrite the `default.nix` file or not. - `print`: whether to print the `default.nix` file to the console or not. Run the following command to generate the right `default.nix` file: ```{r, eval = FALSE} path_default_nix <- tempdir() rix( r_ver = "latest", r_pkgs = c("dplyr", "ggplot2"), system_pkgs = NULL, git_pkgs = NULL, ide = "code", project_path = path_default_nix, overwrite = TRUE, print = TRUE ) #> # This file was generated by the {rix} R package v0.7.1 on 2024-07-01 #> # with following call: #> # >rix(r_ver = "12a9c0004bc987afb1ff511ebb97b67497a68e22", #> # > r_pkgs = c("dplyr", #> # > "ggplot2"), #> # > system_pkgs = NULL, #> # > git_pkgs = NULL, #> # > ide = "code", #> # > project_path = path_default_nix, #> # > overwrite = TRUE, #> # > print = TRUE) #> # It uses nixpkgs' revision 12a9c0004bc987afb1ff511ebb97b67497a68e22 for reproducibility purposes #> # which will install R version latest. #> # Report any issues to https://github.com/ropensci/rix #> let #> pkgs = import (fetchTarball "https://github.com/NixOS/nixpkgs/archive/12a9c0004bc987afb1ff511ebb97b67497a68e22.tar.gz") {}; #> #> rpkgs = builtins.attrValues { #> inherit (pkgs.rPackages) #> dplyr #> ggplot2 #> languageserver; #> }; #> #> system_packages = builtins.attrValues { #> inherit (pkgs) #> R #> glibcLocales #> nix; #> }; #> #> in #> #> pkgs.mkShell { #> LOCALE_ARCHIVE = if pkgs.system == "x86_64-linux" then "${pkgs.glibcLocales}/lib/locale/locale-archive" else ""; #> LANG = "en_US.UTF-8"; #> LC_ALL = "en_US.UTF-8"; #> LC_TIME = "en_US.UTF-8"; #> LC_MONETARY = "en_US.UTF-8"; #> LC_PAPER = "en_US.UTF-8"; #> LC_MEASUREMENT = "en_US.UTF-8"; #> #> buildInputs = [ rpkgs system_packages ]; #> #> } ``` To start using this environment, open a terminal in the folder containing `default.nix` and use the following Nix command: ``` nix-build ``` `nix-build` is a Nix command that builds an environment according to the specifications found in a `default.nix` file. Once the environment is done building, you should find a new file called `result` next to the `default.nix` file. This file is a symlink to the software installed by Nix. `{rix}` also provides a `nix_build()` function to build Nix environments from within an interactive R session, but it is not always guaranteed to succeed, due to differences in platforms. This is explained in more detail in the following vignette `vignette("z-advanced-topic-running-r-or-shell-code-in-nix-from-r")`. In case of doubt, run `nix-build` from your usual terminal application. To now use the environment, type in the same terminal as before: ``` nix-shell ``` This will activate the environment. If you have VS Code installed you can start it from this environment and VS Code will use this specific R version library of packages. We will explore this in greater detail in the vignette `vignette("e-interactive-use")`. ## Running old projects with {rix} The example below shows how to create a `default.nix` with instructions to build an environment with R version 4.2.1, the `{dplyr}` and `{janitor}` packages and no specific IDE: ```{r parsermd-chunk-2, eval = F} path_default_nix <- tempdir() rix( r_ver = "4.2.1", r_pkgs = c("dplyr", "janitor"), system_pkgs = c("quarto"), git_pkgs = NULL, ide = "other", project_path = path_default_nix, overwrite = TRUE ) ``` The file looks like this: ``` #> # This file was generated by the {rix} R package v0.7.1 on 2024-07-01 #> # with following call: #> # >rix(r_ver = "79b3d4bcae8c7007c9fd51c279a8a67acfa73a2a", #> # > r_pkgs = c("dplyr", #> # > "janitor"), #> # > system_pkgs = c("quarto"), #> # > git_pkgs = NULL, #> # > ide = "other", #> # > project_path = path_default_nix, #> # > overwrite = TRUE) #> # It uses nixpkgs' revision 79b3d4bcae8c7007c9fd51c279a8a67acfa73a2a for reproducibility purposes #> # which will install R version 4.2.1. #> # Report any issues to https://github.com/ropensci/rix #> let #> pkgs = import (fetchTarball "https://github.com/NixOS/nixpkgs/archive/79b3d4bcae8c7007c9fd51c279a8a67acfa73a2a.tar.gz") {}; #> #> rpkgs = builtins.attrValues { #> inherit (pkgs.rPackages) #> dplyr #> janitor; #> }; #> #> system_packages = builtins.attrValues { #> inherit (pkgs) #> quarto #> R #> glibcLocales #> nix; #> }; #> #> in #> #> pkgs.mkShell { #> LOCALE_ARCHIVE = if pkgs.system == "x86_64-linux" then "${pkgs.glibcLocales}/lib/locale/locale-archive" else ""; #> LANG = "en_US.UTF-8"; #> LC_ALL = "en_US.UTF-8"; #> LC_TIME = "en_US.UTF-8"; #> LC_MONETARY = "en_US.UTF-8"; #> LC_PAPER = "en_US.UTF-8"; #> LC_MEASUREMENT = "en_US.UTF-8"; #> #> buildInputs = [ rpkgs system_packages ]; #> #> } ``` The first line is quite important, as it shows which *revision* of `nixpkgs` is being used for this environment. The *revision* is the commit hash of that particular release of `nixpkgs`, here: `79b3d4bcae8`. This revision of `nixpkgs` is the one that shipped version 4.2.1 of R, so the `{dplyr}` and `{janitor}` packages that will get installed will be the versions available in that revision as well. This means that R versions and package versions are always coupled when using Nix. However, if you need a specific version of R, but also a specific version of a package that is not available in that particular Nix revision, one solution is to install that package from Github or fro the CRAN archives. Read the vignette `vignette("d1-installing-r-packages-in-a-nix-environment")` to know more about this. To know which versions of R are available, read the documention of `available_r()`. ## Running programs from an environment You could create a bash script that you put in the path to make the process of launching your editor from that environment more streamlined. For example, if your project is called `housing`, you could create this script and execute it to start your project: #!/bin/bash nix-shell /absolute/path/to/housing/default.nix --run code This will execute VS Code in the environment for the `housing` project. If you use `{targets}` you could execute the pipeline in the environment by running: ``` cd /absolute/path/to/housing/ && nix-shell default.nix --run "Rscript -e 'targets::tar_make()'" ``` ## Running single functions in a subshell It is also possible to run single functions in an isolated environment from an active R session using `with_nix()` and get the output of that function loaded into the current session. Refer to this vignette `vignette("z-advanced-topic-running-r-or-shell-code-in-nix-from-r")` for more details on how to achieve this. Concretely this means that you could be running R version 4.3.2 (installed via Nix, or not), and execute a function on R version 4.0.0 for example in a subshell (or execute a function that requires an old version of a package in that subshell), and get the result of the computation back into the main R session. ## Nix environments are not completely isolated from your system It is important to know that an environment built by Nix is not totally isolated from the rest of the system. Suppose that you have the program `sl` installed on your system, and suppose you build a Nix environment that also comes with `sl`. If you activate that environment, the version of `sl` that will run when called is the one included in the Nix environment. If, however, you start `sl` in a Nix environment that does not come with it, then your system's `sl` will get used instead. In the context of the R programming language, this means that if you have a user or system library of packages (meaning, a library of packages generated by a regular installation of R), these packages may be loaded by an R version running from a Nix shell. To avoid issues with this, calling `rix()` automatically runs `rix_init()` as well, which generates a custom `.Rprofile` file in the project's directory. This way, your regular user library of packages will not interfere with R environments managed by Nix. Moreover, this custom `.Rprofile` also redefines `install.packages()` and makes it throw an error: indeed, if you wish to add packages to your an R environment managed by Nix, you should add these packages to the `default.nix` file instead, and rebuild the environment. If you want to even make other programs inaccessible to a running Nix environment, you can drop into it using `nix-shell --pure` instead of only `nix-shell`.