nix-shell, but make it lovely

I'm pretty happy with a pattern I stumbled into last year for using Nix to provision dependencies for a task-/utility-oriented Makefile.

For a while I've wanted the rough equivalent of a nix-shell shebang--but for Makefiles. The naive approach is something like:

naive
1#!/usr/bin/env nix-shell
2#! nix-shell -i gnumake -p hello
3
4.PHONY: all
5all:
6	@echo nice hello
7	hello --version

But this won't quite work either way we can run it. If we run the following:

1make --makefile=naive all > make-naive.console
2./naive > naive.console

The first one fails because, of course, make doesn't give a hoot about our shebang:

make-naive.console
1nice hello
2hello --version
3make: hello: No such file or directory
4make: *** [naive:7: all] Error 127

and the second fails because we need to be able to pass an -f flag to use make as a shebang interpreter:

naive.console
1gnumake: Nothing to be done for `naive'.

All we really need to do (though it'd be nice to get some sugar in Nix for this...) is to run nix-shell with the dependencies, echo the shell's PATH, and export this PATH in the makefile:

nix-make
1export PATH := $(shell nix-shell -p hello --run 'echo $$PATH')
2
3.PHONY: all
4all:
5	@echo nice hello
6	hello --version

When we run make --makefile=nix-make all, we get what we want:

nix-make.console
 1nice hello
 2hello --version
 3hello (GNU Hello) 2.12.1-6fe9
 4Copyright (C) 2020 Free Software Foundation, Inc.
 5License GPLv3+: GNU GPL version 3 or later <https://gnu.org/licenses/gpl.html>.
 6This is free software: you are free to change and redistribute it.
 7There is NO WARRANTY, to the extent permitted by law.
 8
 9Written by Karl Berry, Sami Kerola, Jim Meyering,
10and Reuben Thomas.

If you'd like to see a non-toy use, here's how I use this in resholve:

make.nix (src)
 1{ pkgs ? import <nixpkgs> { } }:
 2
 3with pkgs;
 4let
 5  # wordswurst = callPackage ../wordswurst { };
 6  wordswurst = callPackage
 7    (fetchFromGitHub {
 8      owner = "abathur";
 9      repo = "wordswurst";
10      rev = "df2b4873ea66d904bb863ee9f0e21be1ebaab9be";
11      hash = "sha256-aN29FphL6Bh5ts/5/ydso5vVFeB/6b5hj+6fynvoYus=";
12    })
13    { };
14in
15pkgs.mkShell {
16  buildInputs = [ nix coreutils gnused groff util-linux wordswurst sassc ];
17}
Makefile (src)
 1#! /usr/bin/env make
 2#export PATH := $(shell nix-shell -p nix coreutils gnused groff util-linux --run 'echo $$PATH')
 3export PATH := $(shell nix-shell make.nix --run 'echo $$PATH')
 4
 5.PHONY: apologeez ci clean update
 6
 7apologeez:
 8	@echo Sorry--the Makefile is a lie. I just use this for dev tasks atm.
 9	@echo See README.md / https://github.com/abathur/resholve
10
11all: apologeez
12install: apologeez
13uninstall: apologeez
14
15.local : *.nix setup.cfg setup.py test.sh demo tests/* resholve.1 resholve _resholve/*
16	touch .local
17
18
19result-ci: .local
20	@echo Building ci.nix
21	@nix-build --out-link result-ci ci.nix
22	@touch result-ci result-ci/* || true # TODO: fails on MU
23
24ci: result-ci
25
26result-quick: .local
27	@echo Building quick.nix
28	@nix-build --out-link result-quick quick.nix
29	@touch result-quick result-quick/* || true # TODO: fails on MU
30
31quick: result-quick
32
33clean:
34	rm .local result-ci result-quick docs/README.nixpkgs.md
35
36result-ci/test.txt result-ci/demo.txt result-ci/nix-demo.txt: result-ci
37
38timings.md: bits/timings.md.pre bits/timings.md.post result-ci/test.txt
39	@echo Building timings.md
40	@cat bits/timings.md.pre \
41		result-ci/test.txt \
42		bits/timings.md.post \
43		> timings.md
44
45demos.md: bits/demos.md.pre bits/demos.md.mid bits/demos.md.post result-ci/demo.txt result-ci/nix-demo.txt
46	@echo Building demos.md
47	@cat bits/demos.md.pre \
48		result-ci/demo.txt \
49		bits/demos.md.mid \
50		result-ci/nix-demo.txt \
51		bits/demos.md.post \
52		| sed -E 's@/nix/store/[a-z0-9]{32}-@/nix/store/...-@g' > demos.md
53
54resholve.1: docs/manpage.wwst docs/manpage.css docs/content.wwst
55	@echo Building manpage
56	@wordswurst $< > $@
57
58docs/README.nixpkgs.md: docs/markdown.wwst docs/markdown.css docs/content.wwst
59	@echo "Building Nixpkgs README (markdown)"
60	@wordswurst $< > $@
61
62docs/%.css: docs/%.scss
63	@echo Sassing $@ from $<
64	@sassc --omit-map-comment $< $@
65
66docs/resholve.1.txt: resholve.1
67	@echo Building plain-text copy of manpage
68	@groff -m mdoc -T utf8 $< | col -bx > $@
69
70_resholve/strings.py: docs/strings.wwst docs/strings.css docs/content.wwst
71	@echo Wursting $@ from $<
72	@wordswurst $< > $@
73
74update: timings.md demos.md docs/resholve.1.txt docs/README.nixpkgs.md
Discussing this elsewhere?
Enter 'link' for a markdown link
or 'tweet <message>' for a pre-populated Tweet :)
Want to subscribe? Enter 'rss' for a feed URL.
>