- The file system in macOS underlies every app — and the FileManager class has a lot to do with that. Your app is stored in the Applications folder, users store documents in their Documents folder, and preferences and support files are stored in the users Library folder.
- Dec 19, 2020 Question or problem in the Swift programming language: Working on a command line tool, I am trying to retrieve the path of a list of words stored in a file called words.txt. The file is added to the project, included in the project's Target Membership, and selected to copy during the target's Copy Files build.
Bible app is simple, elegant and beautiful. It's an easy to navigate offline Bible app for Mac that has a smart UI and many useful features. It allows you to sneak into the sacred scriptures without the confusion of the Internet. Sync your Marked, Journals, Sermons, Prayers with iCloud, Top Featu. (setq-default mac-right-option-modifier nil) 3.1.2 Define words using OS X Dictionary This layer by default enables defining words under point SPC x w d using OS X Dictionary. In some cases you might want to manually setup dictionary to use.
March 21, 2018
This post describes how I set up a reproducible development environment in a few seconds on any Linux distribution (and potentially macOS as well). This setup includes simple executables (curl, git) but also programs with custom configurations and dotfiles (vim
, tmux
). The Nix language is used to describe the system configuration, which you can find on github and follow along.
Developers have access to wonderful tools, which, when leveraged appropriately, allow them to build wonderful things in no time. Some of these tools, like vim and Emacs, can be customized to the point that working with them becomes a second nature, and some people will put a lot of effort into making sure that their setup is tailored for their workflows. This sometimes involves spending hours fighting with dependencies, plugins, language syntax highlighters… only to wake up the next day and realize that their daily update broke everything.
I've used GNU stow. I've stored my dotfiles in a Git repository. I've written scripts to extract and load sets of packages with aptitude
. It never worked reliably. Now I have a solution that actually works. I'm using a few text files that describe my entire setup, store them on GitHub, and don't anymore fear upgrading my system, losing my laptop or spawning short-lived development instances. Let me show you how.
If you've never heard of Nix, worry not, the next section will present its main concepts. If you've used Nix before, feel free to skip ahead. The sections are mostly independent, pick any one that is most relevant to you:
Nix and NixOS
Nix is a programming language with unconventional properties, which was developed mostly to work as a package manager. Today we are not going to focus much on the language itself, but on the package management model and how it fits in Unix systems. Check out Jim Fisher's post for a good introduction to the language itself. From now on I may use 'Nix' interchangeably for both the language and the package manager.
Here's a quote from the Nix manual:
In Nix, packages are stored in unique locations in the Nix store (typically, /nix/store). For instance, a particular version of the Subversion package might be stored in a directory /nix/store/dpmvp969yhdqs7lm2r1a3gng7pyq6vy4-subversion-1.1.3/
, while another version might be stored in /nix/store/5mq2jcn36ldlmh93yj1n8s9c95pj7c5s-subversion-1.1.2
. The long strings prefixed to the directory names are cryptographic hashes of all inputs involved in building the package — sources, dependencies, compiler flags, and so on. So if two packages differ in any way, they end up in different locations in the file system, so they don't interfere with each other.
This captures the essence of Nix. All this package building is described through a set of Nix files (with a .nix
extension). Nix does not actually have a package archive: all it has is a package repository description, nixpkgs
, which is nothing but a bunch of Nix files! Nix downloads those files and prepares the packages on your machine. Most of it, however, was already built and cached, so after installing Nix you should be able to download any package from nixpkgs
' cache:
Check out the Nix manual and the Nix Pills for a deeper introduction.
NixOS
There actually is an entire operating system based on Nix: NixOS. Everything, from your packages to the services and users, is described with Nix. Using NixOS is a great solution if you can afford it. Using the Nix package manager alone is much more lightweight, as you can always piggy back on your distribution's package manager if you need to, and you can always get rid of Nix entirely (including everything it's ever installed) by wiping /nix
. I, personally, only need a single user on my system, and no services besides the ones provided by Ubuntu by default, so the setup I describe below is perfect.
Package management
I'll start by showing you how I curate the set of packages installed on my system at all times: my homies. Let's have a look at the main homies
Nix file, default.nix
:
The let … in …
is a typical functional programming construct: it defines some values after the let
and brings them into scope after the in
. A few values are defined:
pkgs
: where we'll draw our packages from – you don't have to usenixpkgs
!bashrc
,git
,tmux
,vim
: some packages I customized for my needs, we'll get to what exactly that means in the next sections.homies
: the list of packages that I want to be installed on my system.
If you've never had any exposure to functional programming, the code above might look somewhat strange: that's fine. You should nevertheless be able to tailor it to your needs by adding some packages sourced from nixpkgs
(e.g. pkgs.blender
or pkgs.firefox
) to the homies
list.
The following command removes all your (Nix-) installed packages and replaces them with the ones defined in default.nix
:
Let's deconstruct what's happening:
nix-env
: this is the command that deals with installing packages on and removing packages from your system.-f default.nix
: by defaultnix-env
will look for packages innixpkgs
; by specifyingdefault.nix
we actually instruct it not to install the whole set of packages defined innixpkgs
…-i
: 'install'.--remove-all
: instructnix-env
to remove all the packages previously installed.
All the homies
packages are now installed. There might be something bugging you:
- Aren't all packages located at a
/nix/store/XXXXXXX-foo
-style path? - Wasn't I lead to believe that wiping
/nix
would get rid of nix? - How can the packages be present on my
$PATH
then; did Nix just tinker with my$PATH
!?
Nix didn't tinker with your $PATH
, or at least not just now. During the installation of Nix itself, you might have been asked to add the following line to your .bashrc
/.profile
:
What this small shell script does is very simple (in its essence): it adds $HOME/.nix-profile/bin/
to your $PATH
. When you run nix-env -i
(as we did above) Nix will build the packages in a temporary directory, store them in a /nix/store/XXXXXXX-foo
-style location (a so-called entry in the Nix store), and create a symlink in $HOME/.nix-profile/bin/
to the newly created entry in the Nix store. This is very powerful because Nix can perform atomic updates, without ever erasing packages: it only updates the symlinks if the whole build was successful. This enables very interesting operations, like rolling back to a previous 'generation' (a generation is created on every successful nix-env -i
call):
(No, it's not on purpose, I just happen to be at generation 42…)
You might start to wonder how this is possible, since built packages take up space and that space is limited. You can run garbage collection runs whenever you feel like it, which you can read more about here.
You now know how to perform basic package installs from a .nix
file. Congratulations! Next, let's see how to manage dotfiles.
Packaging up the dotfiles: tmux and vim
As mentioned above, part of the homies
are sourced directly from nixpkgs
(curl
, htop
, …) while others are customized (in particular tmux
and vim
). The reason is that I use the former ones directly, while the latter ones I want to use with a dotfile, like .tmux.conf
and .vimrc
. We'll start with packaging your beloved .tmux.conf
with Nix (you can find vim
in the next section).
My homies
have a special directory dedicated to tmux
, which you might think of as a 'module' (although modules in Nix are something else):
You might have expected tmux.conf
, which is exactly what you expect it to be. Let's look at tmux/default.nix
instead!
There are a few things going on, but we can ignore most of that. We will focus on the following part:
First, the double (single-)quotes '
: that's a string. What's inside the string is mostly bash. What's not bash is the ${./tmux.conf}
part: that's a way of referencing Nix values inside a bash statement – and inside any string, actually. To Nix, this snippet is just a string, it will just happen to be run as a bash script at some point. So ${ foo }
interpolates the Nix value ./tmux.conf
to a string. The gambling forum. The next question is: what kind of value is ./tmux.conf
?
Cd label free software download. Wanna have a guess?
Well, it looks like a path, doesn't it. And as it turns out there is a file tmux.conf
in the directory. A Nix value that starts with ./
is Nix' quick way of creating an entry in the Nix store: by interpolating it in the snippet above, Nix will replace ${ ./tmux.conf }
with a /nix/store/XXXXXXX-foo
-style path. Sweet! The rest of the obscure incantation is just a way of telling Nix to wrap tmux
(some tmux
that was built by Nix and lives in /nix/store
) and bake in the -f
flag which specifies the location of the .tmux.conf
file to use. You can convince yourself of it by squinting long enough at the actual tmux
that's located on my $PATH
:
And just like that, your beloved .tmux.conf
is baked in your tmux
! Next, vim and vimrc
!
vim
Let's now bundle vim
with a vimrc
and some plugins. Maybe you've had this experience:
- Plugin A needs python 2.7,
- Plugin B needs python 3.0,
- Plugin C needs python 2.8, which is a special flavor of python 2.7.8 that can only be compiled during full moon.
What this small shell script does is very simple (in its essence): it adds $HOME/.nix-profile/bin/
to your $PATH
. When you run nix-env -i
(as we did above) Nix will build the packages in a temporary directory, store them in a /nix/store/XXXXXXX-foo
-style location (a so-called entry in the Nix store), and create a symlink in $HOME/.nix-profile/bin/
to the newly created entry in the Nix store. This is very powerful because Nix can perform atomic updates, without ever erasing packages: it only updates the symlinks if the whole build was successful. This enables very interesting operations, like rolling back to a previous 'generation' (a generation is created on every successful nix-env -i
call):
(No, it's not on purpose, I just happen to be at generation 42…)
You might start to wonder how this is possible, since built packages take up space and that space is limited. You can run garbage collection runs whenever you feel like it, which you can read more about here.
You now know how to perform basic package installs from a .nix
file. Congratulations! Next, let's see how to manage dotfiles.
Packaging up the dotfiles: tmux and vim
As mentioned above, part of the homies
are sourced directly from nixpkgs
(curl
, htop
, …) while others are customized (in particular tmux
and vim
). The reason is that I use the former ones directly, while the latter ones I want to use with a dotfile, like .tmux.conf
and .vimrc
. We'll start with packaging your beloved .tmux.conf
with Nix (you can find vim
in the next section).
My homies
have a special directory dedicated to tmux
, which you might think of as a 'module' (although modules in Nix are something else):
You might have expected tmux.conf
, which is exactly what you expect it to be. Let's look at tmux/default.nix
instead!
There are a few things going on, but we can ignore most of that. We will focus on the following part:
First, the double (single-)quotes '
: that's a string. What's inside the string is mostly bash. What's not bash is the ${./tmux.conf}
part: that's a way of referencing Nix values inside a bash statement – and inside any string, actually. To Nix, this snippet is just a string, it will just happen to be run as a bash script at some point. So ${ foo }
interpolates the Nix value ./tmux.conf
to a string. The gambling forum. The next question is: what kind of value is ./tmux.conf
?
Cd label free software download. Wanna have a guess?
Well, it looks like a path, doesn't it. And as it turns out there is a file tmux.conf
in the directory. A Nix value that starts with ./
is Nix' quick way of creating an entry in the Nix store: by interpolating it in the snippet above, Nix will replace ${ ./tmux.conf }
with a /nix/store/XXXXXXX-foo
-style path. Sweet! The rest of the obscure incantation is just a way of telling Nix to wrap tmux
(some tmux
that was built by Nix and lives in /nix/store
) and bake in the -f
flag which specifies the location of the .tmux.conf
file to use. You can convince yourself of it by squinting long enough at the actual tmux
that's located on my $PATH
:
And just like that, your beloved .tmux.conf
is baked in your tmux
! Next, vim and vimrc
!
vim
Let's now bundle vim
with a vimrc
and some plugins. Maybe you've had this experience:
- Plugin A needs python 2.7,
- Plugin B needs python 3.0,
- Plugin C needs python 2.8, which is a special flavor of python 2.7.8 that can only be compiled during full moon.
You might expect the vim
setup to be a bit more complex, mostly because of plugins, but in practice it is fairly easy. Because the nixpkgs
are hosted on GitHub, anybody is free to submit a pull request, and a bit of infrastructure was merged in for vim
plugin support.
The python version (versionssss) issue mentioned above is completely alleviated with Nix, because the plugins themselves can specify their system dependencies, and different versions of Python/what-have-you can happily cohabit with one another. Here's my complete vim setup:
(and the ./vimrc
file:)
The vimrc
file itself is sourced from the file in my homies repository (although in a different way than the .tmux.conf
file from the previous section) and lists zero plugins. Those are magically handled by the vimUtils.vimrcFile
function.
You might recognize the obscure wrapProgram
incantation that we used with tmux
earlier, which this time instructs vim
to start with -u …
. This is how we tell vim
to use the Nix generated vimrc
. But now, we pass a second argument to wrapProgram
:
The reason for that is that I trigger hasktags
– a Haskell ctags generator – upon a Haskell file save, and --prefix PATH …
will ensure that hasktags
is in $PATH
when vim
is invoked. This used to be a pain to deal with, as I had to remember to also install the hasktags
program after setting up my dotfiles
on a new machine. Now the dependency is stored with my homies
!
Take home message: vim
configuration does not have to be a pain. And you should not have to log in into your development boxes with a stripped down, unfamiliar default vim configuration. Bring your homies along. It's so easy.
Cowsay: The nix-shell
Alright, buckle up now, we're getting real. I've talked about my so-called 'homies' – the packages that I like having around – for a while now, and you might have wondered how I survive with those sad 10 packages (I counted). Here's my answer: I don't. Does that make sense? No? Then let me introduce the 8th Wonder of the World, the nix-shell
:
The nix-shell
is the Nix equivalent of a one-night stand. It will bring packages in scope for the lifetime of a shell (this time not through symlinks: it crafts a special $PATH
for the new shell). The simplest usage is the one showcased above – nix-shell -p package1 -p package2 …
– which makes package1
, package2
, … available in your current shell session. After you've exited the shell, they're gone.
Nilma Spa
The notion of a 'package' in Nix is somewhat laxer than in, say, aptitude
. Here's a valid nix-shell
invocation:
And that's how you install Python and tensorflow. Sweet, heh?
Another way to use the nix-shell
is to write a shell.nix
file, which is evaluated when you call nix-shell
. As it turns out, my homies are simply the packages that I regularly use outside of code repositories (by the way if you haven't tried the homies, the easiest way is to copy the repository and run nix-shell
inside it). The nix-shell
is amazing when working on code with others; just drop a shell.nix
with all (and I mean all) the system dependencies for building and running the project in a shell.nix
, and the rest of your team will thank you for it. For more info, check out zimbatm's talk on Sneaking Nix at $work.
This was a quick introduction to the nix-shell
Caesars casino cheat. , or how to install packages for a very short lifetime or project-local scope. The concept is simple but the potential is huge. Go ahead and try it out!
Pro-tip: Add the following to your bashrc
for Haskell one-offs (or copy mine):
Conclusion
That's it for today. We went through the underlying concepts of the Nix package manager, learned how to package tools with customized configuration in a declarative and reproducible way and finally went through a few example use cases of the nix-shell
. I'd like to thank zimbatm and Graham Christensen for proofreading this text and suggesting improvements. Thanks, guys!
P.S.: Nix is not an all-or-nothing package manager, you can install it today, write some configuration, wipe it entirely tomorrow and start where you left it next week – your configuration will still work. You might want to start by installing a few packages on your machine, or drop a shell.nix
in a project that has a few system dependencies that are tricky to install; it's up to you!
Mac Os Catalina
Thank you ❤️