内容简介:AfterOne of the main selling points of
After my brief introduction
to NixOS
and the process of installing it, I thought I’d dive into my experience with development on NixOS
. I failed my initial intention to come with a follow-up post in quick succession to the introduction, but this one dragged on for several reasons. Finding structure and trying to be concise when discussing something as “generic” as Nix
turned out to be quite challenging. Also, while still learning I found many of the things I’d written in the past to be obsolete the next time I returned to writing.
Again, why Nix
?
One of the main selling points of Nix
and NixOS
is its focus on deterministic and reproducable builds, something all developers should strive for in their projects. Many build and packaging tools have been putting considerable effort into maintaining integrity, but normally this is localized to libraries and dependencies in a single language. It is still common for programming languages to depend on system-wide compilers, interpreters and shared libraries by default. No toolhas pioneered and gone to the lengths Nix
has to ensure reproducible builds, where not just the project’s direct dependencies are locked, but practically every
external dependency up to and including the global system.
Nix
does not do away with the build tools you would use for building and deploying a project. Instead, it formalizes and encapsulates these tools in a way that they too are locked to a given version. Nix
-compatible projects are still built using the regular tools for that project’s ecosystem, however the versions of these tools will be deterministic.
Arrested development
Nix
as a language is quite minimalistic, yet built on sound functional programming concepts. The result is a language which shines when creating reusable functions, which again allows it express build recipies (derivations) for a wide variety of projects. Nix
does not directly compete
with existing build tools, but tries to complement
and combine
them. While the “core” of the Nix
ecosystem may be small, the community has accumulated many conventions and utilities with the aim to reduce duplication and boilerplate. This effort is mainly contained within the nixpkgs package collection
. And while the modularity and reusability is impressive, the information overload when dealing with nixpkgs
leads to a steep learning curve for newcomers.
I was of course expecting a learning curve when diving head first into NixOS
, but I must admit there were several times when I questioned my decision to switch. Learning curves imply a drop in productivity as you spend time learning, when you instead could have been producing. It is not easy to value ongoing efforts like these which have yet to produce measurable results. I was steadily learning more about Nix
, yet I felt a growing desperation and despair because despite my efforts, I had very little to show for it. Progress was slow.
In retrospect the goals I had to reach seem more well-defined than they were up front:
- Create project environments free of system dependencies.
- Change my development workflow to accommodate new restrictions and requirements.
-
Manage my own
software using
Nix
.
But to begin with I was a bit stuck in NixOS
, enjoying all the great software built and maintained by others, yet having quite a bit of trouble getting anywhere with my own projects.
Turtles all the way down
While learning Nix
I’ve had many aha
moments. One was when I finally realized that Nix
isn’t a package manager in the normal sense used for distributing binary builds. The fact that it can fetch pre-built derivations is merely a consequence of its design. Primarily it is a source distribution and build
tool. I gradually grokked this as I got further involved with writing nix expressions. Documentation might already state it clearly, but here I’m talking about reaching enlightenment at a deeper level. Perhaps similar to being told something as a kid, but still having to experience
it first hand in order to “get it”.
The Nix
expression for a derivation
(a build unit) must state all of its dependencies in order to build. This first and foremost includes its build
dependencies, but also its runtime dependencies. And here’s where it gets weird. These dependencies are themselves merely other Nix
expressions for other derivations. More concretely, if project A
uses tool B
in its build process, its obvious that B
must be built before attempting to build A
. In most environment I’ve encountered this typically means to use “some package manager”™ to go fetch B
, typically not caring how it is built or distributed. In Nix
though, the dependency A
has on B
is declared by simply referring to the recipe for building B
. This means Nix
will simply go ahead and build B
in order to build A
. And the same goes for all of the dependencies B
might have on other tools, even up to the C
library and compiler.
Nobody wants to waste precious CPU cycles (and time) on rebuilding the “whole world” whenever we wish to build a project, which is why most build tools implement caching in one way or another. By tracking all inputs to every derivation, Nix
is able to implement a content-addressable
cache which is queried for pre-built derivations. This cache is also distributed, allowing content to be fetched from trusted sources, primarily the NixOS
cache at cache.nixos.org
. It is populated by build servers
, ensuring that the most common/popular derivations are always up to date. Locally this doubles as the Nix
store, in which all the artifacts built and used
in the current system or user profile reside.
In the end it’s the sole fact that by having deterministic builds and knowing all the inputs involved, it’s possible to determine up-front which identifier such an artifact will have in the Nix
store. And if it’s already there, there’s no point in building it again. Et voilà, you get binary package distribution for “free”!However, if a dependency is neither in the local Nix
store, nor in one of the trusted binary caches, Nix
simply builds the nested dependencies on demand. They’re just layers upon layers of Nix
expressions after all. Simply mind-bending!
System integration
Virtual environments using nix-shell
Nix
provides packages for many compilers, interpreters, libraries, and related tools. Through Nix
we get a uniform way of installing dependencies, as opposed to using several domain-specific ones, each with their own unique behavior. Nix
also comes with nix-shell
, which starts an interactive shell based on a Nix
expression, analogous to the way virtualenv
work in Python
. It either builds or fetches cached builds of dependencies and adds them to the Nix
store, before making them accessible in a subshell through modified environment variables and symlinks. The user or system environment remains untouched, which means projects can pick and choose developer tools at their leisure, without polluting the user’s environment or requiring root-access.
Following is a short example of my system where neither python3
nor node
is found in my $PATH
, then using nix-shell
to create an ad-hoc environment where the Python 3.7
and Node.js 10.x
interpreters are available:
❯ which python python not found ~ ❯ which node node not found ~ ❯ nix-shell -p python3 -p nodejs-10_x [nix-shell:~]$ python --version Python 3.7.3 [nix-shell:~]$ node --version v10.15.3
Nix
will download pre-built binaries of Python
and Node.js
on the first run, then cache them in the Nix
store until garbage collected. The -p
<package>
flag to nix-shell
is really convenient when you want to quickly try something out, but for proper projects you’d want something more persistent and declarative. Without the -p
flag nix-shell
will look for and evaluate Nix
expressions from files named shell.nix
, or fall back to default.nix
.
Invoking nix-shell
in the same directory then loads the environment in a subshell:
~/project $ nix-shell [nix-shell:~/project]$ node --version v10.15.3 [nix-shell:~/project]$ python --version Python 3.7.3 [nix-shell:~/project]$
We can also instruct Nix
to include Python
packages in our environment:
with import <nixpkgs> {}; mkShell { buildInputs = [ (python3.withPackages (ps: with ps; [ requests ])) ]; }
Where invoking nix-shell
gives us:
[nix-shell:~/tmp]$ python Python 3.7.3 (default, Mar 25 2019, 20:59:09) [GCC 7.4.0] on linux Type "help", "copyright", "credits" or "license" for more information. >>> import requests >>> requests <module 'requests' from '/nix/store/j70h9pxi8sn1sq0cy65k5y3knhrmyqb7-python3-3.7.3-env/lib/python3.7/site-packages/requests/__init__.py'>
nixpkgs
provides definitions for a large set of Python
packages. However, if a package is not available it’s fully possible to pull it down using pip
. In order to use pip
from within the environment it has to be added as a buildInput
like any other. Furthermore, pip install
must either be invoked with the --user
option to install dependencies under ~/.local/lib
, or even better using a virtualenv
. There are also ways of instructing Nix
about how to fetch packages from package archives like pypi
, typically through utilities available in nixpkgs
or using external tools called generators
.
Automatic environment activation using direnv
If you, like me, jump around a lot between projects and environments, the inconvenience of having to invoke nix-shell
all the time quickly becomes apparent. To automate this I rely on a tool called direnv
, a companion for your shell:
direnv is an extension for your shell. It augments existing shells with a new feature that can load and unload environment variables depending on the current directory.
Personally I integrate it with zsh
, which means that whenever I cd
into a project directory tree, direnv
will ensure that the shell is setup with the same environment you would get by invoking nix-shell
directly. Another difference is that direnv
does not invoke a new sub-shell for the new environment, but mutates the current process’ environment. This provides a seamless experience navigating between different projects, not having to worry about loading the correct virtualenvs
or switching between interpreter versions using tools like nvm
or pyenv
:
~ ❯ for prg in cabal ghc hlint; do which "$prg"; done cabal not found ghc not found hlint not found ~ ❯ cd ~/projects/nixon direnv: loading .envrc direnv: using nix direnv: using cached derivation direnv: eval .direnv/cache-.1926.5d6da42cf79 direnv: export +AR +AR_FOR_TARGET ... ~PATH nixon on master [$!?] ❯ for prg in cabal ghc hlint; do which "$prg"; done /nix/store/h433cxh423lrm3d3hb960l056xpdagkh-cabal-install-2.4.1.0/bin/cabal /nix/store/zj821y9lddvn8wkh1wwk6c3j5z6hpjhh-ghc-8.6.5-with-packages/bin/ghc /nix/store/1pwskgibynsvr5fjqbvkdbw616baw8c4-hlint-2.2.2/bin/hlint
For direnv
to know when and how to load an environment, it checks for the existence of .envrc
files. These files are basic shell scripts evaluated using bash
and should output expressions for setting environment variables. In the case of Nix
I typically just invoke use_nix
in these files. The first time an .envrc
file is found (and on changes) direnv
will ask for permission to evaluate its content. This is a security mechanism in order to avoid accidentally invoking malicious code. Once allowed, direnv
will continue to load and unload the environment when entering and leaving project directory trees.
~/tmp/project ❯ echo 'use_nix' > .envrc direnv: error .envrc is blocked. Run `direnv allow` to approve its content. ~/tmp/project ❯ direnv allow direnv: loading .envrc error: getting status of '/home/mmyrseth/tmp/project/default.nix': No such file or directory direnv: eval .direnv/cache-.1926.5d6da42cf79 direnv: export ~PATH
The single Emacs
process conundrum
Back in my vim
days I’d typically launch the editor from within a virtualenv
in a shell, or at least starting in a project directory. Typically I’d have a tmux
session for each project, a single vim
for that project in one pane, and potentially several shells in other panes. When switching to Emacs
I quickly got used to using projectile
for switching between projects in combination with perspective
to provide workspaces for each project. This keeps buffer lists and window layouts tidy and organized while working on multiple projects in a single Emacs
process.
Emacs
uses a single variable for the execution path ( exec-path
) and other similar globals defining environmental values, which ultimately affect how Emacs
will spawn external commands like compilers, linters, repls, and so on. Naturally Emacs
won’t be able to launch these tools if they aren’t in the $PATH
, and so these globals have to change when switching between projects. This can be done manually by invoking commands, or automatically by hooks triggered when switching between buffers. I was already using plugins like pyvenv
to switch between virtualenvs
in Python
projects. Most node
-related plugins already support finding tools in npm bin
.
I started off looking for solutions which would allow me to keep my “single process Emacs
”-based workflow. There are direnv
plugins for Emacs
which loads the project environment on file/buffer changes in Emacs
. Unfortunately, after using emacs-direnv
for while I came to realize it wasn’t the solution I wanted. The main issue with the direnv
plugin for Emacs
is that environments are loaded automatically, while this is typically what you want, I found that switching between buffers Emacs
would keep evaluating and updating the environment. In the end this caused the editor to feel slow and unresponsive. A deal-breaker!
Biting the bullet, I moved on to a workflow centered around having one Emacs
instance per project I was currently working on. I dropped my single long-lived Emacs
sessions in favor of multiple sessions, each running within the project environment set up by nix-shell
. It ended up with me firing up and shutting down Emacs
much more often than before, as well as having to find the correct editor instance for a certain project. This quickly started to annoy me in the same way using a slow direnv
did. If only I could make the first approach faster…
Turns out I wasn’t the only one looking for this
and I eventually stumbled on an implementation
of the use_nix
function used by direnv
. This provided a significant performance increase by caching
the result of evaluating nix-shell
. Another benefit of this function is that it also symlinks the environment derivation into Nix
’s gcroots
. Don’t worry, this basically means that the artifacts required by the development environment won’t be garbage collected when cleaning out the Nix
store using nix-collect-garbage
.
Even more time passes, and I became aware of a new tool built by Target
, called lorri
. It is basically a daemon you can run in the background, building all your environments as their expressions or dependencies change, while also ensuring they are not garbage collected. I have yet to start using lorri
myself mostly out of laziness, but I must say it looks very promising.
Defining development environments
Installing my own tools
In Nix
it’s important to distinguish between software intended to be used as a dependency, like libraries, compilers, and so on, and end-user
software, which can be command line tools and GUI applications. While libraries and developer tools should only be available from within any given project depending on them, end-user software should be accessible from a user environment. I do develop a few end-user tools that make my life easier, and so I had to figure out how to best install these projects into my user profile.
Both stack
and npm
, and many other package managers, are able to install software into a “global” location. The stack install
and npm install
--global
commands allow installing not just upstream packages, but also locally from the same machine. Even though this was the way I installed my own software on other operating systems, it was not the way I liked to do it on NixOS
. In my opinion it’s a smell
when you have to invoke several different tools to not only install software, but also figure out what you’ve already installed. Some tools do not even track
what they installed, forcing you to manually go through and remove stuff from you ~/.local~
.
Nix
resolves these issues in one go, at the cost of having to figure out how
to create proper
Nix
expressions for Python
, JavaScript
, and Haskell
code bases. Luckily, nixpkgs
has us covered, normally providing a single function doing what you want. Some nixpkgs
functions also wrap Nix
generators like callCabal2nix
, saving you from having to run these tools yourself. It took me a while to figure out it was callCabal2nix
and buildPythonApplication
I wanted for most Haskell
and Python
projects, respectively. I have yet to make an attempt at installing any of my JavaScript
tools on NixOS
.
A quick note on generators
I’ve mentioned that Nix
doesn’t stop you from using package managers like pip
and yarn
from within a project environment. The downside is that Nix
has no knowledge of what these tools are doing, and so cannot ensure the same guarantees as if it knew about the artifacts these tools create (or fetch). It is possible to use these other tools to fetch or build the software we want, then
inform Nix
about the artifacts, which is then able to add these to the Nix
store.
Since package managers normally operate based on existing dependency meta-data, it’s possible to automate the process of listing out the dependencies, performing the build steps for each, adding artifacts to the Nix
store, and so on. Tools that automatically generate Nix
expressions from some input are called generators
. The output of these generators are Nix
expressions which can then be saved to file and evaluated by nix-build
and nix-shell
. In the case of nixpkgs
there are also wrapper functions around generators, which saves you from having to use
the generators themselves, One example of this is callCabal2nix
used for building Haskell
packages.
Here’s a list of a few assorted generators for different project types:
-
node2nix
: Generate
Nix
derivations to buildnpm
packages. -
setup.nix
: Generate
Nix
derivations forPython
packages. -
cabal2nix
: Generate
Nix
derivations from a.cabal
file.
Pinning nixpkgs
The package repository nixpkgs
is based on the concept of channels. Channels are basically branches of development in the git
repository moving the contained Nix
expressions forward by updating upstream versions, fixing bugs and security issues, and provide new Nix
utilities. Channels are also moving targets. System users
want to automatically receive security updates, new application versions, and so on. Software developers on the other hand want to control the upgrade of dependencies in a controlled manner.
The Nix
way of locking down dependencies is to
pin the nixpkgs
versions
. In essence this is to use a version of nixpkgs
from a specific commit, a snapshot. This ensures that building the Nix
derivation will always result in the same output, regardless of future upstream changes to nixpkgs
. Different derivations may also use different versions of nixpkgs
without that necessarily becoming an issue. To upgrade one or more dependencies it is often enough to just change the snapshot of nixpkgs
to a newer version.
Haskell
Haskell
projects are typically built using cabal
. stack
is another popular tool, which manages package sets of GHC
versions along with compatible Haskell
packages. Gabriel Gonzales’ writeup of Nix and Haskell in production
state that Nix
is not a replacement for cabal
, but rather a stack
replacement.
Nix
has become quite popular in the Haskell
community and it seems many people choose it to build their projects. In a way similar to Stackage
, nixpkgs
contains package sets build for different versions of ghc
. There’s a section in the nixpkgs
manual under “User’s Guide to the Haskell Infrastructure”
providing some information on how to use Nix
for Haskell
.
I used stack
for all Haskell
development I’d been doing leading up to my switch to NixOS
, and so it felt natural to continue using stack
under Nix
. stack
even has native Nix support
. However, since there’s quite a bit of overlap what stack
and Nix
attempts to solve, I’ve since switched my workflow over to Nix
and just cabal
. nixpkgs
provide a callCabal2nix
function which in short suffices to setup a simple project. Following are a few hobby projects which I’ve recently switched over to this model:
nixon
- Nix
-aware project environment launcher
Using either rofi
or fzf
, nixon
selects projects from predefined directories and launches nix-shell
(or other commands) in the project’s environment. This is very useful when projects have .nix
files setting up shell environments in which you want to spawn a terminal, an editor, run compilation commands, and so on.
This project uses a single default.nix
file which also works by creating a shell environment with additional developer tools when run in nix-shell
:
default.nix
:
{ pkgs ? import ./nixpkgs.nix {}, haskellPackages ? pkgs.haskellPackages, }: let gitignore = pkgs.nix-gitignore.gitignoreSourcePure [ ./.gitignore ]; in haskellPackages.mkDerivation { pname = "nixon"; version = "0.1.0.0"; src = (gitignore ./.); isLibrary = true; isExecutable = true; executableHaskellDepends = with haskellPackages; [ aeson base bytestring containers directory foldl haskeline process text transformers turtle unix unordered-containers wordexp ]; executableSystemDepends = with pkgs; [ fzf rofi ]; testDepends = with haskellPackages; [ hspec ]; license = pkgs.stdenv.lib.licenses.mit; }
shell.nix
:
{ pkgs ? import ./nixpkgs.nix {}, haskellPackages ? pkgs.haskellPackages, }: let drv = (import ./default.nix) { inherit pkgs haskellPackages; }; in haskellPackages.shellFor { packages = _: [ drv ]; buildInputs = (with pkgs; [ cabal2nix cabal-install hlint ]) ++ (with haskellPackages; [ ghcid ]); }
In short, to define the derivation ( drv
) I’m using the Haskell
specialization of mkDerivation
in haskellPackages.mkDerivation
. It also makes use of haskellPackages.shellFor
to setup a shell environment used when developing. This shell includes cabal2nix
, cabal
, hlint
, and ghcid
.
i3ws - Automatic workspace management in i3 .
This project is interesting because the project was using stack
in a monorepo style layout before switching to Nix
. This meant that I had to find a nice way to have several packages under development integrating nicely in Nix
. Luckily somebody beat me to it, and I drew some inspiration from the “ Nix + Haskell monorepo tutorial
” post on the NixOS
discourse
, pointing to the nix-haskell-monorepo
GitHub
repo.
The new-style commands of cabal
supports multiple projects using a cabal.project
file. This file contains a listing of the packages/subdirectories contained in the project, each with their own .cabal
file:
$ cat cabal.project packages: foo bar baz
For a working example of this setup, see the GitHub repo
for i3ws
.
Python
We use Python
extensively at work, and our most active codebase is a web application with a Python
backend and a JavaScript/TypeScript
frontend. It was this project I first tried to get working on my laptop after switching it to NixOS
. We use some automation scripts which call out to pip
and yarn
to install dependencies.
This is not a trivial project, but still I find the shell.nix
file I use to setup the environment to not be very large. It is worth noting that we do not build and deploy this project using Nix
, and so the expression is only
setting up enough for me to successfully run our install, testing and packaging scripts:
{ pkgs ? import (fetchTarball { url = "https://github.com/NixOS/nixpkgs/archive/19.09.tar.gz"; sha256 = "0mhqhq21y5vrr1f30qd2bvydv4bbbslvyzclhw0kdxmkgg3z4c92"; }) {}, }: let # Pin Pillow to v6.0.0 pillowOverride = ps: with ps; pillow.override { buildPythonPackage = attrs: buildPythonPackage (attrs // rec { pname = "Pillow"; version = "6.0.0"; src = fetchPypi { inherit pname version; sha256 = "809c0a2ce9032cbcd7b5313f71af4bdc5c8c771cb86eb7559afd954cab82ebb5"; }; }); }; venv = "./venv"; in self.mkShell { buildInputs = with pkgs; [ binutils gcc gnumake libffi.dev libjpeg.dev libxslt.dev nodejs openssl.dev (python36.withPackages (ps: with ps; [ (pillowOverride ps) pip python-language-server virtualenv ])) squashfsTools sshpass yarn zip zlib.dev ]; shellHook = '' # For using Python wheels export SOURCE_DATE_EPOCH="$(date +%s)" # https://github.com/NixOS/nixpkgs/issues/66366 export PYTHONEXECUTABLE=${venv}/bin/python export PYTHONPATH=${python}/lib/python3.7/site-packages if [ -d ${venv} ]; then source ${venv}/bin/activate fi ''; };
First of all, none of the other developers on the team use Nix
, which means I have to add my Nix
configuration without being too intrusive on the others. I also want to make sure I don’t deviate too much from the rest, leading to issues caused by differences in my environment. We also have several scripts and workflows centered around some of these tools, like automating dependency installation across multiple sub-projects, package introspection, and yarn
workspace
symlinking, to name a few.
I could go on a digression as to how NixOS
breaks the Filesystem Hierarchy Standard
of Linux
, but essentially it means that libraries and executables are not found in standard locations. Pillow
uses some hardcoded paths in its setup.py
which point to invalid locations on NixOS
. That makes it hard to install it using pip
, and so it’s the only Python
dependency installed from nixpkgs
. Overriding it to pin it to the version we are using, which ensures pip
is not going to try to install another version by itself. In the end this works well, but I spent a lot
of time trying to do this in several other ways.
In my quest to get Pillow
working nicely in our project I had to dive through the nixpkgs
codebase. At which point I got more aware of all the helpers functions in that repository for building projects of different shapes and sizes. What buildPythonPackage
does should be obvious from its name, but I found that figuring out usage, differences, and even discovering of all these different utilities within nixpkgs
is not very easy. Much improvement could be made in the Nix
community on this front.
JavaScript & TypeScript
The Node.js
packages in nixpkgs
are mainly end user
packages. Some few nodejs
libraries are present because they are dependencies of non-NPM packages. The nixpkgs
docs has a section on Node.js packages
. The recommendation is to use the node2nix
generator directly on a project’s package.json
file. Here’s a short list of possible generators for Node.js
packages:
For simpler setups I prefer to use Nix
to only provide node
, npm
, and yarn
, then invoke these directly as it seems to work fine in most scenarios. I haven’t had much reason for using node2nix
yet, so I can’t say much about that experience.
One thing I typically do in my JavaScript/TypeScript
environments is to include the javascript-typescript-langserver
package, which is used by lsp-mode
in Emacs
to provide IDE-like tools.
Ad-hoc environments
Sometimes you want access to certain language tools in order to test something. While on other systems you typically have node
or python
installed somewhere directly accessible on the shell, in NixOS
this isn’t the case. Instead, by adding a few expressions to the nixpkgs
configuration file it’s easy to launch shells with access to these tools.
Using nix-shell
to run scripts
nix-shell
also has support for being used in shebangs, making it ideal for setting up ad-hoc environments used by simple scripts. The following example instructs nix-shell
to create a Haskell
environment with GHC
along with a predefined package turtle
.
#! /usr/bin/env nix-shell #! nix-shell -p "haskellPackages.ghcWithPackages (ps: with ps; [turtle])" #! nix-shell -i runghc {-# LANGUAGE OverloadedStrings #-} import Turtle main :: IO () main = do echo "Hello, World!"
Pre-defined environments
Using Nix
overlays we can also define environments which can be referenced in nix-shell
invocations to provide ad-hoc environments when testing out things. Overlays are a way in nixpkgs
to define new packages and overrides to existing packages. It’s a powerful concept, but here we’re using it just to create our own derivations:
-
Node.js
Define
env-node
as an overlay in~/.config/nixpkgs/overlays.nix
:let overlay = self: super: { nodeEnv = with self; buildEnv { name = "env-node"; paths = [ nodejs-10_x nodePackages_10_x.javascript-typescript-langserver yarn ]; }; }; in [overlay]
Launching the environment:
$ nix-shell -p nodeEnv [nix-shell:~]$ node --version v10.15.3 [nix-shell:~]$ npm --version 6.4.1 [nix-shell:~]$ yarn --version 1.13.0 [nix-shell:~]$
-
Python
Similarly to
nodeEnv
, define an overlay in~/.config/nixpkgs/overlays.nix
:let overlay = self: super: { pythonEnv = with self; buildEnv { name = "env-python"; paths = [ (python3.withPackages (ps: with ps; [ pip virtualenv ])) ]; }; }; in [overlay]
Launching the environment (here we’re also adding
ipython
manually):❯ nix-shell -p pythonEnv -p python3Packages.ipython [nix-shell:~]$ python --version Python 3.7.3 [nix-shell:~]$ ipython Python 3.7.3 (default, Mar 25 2019, 20:59:09) Type 'copyright', 'credits' or 'license' for more information IPython 7.2.0 -- An enhanced Interactive Python. Type '?' for help. In [1]:
Summary
In hindsight I should have known attempting to write a post like this would be opening a can of worms. Well, my setup and configurations did
end up changing parallel to writing this post, and so time dragged on. Also, nailing the scope of something as broad as this is not easy and I feel I’ve only managed to scrape the surface of describing development on NixOS
(or using just Nix
, the package manager).
Development based around Nix
can be a very powerful thing indeed, but don’t expect it to be a walk in the park. I see the lack of proper documentation and poor discoverability as one of the main hurdles Nix
and nixpkgs
has to overcome. Again, nixpkgs
is a huge
collection of Nix
expressions for applications, libraries, and tools ranging across many different programming languages and ecosystems. I think because of both the size of the repository and the diversity of its content, there has evolved certain idioms within
different areas of the nixpkgs
repo. This makes finding the correct functions and utilities to use for building a certain project harder for newcomers (and perhaps even seasoned Nix
-ers).
Despite some of these areas of improvement I’m conviced that the concepts pioneered by Nix
is here to stay. I have yet to find better alternatives for managing the complexity of building and distributing software.
Finally I’d like to thank Bjørnar Snoksrud @snoksrud for proofreading.
以上所述就是小编给大家介绍的《myme.no - NixOS: For developers》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Wikis For Dummies
Dan Woods、Peter Thoeny / For Dummies / 2007-7-23 / USD 24.99
Corporations have finally realized the value of collaboration tools for knowledge sharing and Wiki is the open source technology for creating collaborative Web sites, as either a public site on the In......一起来看看 《Wikis For Dummies》 这本书的介绍吧!