mirror of
https://codeberg.org/mhwombat/nix-book.git
synced 2025-12-27 00:34:58 +08:00
2283 lines
No EOL
108 KiB
HTML
2283 lines
No EOL
108 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="en">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<meta name="generator" content="Asciidoctor 2.0.18">
|
||
<meta name="author" content="{authors}, {build_date}">
|
||
<title>Wombat’s Book of Nix</title>
|
||
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Open+Sans:300,300italic,400,400italic,600,600italic%7CNoto+Serif:400,400italic,700,700italic%7CDroid+Sans+Mono:400,700">
|
||
<style>
|
||
/*! Asciidoctor default stylesheet | MIT License | https://asciidoctor.org */
|
||
/* Uncomment the following line when using as a custom stylesheet */
|
||
/* @import "https://fonts.googleapis.com/css?family=Open+Sans:300,300italic,400,400italic,600,600italic%7CNoto+Serif:400,400italic,700,700italic%7CDroid+Sans+Mono:400,700"; */
|
||
html{font-family:sans-serif;-webkit-text-size-adjust:100%}
|
||
a{background:none}
|
||
a:focus{outline:thin dotted}
|
||
a:active,a:hover{outline:0}
|
||
h1{font-size:2em;margin:.67em 0}
|
||
b,strong{font-weight:bold}
|
||
abbr{font-size:.9em}
|
||
abbr[title]{cursor:help;border-bottom:1px dotted #dddddf;text-decoration:none}
|
||
dfn{font-style:italic}
|
||
hr{height:0}
|
||
mark{background:#ff0;color:#000}
|
||
code,kbd,pre,samp{font-family:monospace;font-size:1em}
|
||
pre{white-space:pre-wrap}
|
||
q{quotes:"\201C" "\201D" "\2018" "\2019"}
|
||
small{font-size:80%}
|
||
sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}
|
||
sup{top:-.5em}
|
||
sub{bottom:-.25em}
|
||
img{border:0}
|
||
svg:not(:root){overflow:hidden}
|
||
figure{margin:0}
|
||
audio,video{display:inline-block}
|
||
audio:not([controls]){display:none;height:0}
|
||
fieldset{border:1px solid silver;margin:0 2px;padding:.35em .625em .75em}
|
||
legend{border:0;padding:0}
|
||
button,input,select,textarea{font-family:inherit;font-size:100%;margin:0}
|
||
button,input{line-height:normal}
|
||
button,select{text-transform:none}
|
||
button,html input[type=button],input[type=reset],input[type=submit]{-webkit-appearance:button;cursor:pointer}
|
||
button[disabled],html input[disabled]{cursor:default}
|
||
input[type=checkbox],input[type=radio]{padding:0}
|
||
button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}
|
||
textarea{overflow:auto;vertical-align:top}
|
||
table{border-collapse:collapse;border-spacing:0}
|
||
*,::before,::after{box-sizing:border-box}
|
||
html,body{font-size:100%}
|
||
body{background:#fff;color:rgba(0,0,0,.8);padding:0;margin:0;font-family:"Noto Serif","DejaVu Serif",serif;line-height:1;position:relative;cursor:auto;-moz-tab-size:4;-o-tab-size:4;tab-size:4;word-wrap:anywhere;-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased}
|
||
a:hover{cursor:pointer}
|
||
img,object,embed{max-width:100%;height:auto}
|
||
object,embed{height:100%}
|
||
img{-ms-interpolation-mode:bicubic}
|
||
.left{float:left!important}
|
||
.right{float:right!important}
|
||
.text-left{text-align:left!important}
|
||
.text-right{text-align:right!important}
|
||
.text-center{text-align:center!important}
|
||
.text-justify{text-align:justify!important}
|
||
.hide{display:none}
|
||
img,object,svg{display:inline-block;vertical-align:middle}
|
||
textarea{height:auto;min-height:50px}
|
||
select{width:100%}
|
||
.subheader,.admonitionblock td.content>.title,.audioblock>.title,.exampleblock>.title,.imageblock>.title,.listingblock>.title,.literalblock>.title,.stemblock>.title,.openblock>.title,.paragraph>.title,.quoteblock>.title,table.tableblock>.title,.verseblock>.title,.videoblock>.title,.dlist>.title,.olist>.title,.ulist>.title,.qlist>.title,.hdlist>.title{line-height:1.45;color:#7a2518;font-weight:400;margin-top:0;margin-bottom:.25em}
|
||
div,dl,dt,dd,ul,ol,li,h1,h2,h3,#toctitle,.sidebarblock>.content>.title,h4,h5,h6,pre,form,p,blockquote,th,td{margin:0;padding:0}
|
||
a{color:#2156a5;text-decoration:underline;line-height:inherit}
|
||
a:hover,a:focus{color:#1d4b8f}
|
||
a img{border:0}
|
||
p{line-height:1.6;margin-bottom:1.25em;text-rendering:optimizeLegibility}
|
||
p aside{font-size:.875em;line-height:1.35;font-style:italic}
|
||
h1,h2,h3,#toctitle,.sidebarblock>.content>.title,h4,h5,h6{font-family:"Open Sans","DejaVu Sans",sans-serif;font-weight:300;font-style:normal;color:#ba3925;text-rendering:optimizeLegibility;margin-top:1em;margin-bottom:.5em;line-height:1.0125em}
|
||
h1 small,h2 small,h3 small,#toctitle small,.sidebarblock>.content>.title small,h4 small,h5 small,h6 small{font-size:60%;color:#e99b8f;line-height:0}
|
||
h1{font-size:2.125em}
|
||
h2{font-size:1.6875em}
|
||
h3,#toctitle,.sidebarblock>.content>.title{font-size:1.375em}
|
||
h4,h5{font-size:1.125em}
|
||
h6{font-size:1em}
|
||
hr{border:solid #dddddf;border-width:1px 0 0;clear:both;margin:1.25em 0 1.1875em}
|
||
em,i{font-style:italic;line-height:inherit}
|
||
strong,b{font-weight:bold;line-height:inherit}
|
||
small{font-size:60%;line-height:inherit}
|
||
code{font-family:"Droid Sans Mono","DejaVu Sans Mono",monospace;font-weight:400;color:rgba(0,0,0,.9)}
|
||
ul,ol,dl{line-height:1.6;margin-bottom:1.25em;list-style-position:outside;font-family:inherit}
|
||
ul,ol{margin-left:1.5em}
|
||
ul li ul,ul li ol{margin-left:1.25em;margin-bottom:0}
|
||
ul.circle{list-style-type:circle}
|
||
ul.disc{list-style-type:disc}
|
||
ul.square{list-style-type:square}
|
||
ul.circle ul:not([class]),ul.disc ul:not([class]),ul.square ul:not([class]){list-style:inherit}
|
||
ol li ul,ol li ol{margin-left:1.25em;margin-bottom:0}
|
||
dl dt{margin-bottom:.3125em;font-weight:bold}
|
||
dl dd{margin-bottom:1.25em}
|
||
blockquote{margin:0 0 1.25em;padding:.5625em 1.25em 0 1.1875em;border-left:1px solid #ddd}
|
||
blockquote,blockquote p{line-height:1.6;color:rgba(0,0,0,.85)}
|
||
@media screen and (min-width:768px){h1,h2,h3,#toctitle,.sidebarblock>.content>.title,h4,h5,h6{line-height:1.2}
|
||
h1{font-size:2.75em}
|
||
h2{font-size:2.3125em}
|
||
h3,#toctitle,.sidebarblock>.content>.title{font-size:1.6875em}
|
||
h4{font-size:1.4375em}}
|
||
table{background:#fff;margin-bottom:1.25em;border:1px solid #dedede;word-wrap:normal}
|
||
table thead,table tfoot{background:#f7f8f7}
|
||
table thead tr th,table thead tr td,table tfoot tr th,table tfoot tr td{padding:.5em .625em .625em;font-size:inherit;color:rgba(0,0,0,.8);text-align:left}
|
||
table tr th,table tr td{padding:.5625em .625em;font-size:inherit;color:rgba(0,0,0,.8)}
|
||
table tr.even,table tr.alt{background:#f8f8f7}
|
||
table thead tr th,table tfoot tr th,table tbody tr td,table tr td,table tfoot tr td{line-height:1.6}
|
||
h1,h2,h3,#toctitle,.sidebarblock>.content>.title,h4,h5,h6{line-height:1.2;word-spacing:-.05em}
|
||
h1 strong,h2 strong,h3 strong,#toctitle strong,.sidebarblock>.content>.title strong,h4 strong,h5 strong,h6 strong{font-weight:400}
|
||
.center{margin-left:auto;margin-right:auto}
|
||
.stretch{width:100%}
|
||
.clearfix::before,.clearfix::after,.float-group::before,.float-group::after{content:" ";display:table}
|
||
.clearfix::after,.float-group::after{clear:both}
|
||
:not(pre).nobreak{word-wrap:normal}
|
||
:not(pre).nowrap{white-space:nowrap}
|
||
:not(pre).pre-wrap{white-space:pre-wrap}
|
||
:not(pre):not([class^=L])>code{font-size:.9375em;font-style:normal!important;letter-spacing:0;padding:.1em .5ex;word-spacing:-.15em;background:#f7f7f8;border-radius:4px;line-height:1.45;text-rendering:optimizeSpeed}
|
||
pre{color:rgba(0,0,0,.9);font-family:"Droid Sans Mono","DejaVu Sans Mono",monospace;line-height:1.45;text-rendering:optimizeSpeed}
|
||
pre code,pre pre{color:inherit;font-size:inherit;line-height:inherit}
|
||
pre>code{display:block}
|
||
pre.nowrap,pre.nowrap pre{white-space:pre;word-wrap:normal}
|
||
em em{font-style:normal}
|
||
strong strong{font-weight:400}
|
||
.keyseq{color:rgba(51,51,51,.8)}
|
||
kbd{font-family:"Droid Sans Mono","DejaVu Sans Mono",monospace;display:inline-block;color:rgba(0,0,0,.8);font-size:.65em;line-height:1.45;background:#f7f7f7;border:1px solid #ccc;border-radius:3px;box-shadow:0 1px 0 rgba(0,0,0,.2),inset 0 0 0 .1em #fff;margin:0 .15em;padding:.2em .5em;vertical-align:middle;position:relative;top:-.1em;white-space:nowrap}
|
||
.keyseq kbd:first-child{margin-left:0}
|
||
.keyseq kbd:last-child{margin-right:0}
|
||
.menuseq,.menuref{color:#000}
|
||
.menuseq b:not(.caret),.menuref{font-weight:inherit}
|
||
.menuseq{word-spacing:-.02em}
|
||
.menuseq b.caret{font-size:1.25em;line-height:.8}
|
||
.menuseq i.caret{font-weight:bold;text-align:center;width:.45em}
|
||
b.button::before,b.button::after{position:relative;top:-1px;font-weight:400}
|
||
b.button::before{content:"[";padding:0 3px 0 2px}
|
||
b.button::after{content:"]";padding:0 2px 0 3px}
|
||
p a>code:hover{color:rgba(0,0,0,.9)}
|
||
#header,#content,#footnotes,#footer{width:100%;margin:0 auto;max-width:62.5em;*zoom:1;position:relative;padding-left:.9375em;padding-right:.9375em}
|
||
#header::before,#header::after,#content::before,#content::after,#footnotes::before,#footnotes::after,#footer::before,#footer::after{content:" ";display:table}
|
||
#header::after,#content::after,#footnotes::after,#footer::after{clear:both}
|
||
#content{margin-top:1.25em}
|
||
#content::before{content:none}
|
||
#header>h1:first-child{color:rgba(0,0,0,.85);margin-top:2.25rem;margin-bottom:0}
|
||
#header>h1:first-child+#toc{margin-top:8px;border-top:1px solid #dddddf}
|
||
#header>h1:only-child,body.toc2 #header>h1:nth-last-child(2){border-bottom:1px solid #dddddf;padding-bottom:8px}
|
||
#header .details{border-bottom:1px solid #dddddf;line-height:1.45;padding-top:.25em;padding-bottom:.25em;padding-left:.25em;color:rgba(0,0,0,.6);display:flex;flex-flow:row wrap}
|
||
#header .details span:first-child{margin-left:-.125em}
|
||
#header .details span.email a{color:rgba(0,0,0,.85)}
|
||
#header .details br{display:none}
|
||
#header .details br+span::before{content:"\00a0\2013\00a0"}
|
||
#header .details br+span.author::before{content:"\00a0\22c5\00a0";color:rgba(0,0,0,.85)}
|
||
#header .details br+span#revremark::before{content:"\00a0|\00a0"}
|
||
#header #revnumber{text-transform:capitalize}
|
||
#header #revnumber::after{content:"\00a0"}
|
||
#content>h1:first-child:not([class]){color:rgba(0,0,0,.85);border-bottom:1px solid #dddddf;padding-bottom:8px;margin-top:0;padding-top:1rem;margin-bottom:1.25rem}
|
||
#toc{border-bottom:1px solid #e7e7e9;padding-bottom:.5em}
|
||
#toc>ul{margin-left:.125em}
|
||
#toc ul.sectlevel0>li>a{font-style:italic}
|
||
#toc ul.sectlevel0 ul.sectlevel1{margin:.5em 0}
|
||
#toc ul{font-family:"Open Sans","DejaVu Sans",sans-serif;list-style-type:none}
|
||
#toc li{line-height:1.3334;margin-top:.3334em}
|
||
#toc a{text-decoration:none}
|
||
#toc a:active{text-decoration:underline}
|
||
#toctitle{color:#7a2518;font-size:1.2em}
|
||
@media screen and (min-width:768px){#toctitle{font-size:1.375em}
|
||
body.toc2{padding-left:15em;padding-right:0}
|
||
#toc.toc2{margin-top:0!important;background:#f8f8f7;position:fixed;width:15em;left:0;top:0;border-right:1px solid #e7e7e9;border-top-width:0!important;border-bottom-width:0!important;z-index:1000;padding:1.25em 1em;height:100%;overflow:auto}
|
||
#toc.toc2 #toctitle{margin-top:0;margin-bottom:.8rem;font-size:1.2em}
|
||
#toc.toc2>ul{font-size:.9em;margin-bottom:0}
|
||
#toc.toc2 ul ul{margin-left:0;padding-left:1em}
|
||
#toc.toc2 ul.sectlevel0 ul.sectlevel1{padding-left:0;margin-top:.5em;margin-bottom:.5em}
|
||
body.toc2.toc-right{padding-left:0;padding-right:15em}
|
||
body.toc2.toc-right #toc.toc2{border-right-width:0;border-left:1px solid #e7e7e9;left:auto;right:0}}
|
||
@media screen and (min-width:1280px){body.toc2{padding-left:20em;padding-right:0}
|
||
#toc.toc2{width:20em}
|
||
#toc.toc2 #toctitle{font-size:1.375em}
|
||
#toc.toc2>ul{font-size:.95em}
|
||
#toc.toc2 ul ul{padding-left:1.25em}
|
||
body.toc2.toc-right{padding-left:0;padding-right:20em}}
|
||
#content #toc{border:1px solid #e0e0dc;margin-bottom:1.25em;padding:1.25em;background:#f8f8f7;border-radius:4px}
|
||
#content #toc>:first-child{margin-top:0}
|
||
#content #toc>:last-child{margin-bottom:0}
|
||
#footer{max-width:none;background:rgba(0,0,0,.8);padding:1.25em}
|
||
#footer-text{color:hsla(0,0%,100%,.8);line-height:1.44}
|
||
#content{margin-bottom:.625em}
|
||
.sect1{padding-bottom:.625em}
|
||
@media screen and (min-width:768px){#content{margin-bottom:1.25em}
|
||
.sect1{padding-bottom:1.25em}}
|
||
.sect1:last-child{padding-bottom:0}
|
||
.sect1+.sect1{border-top:1px solid #e7e7e9}
|
||
#content h1>a.anchor,h2>a.anchor,h3>a.anchor,#toctitle>a.anchor,.sidebarblock>.content>.title>a.anchor,h4>a.anchor,h5>a.anchor,h6>a.anchor{position:absolute;z-index:1001;width:1.5ex;margin-left:-1.5ex;display:block;text-decoration:none!important;visibility:hidden;text-align:center;font-weight:400}
|
||
#content h1>a.anchor::before,h2>a.anchor::before,h3>a.anchor::before,#toctitle>a.anchor::before,.sidebarblock>.content>.title>a.anchor::before,h4>a.anchor::before,h5>a.anchor::before,h6>a.anchor::before{content:"\00A7";font-size:.85em;display:block;padding-top:.1em}
|
||
#content h1:hover>a.anchor,#content h1>a.anchor:hover,h2:hover>a.anchor,h2>a.anchor:hover,h3:hover>a.anchor,#toctitle:hover>a.anchor,.sidebarblock>.content>.title:hover>a.anchor,h3>a.anchor:hover,#toctitle>a.anchor:hover,.sidebarblock>.content>.title>a.anchor:hover,h4:hover>a.anchor,h4>a.anchor:hover,h5:hover>a.anchor,h5>a.anchor:hover,h6:hover>a.anchor,h6>a.anchor:hover{visibility:visible}
|
||
#content h1>a.link,h2>a.link,h3>a.link,#toctitle>a.link,.sidebarblock>.content>.title>a.link,h4>a.link,h5>a.link,h6>a.link{color:#ba3925;text-decoration:none}
|
||
#content h1>a.link:hover,h2>a.link:hover,h3>a.link:hover,#toctitle>a.link:hover,.sidebarblock>.content>.title>a.link:hover,h4>a.link:hover,h5>a.link:hover,h6>a.link:hover{color:#a53221}
|
||
details,.audioblock,.imageblock,.literalblock,.listingblock,.stemblock,.videoblock{margin-bottom:1.25em}
|
||
details{margin-left:1.25rem}
|
||
details>summary{cursor:pointer;display:block;position:relative;line-height:1.6;margin-bottom:.625rem;outline:none;-webkit-tap-highlight-color:transparent}
|
||
details>summary::-webkit-details-marker{display:none}
|
||
details>summary::before{content:"";border:solid transparent;border-left:solid;border-width:.3em 0 .3em .5em;position:absolute;top:.5em;left:-1.25rem;transform:translateX(15%)}
|
||
details[open]>summary::before{border:solid transparent;border-top:solid;border-width:.5em .3em 0;transform:translateY(15%)}
|
||
details>summary::after{content:"";width:1.25rem;height:1em;position:absolute;top:.3em;left:-1.25rem}
|
||
.admonitionblock td.content>.title,.audioblock>.title,.exampleblock>.title,.imageblock>.title,.listingblock>.title,.literalblock>.title,.stemblock>.title,.openblock>.title,.paragraph>.title,.quoteblock>.title,table.tableblock>.title,.verseblock>.title,.videoblock>.title,.dlist>.title,.olist>.title,.ulist>.title,.qlist>.title,.hdlist>.title{text-rendering:optimizeLegibility;text-align:left;font-family:"Noto Serif","DejaVu Serif",serif;font-size:1rem;font-style:italic}
|
||
table.tableblock.fit-content>caption.title{white-space:nowrap;width:0}
|
||
.paragraph.lead>p,#preamble>.sectionbody>[class=paragraph]:first-of-type p{font-size:1.21875em;line-height:1.6;color:rgba(0,0,0,.85)}
|
||
.admonitionblock>table{border-collapse:separate;border:0;background:none;width:100%}
|
||
.admonitionblock>table td.icon{text-align:center;width:80px}
|
||
.admonitionblock>table td.icon img{max-width:none}
|
||
.admonitionblock>table td.icon .title{font-weight:bold;font-family:"Open Sans","DejaVu Sans",sans-serif;text-transform:uppercase}
|
||
.admonitionblock>table td.content{padding-left:1.125em;padding-right:1.25em;border-left:1px solid #dddddf;color:rgba(0,0,0,.6);word-wrap:anywhere}
|
||
.admonitionblock>table td.content>:last-child>:last-child{margin-bottom:0}
|
||
.exampleblock>.content{border:1px solid #e6e6e6;margin-bottom:1.25em;padding:1.25em;background:#fff;border-radius:4px}
|
||
.exampleblock>.content>:first-child{margin-top:0}
|
||
.exampleblock>.content>:last-child{margin-bottom:0}
|
||
.sidebarblock{border:1px solid #dbdbd6;margin-bottom:1.25em;padding:1.25em;background:#f3f3f2;border-radius:4px}
|
||
.sidebarblock>:first-child{margin-top:0}
|
||
.sidebarblock>:last-child{margin-bottom:0}
|
||
.sidebarblock>.content>.title{color:#7a2518;margin-top:0;text-align:center}
|
||
.exampleblock>.content>:last-child>:last-child,.exampleblock>.content .olist>ol>li:last-child>:last-child,.exampleblock>.content .ulist>ul>li:last-child>:last-child,.exampleblock>.content .qlist>ol>li:last-child>:last-child,.sidebarblock>.content>:last-child>:last-child,.sidebarblock>.content .olist>ol>li:last-child>:last-child,.sidebarblock>.content .ulist>ul>li:last-child>:last-child,.sidebarblock>.content .qlist>ol>li:last-child>:last-child{margin-bottom:0}
|
||
.literalblock pre,.listingblock>.content>pre{border-radius:4px;overflow-x:auto;padding:1em;font-size:.8125em}
|
||
@media screen and (min-width:768px){.literalblock pre,.listingblock>.content>pre{font-size:.90625em}}
|
||
@media screen and (min-width:1280px){.literalblock pre,.listingblock>.content>pre{font-size:1em}}
|
||
.literalblock pre,.listingblock>.content>pre:not(.highlight),.listingblock>.content>pre[class=highlight],.listingblock>.content>pre[class^="highlight "]{background:#f7f7f8}
|
||
.literalblock.output pre{color:#f7f7f8;background:rgba(0,0,0,.9)}
|
||
.listingblock>.content{position:relative}
|
||
.listingblock code[data-lang]::before{display:none;content:attr(data-lang);position:absolute;font-size:.75em;top:.425rem;right:.5rem;line-height:1;text-transform:uppercase;color:inherit;opacity:.5}
|
||
.listingblock:hover code[data-lang]::before{display:block}
|
||
.listingblock.terminal pre .command::before{content:attr(data-prompt);padding-right:.5em;color:inherit;opacity:.5}
|
||
.listingblock.terminal pre .command:not([data-prompt])::before{content:"$"}
|
||
.listingblock pre.highlightjs{padding:0}
|
||
.listingblock pre.highlightjs>code{padding:1em;border-radius:4px}
|
||
.listingblock pre.prettyprint{border-width:0}
|
||
.prettyprint{background:#f7f7f8}
|
||
pre.prettyprint .linenums{line-height:1.45;margin-left:2em}
|
||
pre.prettyprint li{background:none;list-style-type:inherit;padding-left:0}
|
||
pre.prettyprint li code[data-lang]::before{opacity:1}
|
||
pre.prettyprint li:not(:first-child) code[data-lang]::before{display:none}
|
||
table.linenotable{border-collapse:separate;border:0;margin-bottom:0;background:none}
|
||
table.linenotable td[class]{color:inherit;vertical-align:top;padding:0;line-height:inherit;white-space:normal}
|
||
table.linenotable td.code{padding-left:.75em}
|
||
table.linenotable td.linenos,pre.pygments .linenos{border-right:1px solid;opacity:.35;padding-right:.5em;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}
|
||
pre.pygments span.linenos{display:inline-block;margin-right:.75em}
|
||
.quoteblock{margin:0 1em 1.25em 1.5em;display:table}
|
||
.quoteblock:not(.excerpt)>.title{margin-left:-1.5em;margin-bottom:.75em}
|
||
.quoteblock blockquote,.quoteblock p{color:rgba(0,0,0,.85);font-size:1.15rem;line-height:1.75;word-spacing:.1em;letter-spacing:0;font-style:italic;text-align:justify}
|
||
.quoteblock blockquote{margin:0;padding:0;border:0}
|
||
.quoteblock blockquote::before{content:"\201c";float:left;font-size:2.75em;font-weight:bold;line-height:.6em;margin-left:-.6em;color:#7a2518;text-shadow:0 1px 2px rgba(0,0,0,.1)}
|
||
.quoteblock blockquote>.paragraph:last-child p{margin-bottom:0}
|
||
.quoteblock .attribution{margin-top:.75em;margin-right:.5ex;text-align:right}
|
||
.verseblock{margin:0 1em 1.25em}
|
||
.verseblock pre{font-family:"Open Sans","DejaVu Sans",sans-serif;font-size:1.15rem;color:rgba(0,0,0,.85);font-weight:300;text-rendering:optimizeLegibility}
|
||
.verseblock pre strong{font-weight:400}
|
||
.verseblock .attribution{margin-top:1.25rem;margin-left:.5ex}
|
||
.quoteblock .attribution,.verseblock .attribution{font-size:.9375em;line-height:1.45;font-style:italic}
|
||
.quoteblock .attribution br,.verseblock .attribution br{display:none}
|
||
.quoteblock .attribution cite,.verseblock .attribution cite{display:block;letter-spacing:-.025em;color:rgba(0,0,0,.6)}
|
||
.quoteblock.abstract blockquote::before,.quoteblock.excerpt blockquote::before,.quoteblock .quoteblock blockquote::before{display:none}
|
||
.quoteblock.abstract blockquote,.quoteblock.abstract p,.quoteblock.excerpt blockquote,.quoteblock.excerpt p,.quoteblock .quoteblock blockquote,.quoteblock .quoteblock p{line-height:1.6;word-spacing:0}
|
||
.quoteblock.abstract{margin:0 1em 1.25em;display:block}
|
||
.quoteblock.abstract>.title{margin:0 0 .375em;font-size:1.15em;text-align:center}
|
||
.quoteblock.excerpt>blockquote,.quoteblock .quoteblock{padding:0 0 .25em 1em;border-left:.25em solid #dddddf}
|
||
.quoteblock.excerpt,.quoteblock .quoteblock{margin-left:0}
|
||
.quoteblock.excerpt blockquote,.quoteblock.excerpt p,.quoteblock .quoteblock blockquote,.quoteblock .quoteblock p{color:inherit;font-size:1.0625rem}
|
||
.quoteblock.excerpt .attribution,.quoteblock .quoteblock .attribution{color:inherit;font-size:.85rem;text-align:left;margin-right:0}
|
||
p.tableblock:last-child{margin-bottom:0}
|
||
td.tableblock>.content{margin-bottom:1.25em;word-wrap:anywhere}
|
||
td.tableblock>.content>:last-child{margin-bottom:-1.25em}
|
||
table.tableblock,th.tableblock,td.tableblock{border:0 solid #dedede}
|
||
table.grid-all>*>tr>*{border-width:1px}
|
||
table.grid-cols>*>tr>*{border-width:0 1px}
|
||
table.grid-rows>*>tr>*{border-width:1px 0}
|
||
table.frame-all{border-width:1px}
|
||
table.frame-ends{border-width:1px 0}
|
||
table.frame-sides{border-width:0 1px}
|
||
table.frame-none>colgroup+*>:first-child>*,table.frame-sides>colgroup+*>:first-child>*{border-top-width:0}
|
||
table.frame-none>:last-child>:last-child>*,table.frame-sides>:last-child>:last-child>*{border-bottom-width:0}
|
||
table.frame-none>*>tr>:first-child,table.frame-ends>*>tr>:first-child{border-left-width:0}
|
||
table.frame-none>*>tr>:last-child,table.frame-ends>*>tr>:last-child{border-right-width:0}
|
||
table.stripes-all>*>tr,table.stripes-odd>*>tr:nth-of-type(odd),table.stripes-even>*>tr:nth-of-type(even),table.stripes-hover>*>tr:hover{background:#f8f8f7}
|
||
th.halign-left,td.halign-left{text-align:left}
|
||
th.halign-right,td.halign-right{text-align:right}
|
||
th.halign-center,td.halign-center{text-align:center}
|
||
th.valign-top,td.valign-top{vertical-align:top}
|
||
th.valign-bottom,td.valign-bottom{vertical-align:bottom}
|
||
th.valign-middle,td.valign-middle{vertical-align:middle}
|
||
table thead th,table tfoot th{font-weight:bold}
|
||
tbody tr th{background:#f7f8f7}
|
||
tbody tr th,tbody tr th p,tfoot tr th,tfoot tr th p{color:rgba(0,0,0,.8);font-weight:bold}
|
||
p.tableblock>code:only-child{background:none;padding:0}
|
||
p.tableblock{font-size:1em}
|
||
ol{margin-left:1.75em}
|
||
ul li ol{margin-left:1.5em}
|
||
dl dd{margin-left:1.125em}
|
||
dl dd:last-child,dl dd:last-child>:last-child{margin-bottom:0}
|
||
li p,ul dd,ol dd,.olist .olist,.ulist .ulist,.ulist .olist,.olist .ulist{margin-bottom:.625em}
|
||
ul.checklist,ul.none,ol.none,ul.no-bullet,ol.no-bullet,ol.unnumbered,ul.unstyled,ol.unstyled{list-style-type:none}
|
||
ul.no-bullet,ol.no-bullet,ol.unnumbered{margin-left:.625em}
|
||
ul.unstyled,ol.unstyled{margin-left:0}
|
||
li>p:empty:only-child::before{content:"";display:inline-block}
|
||
ul.checklist>li>p:first-child{margin-left:-1em}
|
||
ul.checklist>li>p:first-child>.fa-square-o:first-child,ul.checklist>li>p:first-child>.fa-check-square-o:first-child{width:1.25em;font-size:.8em;position:relative;bottom:.125em}
|
||
ul.checklist>li>p:first-child>input[type=checkbox]:first-child{margin-right:.25em}
|
||
ul.inline{display:flex;flex-flow:row wrap;list-style:none;margin:0 0 .625em -1.25em}
|
||
ul.inline>li{margin-left:1.25em}
|
||
.unstyled dl dt{font-weight:400;font-style:normal}
|
||
ol.arabic{list-style-type:decimal}
|
||
ol.decimal{list-style-type:decimal-leading-zero}
|
||
ol.loweralpha{list-style-type:lower-alpha}
|
||
ol.upperalpha{list-style-type:upper-alpha}
|
||
ol.lowerroman{list-style-type:lower-roman}
|
||
ol.upperroman{list-style-type:upper-roman}
|
||
ol.lowergreek{list-style-type:lower-greek}
|
||
.hdlist>table,.colist>table{border:0;background:none}
|
||
.hdlist>table>tbody>tr,.colist>table>tbody>tr{background:none}
|
||
td.hdlist1,td.hdlist2{vertical-align:top;padding:0 .625em}
|
||
td.hdlist1{font-weight:bold;padding-bottom:1.25em}
|
||
td.hdlist2{word-wrap:anywhere}
|
||
.literalblock+.colist,.listingblock+.colist{margin-top:-.5em}
|
||
.colist td:not([class]):first-child{padding:.4em .75em 0;line-height:1;vertical-align:top}
|
||
.colist td:not([class]):first-child img{max-width:none}
|
||
.colist td:not([class]):last-child{padding:.25em 0}
|
||
.thumb,.th{line-height:0;display:inline-block;border:4px solid #fff;box-shadow:0 0 0 1px #ddd}
|
||
.imageblock.left{margin:.25em .625em 1.25em 0}
|
||
.imageblock.right{margin:.25em 0 1.25em .625em}
|
||
.imageblock>.title{margin-bottom:0}
|
||
.imageblock.thumb,.imageblock.th{border-width:6px}
|
||
.imageblock.thumb>.title,.imageblock.th>.title{padding:0 .125em}
|
||
.image.left,.image.right{margin-top:.25em;margin-bottom:.25em;display:inline-block;line-height:0}
|
||
.image.left{margin-right:.625em}
|
||
.image.right{margin-left:.625em}
|
||
a.image{text-decoration:none;display:inline-block}
|
||
a.image object{pointer-events:none}
|
||
sup.footnote,sup.footnoteref{font-size:.875em;position:static;vertical-align:super}
|
||
sup.footnote a,sup.footnoteref a{text-decoration:none}
|
||
sup.footnote a:active,sup.footnoteref a:active{text-decoration:underline}
|
||
#footnotes{padding-top:.75em;padding-bottom:.75em;margin-bottom:.625em}
|
||
#footnotes hr{width:20%;min-width:6.25em;margin:-.25em 0 .75em;border-width:1px 0 0}
|
||
#footnotes .footnote{padding:0 .375em 0 .225em;line-height:1.3334;font-size:.875em;margin-left:1.2em;margin-bottom:.2em}
|
||
#footnotes .footnote a:first-of-type{font-weight:bold;text-decoration:none;margin-left:-1.05em}
|
||
#footnotes .footnote:last-of-type{margin-bottom:0}
|
||
#content #footnotes{margin-top:-.625em;margin-bottom:0;padding:.75em 0}
|
||
div.unbreakable{page-break-inside:avoid}
|
||
.big{font-size:larger}
|
||
.small{font-size:smaller}
|
||
.underline{text-decoration:underline}
|
||
.overline{text-decoration:overline}
|
||
.line-through{text-decoration:line-through}
|
||
.aqua{color:#00bfbf}
|
||
.aqua-background{background:#00fafa}
|
||
.black{color:#000}
|
||
.black-background{background:#000}
|
||
.blue{color:#0000bf}
|
||
.blue-background{background:#0000fa}
|
||
.fuchsia{color:#bf00bf}
|
||
.fuchsia-background{background:#fa00fa}
|
||
.gray{color:#606060}
|
||
.gray-background{background:#7d7d7d}
|
||
.green{color:#006000}
|
||
.green-background{background:#007d00}
|
||
.lime{color:#00bf00}
|
||
.lime-background{background:#00fa00}
|
||
.maroon{color:#600000}
|
||
.maroon-background{background:#7d0000}
|
||
.navy{color:#000060}
|
||
.navy-background{background:#00007d}
|
||
.olive{color:#606000}
|
||
.olive-background{background:#7d7d00}
|
||
.purple{color:#600060}
|
||
.purple-background{background:#7d007d}
|
||
.red{color:#bf0000}
|
||
.red-background{background:#fa0000}
|
||
.silver{color:#909090}
|
||
.silver-background{background:#bcbcbc}
|
||
.teal{color:#006060}
|
||
.teal-background{background:#007d7d}
|
||
.white{color:#bfbfbf}
|
||
.white-background{background:#fafafa}
|
||
.yellow{color:#bfbf00}
|
||
.yellow-background{background:#fafa00}
|
||
span.icon>.fa{cursor:default}
|
||
a span.icon>.fa{cursor:inherit}
|
||
.admonitionblock td.icon [class^="fa icon-"]{font-size:2.5em;text-shadow:1px 1px 2px rgba(0,0,0,.5);cursor:default}
|
||
.admonitionblock td.icon .icon-note::before{content:"\f05a";color:#19407c}
|
||
.admonitionblock td.icon .icon-tip::before{content:"\f0eb";text-shadow:1px 1px 2px rgba(155,155,0,.8);color:#111}
|
||
.admonitionblock td.icon .icon-warning::before{content:"\f071";color:#bf6900}
|
||
.admonitionblock td.icon .icon-caution::before{content:"\f06d";color:#bf3400}
|
||
.admonitionblock td.icon .icon-important::before{content:"\f06a";color:#bf0000}
|
||
.conum[data-value]{display:inline-block;color:#fff!important;background:rgba(0,0,0,.8);border-radius:50%;text-align:center;font-size:.75em;width:1.67em;height:1.67em;line-height:1.67em;font-family:"Open Sans","DejaVu Sans",sans-serif;font-style:normal;font-weight:bold}
|
||
.conum[data-value] *{color:#fff!important}
|
||
.conum[data-value]+b{display:none}
|
||
.conum[data-value]::after{content:attr(data-value)}
|
||
pre .conum[data-value]{position:relative;top:-.125em}
|
||
b.conum *{color:inherit!important}
|
||
.conum:not([data-value]):empty{display:none}
|
||
dt,th.tableblock,td.content,div.footnote{text-rendering:optimizeLegibility}
|
||
h1,h2,p,td.content,span.alt,summary{letter-spacing:-.01em}
|
||
p strong,td.content strong,div.footnote strong{letter-spacing:-.005em}
|
||
p,blockquote,dt,td.content,span.alt,summary{font-size:1.0625rem}
|
||
p{margin-bottom:1.25rem}
|
||
.sidebarblock p,.sidebarblock dt,.sidebarblock td.content,p.tableblock{font-size:1em}
|
||
.exampleblock>.content{background:#fffef7;border-color:#e0e0dc;box-shadow:0 1px 4px #e0e0dc}
|
||
.print-only{display:none!important}
|
||
@page{margin:1.25cm .75cm}
|
||
@media print{*{box-shadow:none!important;text-shadow:none!important}
|
||
html{font-size:80%}
|
||
a{color:inherit!important;text-decoration:underline!important}
|
||
a.bare,a[href^="#"],a[href^="mailto:"]{text-decoration:none!important}
|
||
a[href^="http:"]:not(.bare)::after,a[href^="https:"]:not(.bare)::after{content:"(" attr(href) ")";display:inline-block;font-size:.875em;padding-left:.25em}
|
||
abbr[title]{border-bottom:1px dotted}
|
||
abbr[title]::after{content:" (" attr(title) ")"}
|
||
pre,blockquote,tr,img,object,svg{page-break-inside:avoid}
|
||
thead{display:table-header-group}
|
||
svg{max-width:100%}
|
||
p,blockquote,dt,td.content{font-size:1em;orphans:3;widows:3}
|
||
h2,h3,#toctitle,.sidebarblock>.content>.title{page-break-after:avoid}
|
||
#header,#content,#footnotes,#footer{max-width:none}
|
||
#toc,.sidebarblock,.exampleblock>.content{background:none!important}
|
||
#toc{border-bottom:1px solid #dddddf!important;padding-bottom:0!important}
|
||
body.book #header{text-align:center}
|
||
body.book #header>h1:first-child{border:0!important;margin:2.5em 0 1em}
|
||
body.book #header .details{border:0!important;display:block;padding:0!important}
|
||
body.book #header .details span:first-child{margin-left:0!important}
|
||
body.book #header .details br{display:block}
|
||
body.book #header .details br+span::before{content:none!important}
|
||
body.book #toc{border:0!important;text-align:left!important;padding:0!important;margin:0!important}
|
||
body.book #toc,body.book #preamble,body.book h1.sect0,body.book .sect1>h2{page-break-before:always}
|
||
.listingblock code[data-lang]::before{display:block}
|
||
#footer{padding:0 .9375em}
|
||
.hide-on-print{display:none!important}
|
||
.print-only{display:block!important}
|
||
.hide-for-print{display:none!important}
|
||
.show-for-print{display:inherit!important}}
|
||
@media amzn-kf8,print{#header>h1:first-child{margin-top:1.25rem}
|
||
.sect1{padding:0!important}
|
||
.sect1+.sect1{border:0}
|
||
#footer{background:none}
|
||
#footer-text{color:rgba(0,0,0,.6);font-size:.9em}}
|
||
@media amzn-kf8{#header,#content,#footnotes,#footer{padding:0}}
|
||
</style>
|
||
<style>
|
||
pre.pygments .hll { background-color: #ffffcc }
|
||
pre.pygments { background: #f0f3f3; }
|
||
pre.pygments .tok-c { color: #0099FF; font-style: italic } /* Comment */
|
||
pre.pygments .tok-err { color: #AA0000; background-color: #FFAAAA } /* Error */
|
||
pre.pygments .tok-k { color: #006699; font-weight: bold } /* Keyword */
|
||
pre.pygments .tok-o { color: #555555 } /* Operator */
|
||
pre.pygments .tok-ch { color: #0099FF; font-style: italic } /* Comment.Hashbang */
|
||
pre.pygments .tok-cm { color: #0099FF; font-style: italic } /* Comment.Multiline */
|
||
pre.pygments .tok-cp { color: #009999 } /* Comment.Preproc */
|
||
pre.pygments .tok-cpf { color: #0099FF; font-style: italic } /* Comment.PreprocFile */
|
||
pre.pygments .tok-c1 { color: #0099FF; font-style: italic } /* Comment.Single */
|
||
pre.pygments .tok-cs { color: #0099FF; font-weight: bold; font-style: italic } /* Comment.Special */
|
||
pre.pygments .tok-gd { background-color: #FFCCCC; border: 1px solid #CC0000 } /* Generic.Deleted */
|
||
pre.pygments .tok-ge { font-style: italic } /* Generic.Emph */
|
||
pre.pygments .tok-gr { color: #FF0000 } /* Generic.Error */
|
||
pre.pygments .tok-gh { color: #003300; font-weight: bold } /* Generic.Heading */
|
||
pre.pygments .tok-gi { background-color: #CCFFCC; border: 1px solid #00CC00 } /* Generic.Inserted */
|
||
pre.pygments .tok-go { color: #AAAAAA } /* Generic.Output */
|
||
pre.pygments .tok-gp { color: #000099; font-weight: bold } /* Generic.Prompt */
|
||
pre.pygments .tok-gs { font-weight: bold } /* Generic.Strong */
|
||
pre.pygments .tok-gu { color: #003300; font-weight: bold } /* Generic.Subheading */
|
||
pre.pygments .tok-gt { color: #99CC66 } /* Generic.Traceback */
|
||
pre.pygments .tok-kc { color: #006699; font-weight: bold } /* Keyword.Constant */
|
||
pre.pygments .tok-kd { color: #006699; font-weight: bold } /* Keyword.Declaration */
|
||
pre.pygments .tok-kn { color: #006699; font-weight: bold } /* Keyword.Namespace */
|
||
pre.pygments .tok-kp { color: #006699 } /* Keyword.Pseudo */
|
||
pre.pygments .tok-kr { color: #006699; font-weight: bold } /* Keyword.Reserved */
|
||
pre.pygments .tok-kt { color: #007788; font-weight: bold } /* Keyword.Type */
|
||
pre.pygments .tok-m { color: #FF6600 } /* Literal.Number */
|
||
pre.pygments .tok-s { color: #CC3300 } /* Literal.String */
|
||
pre.pygments .tok-na { color: #330099 } /* Name.Attribute */
|
||
pre.pygments .tok-nb { color: #336666 } /* Name.Builtin */
|
||
pre.pygments .tok-nc { color: #00AA88; font-weight: bold } /* Name.Class */
|
||
pre.pygments .tok-no { color: #336600 } /* Name.Constant */
|
||
pre.pygments .tok-nd { color: #9999FF } /* Name.Decorator */
|
||
pre.pygments .tok-ni { color: #999999; font-weight: bold } /* Name.Entity */
|
||
pre.pygments .tok-ne { color: #CC0000; font-weight: bold } /* Name.Exception */
|
||
pre.pygments .tok-nf { color: #CC00FF } /* Name.Function */
|
||
pre.pygments .tok-nl { color: #9999FF } /* Name.Label */
|
||
pre.pygments .tok-nn { color: #00CCFF; font-weight: bold } /* Name.Namespace */
|
||
pre.pygments .tok-nt { color: #330099; font-weight: bold } /* Name.Tag */
|
||
pre.pygments .tok-nv { color: #003333 } /* Name.Variable */
|
||
pre.pygments .tok-ow { color: #000000; font-weight: bold } /* Operator.Word */
|
||
pre.pygments .tok-w { color: #bbbbbb } /* Text.Whitespace */
|
||
pre.pygments .tok-mb { color: #FF6600 } /* Literal.Number.Bin */
|
||
pre.pygments .tok-mf { color: #FF6600 } /* Literal.Number.Float */
|
||
pre.pygments .tok-mh { color: #FF6600 } /* Literal.Number.Hex */
|
||
pre.pygments .tok-mi { color: #FF6600 } /* Literal.Number.Integer */
|
||
pre.pygments .tok-mo { color: #FF6600 } /* Literal.Number.Oct */
|
||
pre.pygments .tok-sa { color: #CC3300 } /* Literal.String.Affix */
|
||
pre.pygments .tok-sb { color: #CC3300 } /* Literal.String.Backtick */
|
||
pre.pygments .tok-sc { color: #CC3300 } /* Literal.String.Char */
|
||
pre.pygments .tok-dl { color: #CC3300 } /* Literal.String.Delimiter */
|
||
pre.pygments .tok-sd { color: #CC3300; font-style: italic } /* Literal.String.Doc */
|
||
pre.pygments .tok-s2 { color: #CC3300 } /* Literal.String.Double */
|
||
pre.pygments .tok-se { color: #CC3300; font-weight: bold } /* Literal.String.Escape */
|
||
pre.pygments .tok-sh { color: #CC3300 } /* Literal.String.Heredoc */
|
||
pre.pygments .tok-si { color: #AA0000 } /* Literal.String.Interpol */
|
||
pre.pygments .tok-sx { color: #CC3300 } /* Literal.String.Other */
|
||
pre.pygments .tok-sr { color: #33AAAA } /* Literal.String.Regex */
|
||
pre.pygments .tok-s1 { color: #CC3300 } /* Literal.String.Single */
|
||
pre.pygments .tok-ss { color: #FFCC33 } /* Literal.String.Symbol */
|
||
pre.pygments .tok-bp { color: #336666 } /* Name.Builtin.Pseudo */
|
||
pre.pygments .tok-fm { color: #CC00FF } /* Name.Function.Magic */
|
||
pre.pygments .tok-vc { color: #003333 } /* Name.Variable.Class */
|
||
pre.pygments .tok-vg { color: #003333 } /* Name.Variable.Global */
|
||
pre.pygments .tok-vi { color: #003333 } /* Name.Variable.Instance */
|
||
pre.pygments .tok-vm { color: #003333 } /* Name.Variable.Magic */
|
||
pre.pygments .tok-il { color: #FF6600 } /* Literal.Number.Integer.Long */
|
||
</style>
|
||
</head>
|
||
<body class="book toc2 toc-left">
|
||
<div id="header">
|
||
<h1>Wombat’s Book of Nix</h1>
|
||
<div class="details">
|
||
<span id="author" class="author">{authors}, {build_date}</span><br>
|
||
</div>
|
||
<div id="toc" class="toc2">
|
||
<div id="toctitle">Table of Contents</div>
|
||
<ul class="sectlevel1">
|
||
<li><a href="#_introduction">1. Introduction</a>
|
||
<ul class="sectlevel2">
|
||
<li><a href="#_why_nix">1.1. Why Nix?</a></li>
|
||
<li><a href="#_why_flakes">1.2. Why <em>flakes</em>?</a></li>
|
||
<li><a href="#_prerequisites">1.3. Prerequisites</a></li>
|
||
<li><a href="#_tip_pay_attention_to_those_hyphens">1.4. Tip: Pay attention to those hyphens</a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a href="#_hello_flake">2. Hello, flake!</a></li>
|
||
<li><a href="#_the_hello_flake_repo">3. The hello-flake repo</a></li>
|
||
<li><a href="#_flake_structure">4. Flake structure</a>
|
||
<ul class="sectlevel2">
|
||
<li><a href="#_inputs">4.1. Inputs</a></li>
|
||
<li><a href="#_outputs">4.2. Outputs</a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a href="#_a_generic_flake">5. A generic flake</a></li>
|
||
<li><a href="#_another_look_at_hello_flake">6. Another look at hello-flake</a></li>
|
||
<li><a href="#_modifying_the_flake">7. Modifying the flake</a>
|
||
<ul class="sectlevel2">
|
||
<li><a href="#_development_workflows">7.1. Development workflows</a></li>
|
||
<li><a href="#_this_all_seems_like_a_hassle">7.2. This all seems like a hassle!</a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a href="#_a_new_flake_from_scratch_python">8. A new flake from scratch (Python)</a></li>
|
||
<li><a href="#_nix_shell_recipes">9. Nix shell recipes</a>
|
||
<ul class="sectlevel2">
|
||
<li><a href="#_shell_with_access_to_a_package_from_the_nixpkgsnixos_repo">9.1. Shell with access to a package from the Nixpkgs/NixOS repo</a></li>
|
||
<li><a href="#_shell_with_access_to_a_package_defined_in_a_remote_git_repo">9.2. Shell with access to a package defined in a remote git repo</a></li>
|
||
<li><a href="#_shell_with_access_to_a_flake">9.3. Shell with access to a flake</a></li>
|
||
<li><a href="#_shell_with_access_to_a_specific_revision_of_a_flake">9.4. Shell with access to a specific revision of a flake</a></li>
|
||
<li><a href="#_shell_with_access_to_a_haskell_package_on_your_local_computer">9.5. Shell with access to a Haskell package on your local computer</a></li>
|
||
<li><a href="#_shell_with_access_to_a_haskell_package_on_your_local_computer_with_interdependencies">9.6. Shell with access to a Haskell package on your local computer, with interdependencies</a></li>
|
||
<li><a href="#_shell_with_an_environment_variable_set">9.7. Shell with an environment variable set</a></li>
|
||
</ul>
|
||
</li>
|
||
<li><a href="#_nix_shell_shebangs">10. Nix-shell shebangs</a>
|
||
<ul class="sectlevel2">
|
||
<li><a href="#_a_bash_script_depending_on_a_package_in_the_nixpkgs_repo">10.1. A Bash script depending on a package in the nixpkgs repo.</a></li>
|
||
<li><a href="#_a_python_script_depending_on_a_package_in_the_nixpkgs_repo">10.2. A Python script depending on a package in the nixpkgs repo.</a></li>
|
||
</ul>
|
||
</li>
|
||
</ul>
|
||
</div>
|
||
</div>
|
||
<div id="content">
|
||
<div class="sect1">
|
||
<h2 id="_introduction">1. Introduction</h2>
|
||
<div class="sectionbody">
|
||
<div class="sect2">
|
||
<h3 id="_why_nix">1.1. Why Nix?</h3>
|
||
<div class="paragraph">
|
||
<p>If you’ve opened this PDF, you already have your own motivation for
|
||
learning Nix. Here’s how it helps me. As a researcher, I tend to work on
|
||
a series of short-term projects, mostly demos and prototypes. For each
|
||
one, I typically develop some software using a compiler, often with some
|
||
open source libraries. Often I use other tools to analyse data or
|
||
generate documentation, for example.</p>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>Problems would arise when handing off the project to colleagues; they
|
||
would report errors when trying to build or run the project. Belatedly I
|
||
would realise that my code relies on a library that they need to
|
||
install. Or perhaps they had installed the library, but the version
|
||
they’re using is incompatible.</p>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>Using containers helped with the problem. However, I didn’t want to
|
||
<em>develop</em> in a container. I did all my development in my nice, familiar,
|
||
environment with my custom aliases and shell prompt. and <em>then</em> I
|
||
containerised the software. This added step was annoying for me, and if
|
||
my colleague wanted to do some additional development, they would
|
||
probably extract all of the source code from the container first anyway.
|
||
Containers are great, but this isn’t the ideal use case for them.</p>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>Nix allows me to work in my custom environment, but forces me to specify
|
||
any dependencies. It automatically tracks the version of each dependency
|
||
so that it can replicate the environment wherever and whenever it’s
|
||
needed.</p>
|
||
</div>
|
||
</div>
|
||
<div class="sect2">
|
||
<h3 id="_why_flakes">1.2. Why <em>flakes</em>?</h3>
|
||
<div class="paragraph">
|
||
<p>Flakes are labeled as an experimental feature, so it might seem safer to
|
||
avoid them. However, they have been in use for years, and there is
|
||
widespread adoption, so the aren’t going away any time soon. Flakes are
|
||
easier to understand, and offer more features than the traditional Nix
|
||
approach. After weighing the pros and cons, I feel it’s better to learn
|
||
and use flakes; and this seems to be the general consensus.</p>
|
||
</div>
|
||
</div>
|
||
<div class="sect2">
|
||
<h3 id="_prerequisites">1.3. Prerequisites</h3>
|
||
<div class="paragraph">
|
||
<p>To follow along with this tutorial, you will need access to a computer
|
||
or (virtual machine) with Nix installed and <em>flakes enabled</em>.</p>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>I recommend the installer from
|
||
<a href="https://zero-to-nix.com/start/install">zero-to-nix.com</a>. This installer
|
||
automatically enables flakes.</p>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>More documentation (and another installer) available at
|
||
<a href="https://nixos.org/">nixos.org</a>.</p>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>To enable flakes on an existing installation, see the instructions in
|
||
the <a href="https://nixos.wiki/wiki/Flakes">NixOS wiki</a>.</p>
|
||
</div>
|
||
</div>
|
||
<div class="sect2">
|
||
<h3 id="_tip_pay_attention_to_those_hyphens">1.4. Tip: Pay attention to those hyphens</h3>
|
||
<div class="paragraph">
|
||
<p>There are hyphenated and unhyphenated versions of many Nix commands. For
|
||
example, <code>nix-shell</code> and <code>nix shell</code> are two different commands.</p>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>Generally speaking, the unhyphenated versions are for working directly
|
||
with flakes, while the hyphenated versions are for everything else.</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="sect1">
|
||
<h2 id="_hello_flake">2. Hello, flake!</h2>
|
||
<div class="sectionbody">
|
||
<div class="paragraph">
|
||
<p>Before learning to write Nix flakes, let’s learn how to use them. I’ve
|
||
created a simple example of a flake in this git
|
||
<a href="https://codeberg.org/mhwombat/hello-flake">repository</a>. To run this
|
||
flake, you don’t need to install anything; simply run the command below.
|
||
The first time you use a flake, Nix has to fetch and build it, which
|
||
may take time. Subsequent invocations should be instantaneous.</p>
|
||
</div>
|
||
<div class="literalblock">
|
||
<div class="content">
|
||
<pre class="nowrap">$ nix run "git+https://codeberg.org/mhwombat/hello-flake"
|
||
Hello from your flake!</pre>
|
||
</div>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>That’s a lot to type every time we want to use this package. Instead, we
|
||
can enter a shell with the package available to us, using the
|
||
<code>nix shell</code> command.</p>
|
||
</div>
|
||
<div class="literalblock">
|
||
<div class="content">
|
||
<pre class="nowrap">$ nix shell "git+https://codeberg.org/mhwombat/hello-flake"</pre>
|
||
</div>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>In this shell, the command is on our <code>$PATH</code>, so we can execute the
|
||
command by name.</p>
|
||
</div>
|
||
<div class="literalblock">
|
||
<div class="content">
|
||
<pre class="nowrap">$ hello-flake
|
||
Hello from your flake!</pre>
|
||
</div>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>Nix didn’t <em>install</em> the package; it merely built and placed it in a
|
||
directory called the ``Nix store''. Thus we can have multiple versions
|
||
of a package without worrying about conflicts. We can find out the
|
||
location of the executable, if we’re curious.</p>
|
||
</div>
|
||
<div class="literalblock">
|
||
<div class="content">
|
||
<pre class="nowrap">$ which hello-flake
|
||
/nix/store/qskl8ajlgnl654fhgsmv74yv8x9r3kzg-hello-flake/bin/hello-flake</pre>
|
||
</div>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>Once we exit that shell, the <code>hello-flake</code> command is no longer
|
||
directly available.</p>
|
||
</div>
|
||
<div class="literalblock">
|
||
<div class="content">
|
||
<pre class="nowrap">$ exit
|
||
$ hello-flake
|
||
sh: line 3: hello-flake: command not found</pre>
|
||
</div>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>However, we can still run the command using the store path we found
|
||
earlier. That’s not particularly convenient, but it does demonstrate
|
||
that the package remains in the store for future use.</p>
|
||
</div>
|
||
<div class="literalblock">
|
||
<div class="content">
|
||
<pre class="nowrap">/nix/store/0xbn2hi6h1m5h4kc02vwffs2cydrbc0r-hello-flake/bin/hello-flake</pre>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="sect1">
|
||
<h2 id="_the_hello_flake_repo">3. The hello-flake repo</h2>
|
||
<div class="sectionbody">
|
||
<div class="paragraph">
|
||
<p>Let’s clone the repository and see how the flake is defined.</p>
|
||
</div>
|
||
<div class="literalblock">
|
||
<div class="content">
|
||
<pre class="nowrap">$ cd ~/tutorial-practice
|
||
$ git clone https://codeberg.org/mhwombat/hello-flake
|
||
$ cd hello-flake
|
||
$ ls</pre>
|
||
</div>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>This is a simple repo with just a few files. Like most git repos, it
|
||
includes <code>LICENSE</code>, which contains the software license, and <code>README.md</code>
|
||
which provides information about the repo.</p>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>The <code>hello-flake</code> file is the command we were executing earlier. This
|
||
particular executable is just a shell script, so we can view it. It’s an
|
||
extremly simple script with just two lines.</p>
|
||
</div>
|
||
<div class="literalblock">
|
||
<div class="content">
|
||
<pre class="nowrap">$ cat hello-flake</pre>
|
||
</div>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>Now that we have a copy of the repo, we can execute this script
|
||
directly.</p>
|
||
</div>
|
||
<div class="literalblock">
|
||
<div class="content">
|
||
<pre class="nowrap">$ ./hello-flake</pre>
|
||
</div>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>Not terribly exciting, I know. But starting with such a simple package
|
||
makes it easier to focus on the flake system without getting bogged down
|
||
in the details. We’ll make this script a little more interesting later.</p>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>Let’s look at another file. The file that defines how to package a flake
|
||
is always called <code>flake.nix</code>.</p>
|
||
</div>
|
||
<div class="literalblock">
|
||
<div class="content">
|
||
<pre class="nowrap">$ cat flake.nix</pre>
|
||
</div>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>If this is your first time seeing a flake definition, it probably looks
|
||
intimidating. Flakes are written in a functional language called
|
||
Nix<sup class="footnote">[<a id="_footnoteref_1" class="footnote" href="#_footnotedef_1" title="View footnote.">1</a>]</sup>. Yes,
|
||
``Nix'' is the name of both the package manager and the language it
|
||
uses. We’ll look at this in more detail shortly. For now, I’d like to
|
||
focus on the inputs section.</p>
|
||
</div>
|
||
<div class="literalblock">
|
||
<div class="content">
|
||
<pre class="nowrap">inputs = {
|
||
nixpkgs.url = "github:NixOS/nixpkgs";
|
||
flake-utils.url = "github:numtide/flake-utils";
|
||
};</pre>
|
||
</div>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>There are just two entries, one for <code>nixpkgs</code> and one for <code>flake-utils</code>.
|
||
The first one, <code>nixpkgs</code> refers to the collection of standard software
|
||
packages that can be installed with the Nix package manager. The second,
|
||
<code>flake-utils</code>, is a collection of utilities that simplify writing
|
||
flakes. The important thing to note is that the <code>hello-flake</code> package
|
||
<em>depends</em> on <code>nixpkgs</code> and <code>flake-utils</code>.</p>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>Finally, let’s look at <code>flake.lock</code>, or rather, just part of it.</p>
|
||
</div>
|
||
<div class="literalblock">
|
||
<div class="content">
|
||
<pre class="nowrap">$ head -n 40 flake.lock</pre>
|
||
</div>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>If <code>flake.nix</code> seemed intimidating, then this file looks like an
|
||
invocation for Cthulhu. The good news is that this file is automatically
|
||
generated; you never need to write it. It contains information about all
|
||
of the dependencies for the flake, including where they came from, the
|
||
exact version/revision, and hash. This lockfile <em>uniquely</em> specifies all
|
||
flake dependencies, (e.g., version number, branch, revision, hash), so
|
||
that <em>anyone, anywhere, any time, can re-create the exact same
|
||
environment that the original developer used.</em></p>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>No more complaints of ``but it works on my machine!''. That is the
|
||
benefit of using flakes.</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="sect1">
|
||
<h2 id="_flake_structure">4. Flake structure</h2>
|
||
<div class="sectionbody">
|
||
<div class="paragraph">
|
||
<p>The basic structure of a flake is shown below.</p>
|
||
</div>
|
||
<div class="literalblock">
|
||
<div class="content">
|
||
<pre class="nowrap">{
|
||
description = ... # package description
|
||
inputs = ... # dependencies
|
||
outputs = ... # what the flake produces
|
||
nixConfig = ... # advanced configuration options
|
||
}</pre>
|
||
</div>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>The <code>description</code> part is self-explanatory; it’s just a string. You
|
||
probably won’t need <code>nixConfig</code> unless you’re doing something fancy. I’m
|
||
going to focus on what goes into the <code>inputs</code> and <code>outputs</code> sections,
|
||
and highlight some of the things I found confusing.</p>
|
||
</div>
|
||
<div class="sect2">
|
||
<h3 id="_inputs">4.1. Inputs</h3>
|
||
<div class="paragraph">
|
||
<p>This section specifies the dependencies of a flake. It’s an <em>attribute
|
||
set</em>; it maps keys to values.</p>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>To ensure that a build is reproducible, the build step runs in a <em>pure</em>
|
||
environment with no network access. Therefore, any external dependencies
|
||
must be specified in the ``inputs'' section so they can be fetched in
|
||
advance (before we enter the pure environment).</p>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>Each entry in this section maps an input name to a <em>flake reference</em>.
|
||
This commonly takes the following form.</p>
|
||
</div>
|
||
<div class="literalblock">
|
||
<div class="content">
|
||
<pre class="nowrap">NAME.url = URL-LIKE-EXPRESSION</pre>
|
||
</div>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>As a first example, all (almost all?) flakes depend on ``nixpkgs'',
|
||
which is a large Git repository of programs and libraries that are
|
||
pre-packaged for Nix. We can write that as</p>
|
||
</div>
|
||
<div class="literalblock">
|
||
<div class="content">
|
||
<pre class="nowrap">nixpkgs.url = "github:NixOS/nixpkgs/nixos-VERSION";</pre>
|
||
</div>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>where <code>NN.MM</code> is replaced with the version number that you used to build
|
||
the package, e.g. <code>22.11</code>. Information about the latest nixpkgs releases
|
||
is available at <a href="https://status.nixos.org/" class="bare">https://status.nixos.org/</a>. You can also write the entry
|
||
without the version number</p>
|
||
</div>
|
||
<div class="literalblock">
|
||
<div class="content">
|
||
<pre class="nowrap">nixpkgs.url = "github:NixOS/nixpkgs/nixos-VERSION";</pre>
|
||
</div>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>or more simply,</p>
|
||
</div>
|
||
<div class="literalblock">
|
||
<div class="content">
|
||
<pre class="nowrap">nixpkgs.url = "nixpkgs";</pre>
|
||
</div>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>You might be concerned that omitting the version number would make the
|
||
build non-reproducible. If someone else builds the flake, could they end
|
||
up with a different version of nixpkgs? No! remember that the lockfile
|
||
(<code>flake.lock</code>) <em>uniquely</em> specifies all flake inputs.</p>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>Git and Mercurial repositories are the most common type of flake
|
||
reference, as in the examples below.</p>
|
||
</div>
|
||
<div class="dlist">
|
||
<dl>
|
||
<dt class="hdlist1">A Git repository</dt>
|
||
<dd>
|
||
<p><code>git+https://github.com/NixOS/patchelf</code></p>
|
||
</dd>
|
||
<dt class="hdlist1">A specific branch of a Git repository</dt>
|
||
<dd>
|
||
<p><code>git+https://github.com/NixOS/patchelf?ref=master</code></p>
|
||
</dd>
|
||
<dt class="hdlist1">A specific revision of a Git repository</dt>
|
||
<dd>
|
||
<p><br>
|
||
<code>git+https://github.com/NixOS/patchelf?ref=master&rev=f34751b88bd07d7f44f5cd3200fb4122bf916c7e</code></p>
|
||
</dd>
|
||
<dt class="hdlist1">A tarball</dt>
|
||
<dd>
|
||
<p><code><a href="https://github.com/NixOS/patchelf/archive/master.tar.gz" class="bare">https://github.com/NixOS/patchelf/archive/master.tar.gz</a></code></p>
|
||
</dd>
|
||
</dl>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>You can find more examples of flake references in the
|
||
<a href="https://nixos.org/manual/nix/stable/command-ref/new-cli/nix3-flake.html#examples">Nix
|
||
Reference Manual</a>.</p>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>Although you probably won’t need to use it, there is another syntax for
|
||
flake references that you might encounter. This example</p>
|
||
</div>
|
||
<div class="literalblock">
|
||
<div class="content">
|
||
<pre class="nowrap">inputs.import-cargo = {
|
||
type = "github";
|
||
owner = "edolstra";
|
||
repo = "import-cargo";
|
||
};</pre>
|
||
</div>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>is equivalent to</p>
|
||
</div>
|
||
<div class="literalblock">
|
||
<div class="content">
|
||
<pre class="nowrap">inputs.import-cargo.url = "github:edolstra/import-cargo";</pre>
|
||
</div>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>Each of the <code>inputs</code> is fetched, evaluated and passed to the <code>outputs</code>
|
||
function as a set of attributes with the same name as the corresponding
|
||
input.</p>
|
||
</div>
|
||
</div>
|
||
<div class="sect2">
|
||
<h3 id="_outputs">4.2. Outputs</h3>
|
||
<div class="paragraph">
|
||
<p>This section is a function that essentially returns the recipe for
|
||
building the flake.</p>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>We said above that <code>inputs</code> are passed to the <code>outputs</code>, so we need to
|
||
list them as parameters. This example references the <code>import-cargo</code>
|
||
dependency defined in the previous example.</p>
|
||
</div>
|
||
<div class="literalblock">
|
||
<div class="content">
|
||
<pre class="nowrap">outputs = { self, nixpkgs, import-cargo }: {
|
||
... outputs ...
|
||
};</pre>
|
||
</div>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>So what actually goes in this section (where I wrote <code>…​outputs…​</code>)?
|
||
That depends on the programming languages your software is written in,
|
||
the build system you use, and more. There are Nix functions and tools
|
||
that can simplify much of this, and new, easier-to-use ones are released
|
||
regularly. We’ll look at some of these in the next section.</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="sect1">
|
||
<h2 id="_a_generic_flake">5. A generic flake</h2>
|
||
<div class="sectionbody">
|
||
<div class="paragraph">
|
||
<p>The previous section presented a very high-level view of flakes,
|
||
focusing on the basic structure. In this section, we will add a bit more
|
||
detail.</p>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>Flakes are written in the Nix programming language, which is a
|
||
functional language. As with most programming languages, there are many
|
||
ways to achieve the same result. Below is an example you can follow when
|
||
writing your own flakes. I’ll explain the example in some detail.</p>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p><span class="image"><img src="template.png" alt="image"></span></p>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>We discussed how to specify flake inputs ❶ in the previous section, so
|
||
this part of the flake should be familiar. Remember also that any
|
||
dependencies in the input section should also be listed at the beginning
|
||
of the outputs section ❷.</p>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>Now it’s time to look at the content of the output section. If we want
|
||
the package to be available for multiple systems (e.g.,
|
||
<code>x86_64-linux'', </code>aarch64-linux'', <code>x86_64-darwin'', and
|
||
</code>aarch64-darwin''), we need to define the output for each of those
|
||
systems. Often the definitions are identical, apart from the name of the
|
||
system. The eachDefaultSystem function ❸ provided by flake-utils allows
|
||
us to write a single definition using a variable for the system name.
|
||
The function then iterates over all default systems to generate the
|
||
outputs for each one.</p>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>The <code>devShells</code> variable specifies the environment that should be
|
||
available when doing development on the package. If you don’t need a
|
||
special development environment, you can omit this section. At ❹ you
|
||
would list any tools (e.g., compilers and language-specific build tools)
|
||
you want to have available in a development shell. If the compiler needs
|
||
access to language-specific packages, there are Nix functions to assist
|
||
with that. These functions are very language-specific, and not always
|
||
well-documented. We will see examples for some languages later in the
|
||
tutorial. In general, I recommend that you do a web search for ``nix
|
||
language'', and try to find resources that were written or updated
|
||
recently.</p>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>The <code>packages</code> variable defines the packages that this flake provides.
|
||
The package definition ❺ depends on the programming languages your
|
||
software is written in, the build system you use, and more. There are
|
||
Nix functions and tools that can simplify much of this, and new,
|
||
easier-to-use ones are released regularly. Again, I recommend that you
|
||
do a web search for ``nix language'', and try to find resources that
|
||
were written or updated recently.</p>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>The <code>apps</code> variable identifies any applications provided by the flake.
|
||
In particular, it identifies the default executable ❻ that <code>nix run</code>
|
||
will run if you don’t specify an app.</p>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>The list below contains are a few functions that are commonly used in
|
||
this section.</p>
|
||
</div>
|
||
<div class="dlist">
|
||
<dl>
|
||
<dt class="hdlist1">General-purpose</dt>
|
||
<dd>
|
||
<p>The standard environment provides <code>mkDerivation</code>, which is especially
|
||
useful for the typical <code>./configure; make; make install</code> scenario.
|
||
It’s customisable.</p>
|
||
</dd>
|
||
<dt class="hdlist1">Python</dt>
|
||
<dd>
|
||
<p><code>buildPythonApplication</code>, <code>buildPythonPackage</code>.</p>
|
||
</dd>
|
||
<dt class="hdlist1">Haskell</dt>
|
||
<dd>
|
||
<p><code>mkDerivation</code> (Haskell version, which is a wrapper around the
|
||
standard environment version), <code>developPackage</code>, <code>callCabal2Nix</code>.</p>
|
||
</dd>
|
||
</dl>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="sect1">
|
||
<h2 id="_another_look_at_hello_flake">6. Another look at hello-flake</h2>
|
||
<div class="sectionbody">
|
||
<div class="paragraph">
|
||
<p>Now that we have a better understanding of the structure of <code>flake.nix</code>,
|
||
let’s have a look at the one we saw earlier, in the <code>hello-flake</code> repo.
|
||
If you compare this flake definition to the colour-coded template
|
||
presented in the previous section, most of it should look familiar.</p>
|
||
</div>
|
||
<div class="literalblock">
|
||
<div class="content">
|
||
<pre class="nowrap">{
|
||
description = "a very simple and friendly flake";
|
||
|
||
inputs = {
|
||
nixpkgs.url = "github:NixOS/nixpkgs";
|
||
flake-utils.url = "github:numtide/flake-utils";
|
||
};
|
||
|
||
outputs = { self, nixpkgs, flake-utils }:
|
||
flake-utils.lib.eachDefaultSystem (system:
|
||
let
|
||
pkgs = import nixpkgs { inherit system; };
|
||
in
|
||
{
|
||
packages = rec {
|
||
hello =
|
||
. . .
|
||
SOME UNFAMILIAR STUFF
|
||
. . .
|
||
};
|
||
default = hello;
|
||
};
|
||
|
||
apps = rec {
|
||
hello = flake-utils.lib.mkApp { drv = self.packages.${system}.hello; };
|
||
default = hello;
|
||
};
|
||
}
|
||
);
|
||
}</pre>
|
||
</div>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>This <code>flake.nix</code> doesn’t have a <code>devShells</code> section, because development
|
||
on the current version doesn’t require anything beyond the ``bare
|
||
bones'' linux commands. Later we will add a feature that requires
|
||
additional development tools.</p>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>Now let’s look at the section I labeled ```SOME UNFAMILIAR STUFF’' and
|
||
see what it does.</p>
|
||
</div>
|
||
<div class="literalblock">
|
||
<div class="content">
|
||
<pre class="nowrap"> packages = rec {
|
||
hello = pkgs.stdenv.mkDerivation rec { # See (1) in text
|
||
name = "hello-flake";
|
||
|
||
src = ./.; # See (2) in text
|
||
|
||
unpackPhase = "true";
|
||
|
||
buildPhase = ":";
|
||
|
||
installPhase =
|
||
''
|
||
mkdir -p $out/bin # See (3) in text
|
||
cp $src/hello-flake $out/bin/hello-flake # See (4) in text
|
||
chmod +x $out/bin/hello-flake # See (5) in text
|
||
'';
|
||
};</pre>
|
||
</div>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>This flake uses <code>mkDerivation</code> (1) which is a very useful
|
||
general-purpose package builder provided by the Nix standard
|
||
environment. It’s especially useful for the typical
|
||
<code>./configure; make; make install</code> scenario, but for this flake we don’t
|
||
even need that.</p>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>The <code>name</code> variable is the name of the flake, as it would appear in a
|
||
package listing if we were to add it to Nixpkgs or another package
|
||
collection. The <code>src</code> variable (2) supplies the location of the source
|
||
files, relative to <code>flake.nix</code>. When a flake is accessed for the first
|
||
time, the repository contents are fetched in the form of a tarball. The
|
||
<code>unpackPhase</code> variable indicates that we do want the tarball to be
|
||
unpacked.</p>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>The <code>buildPhase</code> variable is a sequence of Linux commands to build the
|
||
package. Typically, building a package requires compiling the source
|
||
code. However, that’s not required for a simple shell script. So
|
||
<code>buildPhase</code> consists of a single command, <code>:</code>, which is a no-op or ``do
|
||
nothing'' command.</p>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>The <code>installPhase</code> variable is a sequence of Linux commands that will do
|
||
the actual installation. In this case, we create a directory (3) for the
|
||
installation, copy the <code>hello-flake</code> script (4) there, and make the
|
||
script executable (5). The environment variable <code>$src</code> refers to the
|
||
source directory, which we specified earlier (2).</p>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>Earlier we said that the build step runs in a pure environment to ensure
|
||
that builds are reproducible. This means no Internet access; indeed no
|
||
access to any files outside the build directory. During the build and
|
||
install phases, the only commands available are those provided by the
|
||
Nix standard environment and the external dependencies identified in the
|
||
<code>inputs</code> section of the flake.</p>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>I’ve mentioned the Nix standard environment before, but I didn’t explain
|
||
what it is. The standard environment, or <code>stdenv</code>, refers to the
|
||
functionality that is available during the build and install phases of a
|
||
Nix package (or flake). It includes the commands listed
|
||
below<sup class="footnote">[<a id="_footnoteref_2" class="footnote" href="#_footnotedef_2" title="View footnote.">2</a>]</sup>.</p>
|
||
</div>
|
||
<div class="ulist">
|
||
<ul>
|
||
<li>
|
||
<p>The GNU C Compiler, configured with C and C++ support.</p>
|
||
</li>
|
||
<li>
|
||
<p>GNU coreutils (contains a few dozen standard Unix commands).</p>
|
||
</li>
|
||
<li>
|
||
<p>GNU findutils (contains find).</p>
|
||
</li>
|
||
<li>
|
||
<p>GNU diffutils (contains diff, cmp).</p>
|
||
</li>
|
||
<li>
|
||
<p>GNU sed.</p>
|
||
</li>
|
||
<li>
|
||
<p>GNU grep.</p>
|
||
</li>
|
||
<li>
|
||
<p>GNU awk.</p>
|
||
</li>
|
||
<li>
|
||
<p>GNU tar.</p>
|
||
</li>
|
||
<li>
|
||
<p>gzip, bzip2 and xz.</p>
|
||
</li>
|
||
<li>
|
||
<p>GNU Make.</p>
|
||
</li>
|
||
<li>
|
||
<p>Bash.</p>
|
||
</li>
|
||
<li>
|
||
<p>The patch command.</p>
|
||
</li>
|
||
<li>
|
||
<p>On Linux, stdenv also includes the patchelf utility.</p>
|
||
</li>
|
||
</ul>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>Only a few environment variables are available. The most interesting
|
||
ones are listed below.</p>
|
||
</div>
|
||
<div class="ulist">
|
||
<ul>
|
||
<li>
|
||
<p><code>$name</code> is the package name.</p>
|
||
</li>
|
||
<li>
|
||
<p><code>$src</code> refers to the source directory.</p>
|
||
</li>
|
||
<li>
|
||
<p><code>$out</code> is the path to the location in the Nix store where the package
|
||
will be added.</p>
|
||
</li>
|
||
<li>
|
||
<p><code>$system</code> is the system that the package is being built for.</p>
|
||
</li>
|
||
<li>
|
||
<p><code>$PWD</code> and <code>$TMP</code> both point to a temporary build directories</p>
|
||
</li>
|
||
<li>
|
||
<p><code>$HOME</code> and <code>$PATH</code> point to nonexistent directories, so the build
|
||
cannot rely on them.</p>
|
||
</li>
|
||
</ul>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="sect1">
|
||
<h2 id="_modifying_the_flake">7. Modifying the flake</h2>
|
||
<div class="sectionbody">
|
||
<div class="paragraph">
|
||
<p>Let’s make a simple modification to the script. This will give you an
|
||
opportunity to check your understanding of flakes.</p>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>The first step is to enter a development shell.</p>
|
||
</div>
|
||
<div class="literalblock">
|
||
<div class="content">
|
||
<pre class="nowrap">$ cd ~/tutorial-practice/hello-flake
|
||
bash: line 10: cd: /home/amy/tutorial-practice/hello-flake: No such file or directory
|
||
$ nix develop
|
||
path '/home/amy/codeberg/nix-book/source' does not contain a 'flake.nix', searching up
|
||
error: path '/home/amy/codeberg/nix-book' is not part of a flake (neither it nor its parent directories contain a 'flake.nix' file)</pre>
|
||
</div>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>The <code>flake.nix</code> file specifies all of the tools that are needed during
|
||
development of the package. The <code>nix develop</code> command puts us in a shell
|
||
with those tools. As it turns out, we didn’t need any extra tools
|
||
(beyond the standard environment) for development yet, but that’s
|
||
usually not the case. Also, we will soon need another tool.</p>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>A development environment only allows you to <em>develop</em> the package.
|
||
Don’t expect the package <em>outputs</em> (e.g. executables) to be available
|
||
until you build them. However, our script doesn’t need to be compiled,
|
||
so can’t we just run it?</p>
|
||
</div>
|
||
<div class="literalblock">
|
||
<div class="content">
|
||
<pre class="nowrap">$ hello-flake
|
||
bash: line 28: hello-flake: command not found</pre>
|
||
</div>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>That worked before; why isn’t it working now? Earlier we used
|
||
<code>nix shell</code> to enter a <em>runtime</em> environment where <code>hello-flake</code> was
|
||
available and on the <code>$PATH</code>. This time we entered a <em>development</em>
|
||
environment using the <code>nix develop</code> command. Since the flake hasn’t been
|
||
built yet, the executable won’t be on the <code>$PATH</code>. We can, however, run
|
||
it by specifying the path to the script.</p>
|
||
</div>
|
||
<div class="literalblock">
|
||
<div class="content">
|
||
<pre class="nowrap">$ ./hello-flake
|
||
bash: line 40: ./hello-flake: No such file or directory</pre>
|
||
</div>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>We can also build the flake using the <code>nix build</code> command, which places
|
||
the build outputs in a directory called <code>result</code>.</p>
|
||
</div>
|
||
<div class="literalblock">
|
||
<div class="content">
|
||
<pre class="nowrap">$ nix build
|
||
path '/home/amy/codeberg/nix-book/source' does not contain a 'flake.nix', searching up
|
||
error: path '/home/amy/codeberg/nix-book' is not part of a flake (neither it nor its parent directories contain a 'flake.nix' file)
|
||
$ result/bin/hello-flake
|
||
bash: line 50: result/bin/hello-flake: No such file or directory</pre>
|
||
</div>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>Rather than typing the full path to the executable, it’s more convenient
|
||
to use <code>nix run</code>.</p>
|
||
</div>
|
||
<div class="literalblock">
|
||
<div class="content">
|
||
<pre class="nowrap">$ nix run
|
||
path '/home/amy/codeberg/nix-book/source' does not contain a 'flake.nix', searching up
|
||
error: path '/home/amy/codeberg/nix-book' is not part of a flake (neither it nor its parent directories contain a 'flake.nix' file)</pre>
|
||
</div>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>Here’s a summary of the more common Nix commands.</p>
|
||
</div>
|
||
<table class="tableblock frame-all grid-all stretch">
|
||
<colgroup>
|
||
<col style="width: 17%;">
|
||
<col style="width: 83%;">
|
||
</colgroup>
|
||
<thead>
|
||
<tr>
|
||
<th class="tableblock halign-left valign-top">command</th>
|
||
<th class="tableblock halign-left valign-top">Action</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>nix develop</code></p></td>
|
||
<td class="tableblock halign-left valign-top"><p class="tableblock">Enters a <em>development</em> shell with all the required
|
||
development tools (e.g. compilers and linkers) available (as specified
|
||
by <code>flake.nix</code>).</p></td>
|
||
</tr>
|
||
<tr>
|
||
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>nix shell</code></p></td>
|
||
<td class="tableblock halign-left valign-top"><p class="tableblock">Enters a <em>runtime</em> shell where the flake’s executables are
|
||
available on the <code>$PATH</code>.</p></td>
|
||
</tr>
|
||
<tr>
|
||
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>nix build</code></p></td>
|
||
<td class="tableblock halign-left valign-top"><p class="tableblock">Builds the flake and puts the output in a directory called
|
||
<code>result</code>.</p></td>
|
||
</tr>
|
||
<tr>
|
||
<td class="tableblock halign-left valign-top"><p class="tableblock"><code>nix run</code></p></td>
|
||
<td class="tableblock halign-left valign-top"><p class="tableblock">Runs the flake’s default executable, rebuilding the package
|
||
first if needed. Specifically, it runs the version in the Nix store, not
|
||
the version in <code>result</code>.</p></td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<div class="paragraph">
|
||
<p>Now we’re ready to make the flake a little more interesting. Instead of
|
||
using the <code>echo</code> command in the script, we can use the Linux <code>cowsay</code>
|
||
command. The <code>sed</code> command below will make the necessary changes.</p>
|
||
</div>
|
||
<div class="literalblock">
|
||
<div class="content">
|
||
<pre class="nowrap">$ sed -i 's/echo/cowsay/' hello-flake
|
||
sed: can't read hello-flake: No such file or directory
|
||
$ cat hello-flake
|
||
cat: hello-flake: No such file or directory</pre>
|
||
</div>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>Let’s test the modified script.</p>
|
||
</div>
|
||
<div class="literalblock">
|
||
<div class="content">
|
||
<pre class="nowrap">$ ./hello-flake
|
||
bash: line 96: ./hello-flake: No such file or directory</pre>
|
||
</div>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>What went wrong? Remember that we are in a <em>development</em> shell. Since
|
||
<code>flake.nix</code> didn’t define the <code>devShells</code> variable, the development
|
||
shell only includes the Nix standard environment. In particular, the
|
||
<code>cowsay</code> command is not available.</p>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>To fix the problem, we can modify <code>flake.nix</code>. We don’t need to add
|
||
<code>cowsay</code> to the <code>inputs</code> section because it’s included in <code>nixpkgs</code>,
|
||
which is already an input. However, we do need to indicate that we want
|
||
it available in a develoment shell. Add the following lines before the
|
||
<code>packages = rec {</code> line.</p>
|
||
</div>
|
||
<div class="literalblock">
|
||
<div class="content">
|
||
<pre class="nowrap"> devShells = rec {
|
||
default = pkgs.mkShell {
|
||
packages = [ pkgs.cowsay ];
|
||
};
|
||
};</pre>
|
||
</div>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>Here is a <code>`diff'' showing the changes in `flake.nix</code>.</p>
|
||
</div>
|
||
<div class="literalblock">
|
||
<div class="content">
|
||
<pre class="nowrap">sed: can't read flake.nix: No such file or directory
|
||
$ git diff flake.nix
|
||
fatal: ambiguous argument 'flake.nix': unknown revision or path not in the working tree.
|
||
Use '--' to separate paths from revisions, like this:
|
||
'git <command> [<revision>...] -- [<file>...]'</pre>
|
||
</div>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>We restart the development shell and see that the <code>cowsay</code> command is
|
||
now available and the script works. Because we’ve updated source files
|
||
but haven’t `git commit`ed the new version, we get a warning message
|
||
about it being ``dirty''. It’s just a warning, though; the script runs
|
||
correctly.</p>
|
||
</div>
|
||
<div class="literalblock">
|
||
<div class="content">
|
||
<pre class="nowrap">$ nix develop
|
||
path '/home/amy/codeberg/nix-book/source' does not contain a 'flake.nix', searching up
|
||
error: path '/home/amy/codeberg/nix-book' is not part of a flake (neither it nor its parent directories contain a 'flake.nix' file)
|
||
$ which cowsay # is it available now?
|
||
which: no cowsay in (/home/amy/github/wain-tools:/home/amy/github/bin:/home/amy/github/wain-tools:/home/amy/github/bin:/nix/store/dsfcdjmymlc18wzk0hssycwifg4wznan-bash-interactive-5.1-p16/bin:/nix/store/4ccwc439gkpk91gflh6zsyx12fkfcggx-patchelf-0.15.0/bin:/nix/store/y372k7srz9jm7v9sv2cs84g3crz27z1s-gcc-wrapper-11.3.0/bin:/nix/store/wy4ywjsch9q2hj5lphqjdg9p2kf7w0ls-gcc-11.3.0/bin:/nix/store/fi1521530hy97gwjfdlgiqb4wad2g13d-glibc-2.35-224-bin/bin:/nix/store/l6jgwxkc3jhr029vfzfwzcy28iyckwsj-coreutils-9.1/bin:/nix/store/l5i2w0w446pqsnpdxrlsf3azy5aspfxq-binutils-wrapper-2.39/bin:/nix/store/9n9gjvzci75gp2sh1c4rh626dhizqynl-binutils-2.39/bin:/nix/store/gx0iyarrz7jcnac1yllhgzffi75y4gs8-asciidoctor-2.0.18/bin:/nix/store/bbi6z65bazdzmawxaxr1kvgbn41sv9g0-python3.11-pygments-2.13.0/bin:/nix/store/szz6i32n70m8pxidw5zj294rka4m0yrz-python3.11-docutils-0.19/bin:/nix/store/536aws8kkljng4ggdakk8i1nfdqb4f4x-python3-3.11.3/bin:/nix/store/l6jgwxkc3jhr029vfzfwzcy28iyckwsj-coreutils-9.1/bin:/nix/store/gn1s1s5z19cf0wiir2cd38jckcjc6kn6-findutils-4.9.0/bin:/nix/store/5l382qdhcyyjsyhfqqc0g15rmvs3rs4s-diffutils-3.8/bin:/nix/store/v0hg83sdv4v51c0prmdigry6wdmmpzmp-gnused-4.8/bin:/nix/store/pvb117r7fhwb08717ks21a6y9hlnp63b-gnugrep-3.7/bin:/nix/store/51sgpaxsi4sl46ii9fssvyp7n592if1m-gawk-5.1.1/bin:/nix/store/isycdamy5rsi0vq55fxz0438srx5z0hv-gnutar-1.34/bin:/nix/store/w9pbk66kf43wrpdcijjzc8vf8cymnaxy-gzip-1.12/bin:/nix/store/r84vlmw71jw2piv03n0jdhav2zsdvm6n-bzip2-1.0.8-bin/bin:/nix/store/cgbpv0vvz31qayvbf3bif2iznp46wcgl-gnumake-4.3/bin:/nix/store/kga2r02rmyxl14sg96nxbdhifq3rb8lc-bash-5.1-p16/bin:/nix/store/f1gvh42512gjf3378nmlw2vrx491yndq-patch-2.7.6/bin:/nix/store/y8z2dmbxdapyzpcrhfbdv875l5v7ddpi-xz-5.2.7-bin/bin:/nix/store/cmnq5bja0h0an1k3cyb36blkfci8nxaf-file-5.43/bin:/nix/store/dsfcdjmymlc18wzk0hssycwifg4wznan-bash-interactive-5.1-p16/bin:/nix/store/4ccwc439gkpk91gflh6zsyx12fkfcggx-patchelf-0.15.0/bin:/nix/store/y372k7srz9jm7v9sv2cs84g3crz27z1s-gcc-wrapper-11.3.0/bin:/nix/store/wy4ywjsch9q2hj5lphqjdg9p2kf7w0ls-gcc-11.3.0/bin:/nix/store/fi1521530hy97gwjfdlgiqb4wad2g13d-glibc-2.35-224-bin/bin:/nix/store/l6jgwxkc3jhr029vfzfwzcy28iyckwsj-coreutils-9.1/bin:/nix/store/l5i2w0w446pqsnpdxrlsf3azy5aspfxq-binutils-wrapper-2.39/bin:/nix/store/9n9gjvzci75gp2sh1c4rh626dhizqynl-binutils-2.39/bin:/nix/store/0dip0kcky3jnjsp60xvk1cjzhv4yvj2s-python3.10-pelican-4.8.0/bin:/nix/store/089zdkxl8bbif25dqv3sx4nc9jyz6bh5-python3.10-chardet-5.0.0/bin:/nix/store/rpri9nb8xpwwqakyrqbg8zdslkjs2hd3-python3-3.10.11/bin:/nix/store/01axgc9g662lk663h93z2bp8clyy3hqn-python3.10-docutils-0.19/bin:/nix/store/v20lm65xzlndbw2mbihqj4xiasrvgs9w-python3.10-babel-2.11.0/bin:/nix/store/aba7b45bjy0aawqlblag6k4vg5qf44kh-python3.10-pygments-2.13.0/bin:/nix/store/zvylkc7cnar7majappcww2z826ls3sb7-python3.10-commonmark-0.9.1/bin:/nix/store/hkyfwv0yqhmyv7qvxc4g9w3ia534x391-python3.10-future-0.18.3/bin:/nix/store/5ag6v19kxlfnk6qbrrwy8wiq9m4il310-python3.10-unidecode-1.3.4/bin:/nix/store/w3f9jkinz72jyklfsgwmrfmhm4czsmga-python3.10-markdown-3.4.1/bin:/nix/store/l6jgwxkc3jhr029vfzfwzcy28iyckwsj-coreutils-9.1/bin:/nix/store/gn1s1s5z19cf0wiir2cd38jckcjc6kn6-findutils-4.9.0/bin:/nix/store/5l382qdhcyyjsyhfqqc0g15rmvs3rs4s-diffutils-3.8/bin:/nix/store/v0hg83sdv4v51c0prmdigry6wdmmpzmp-gnused-4.8/bin:/nix/store/pvb117r7fhwb08717ks21a6y9hlnp63b-gnugrep-3.7/bin:/nix/store/51sgpaxsi4sl46ii9fssvyp7n592if1m-gawk-5.1.1/bin:/nix/store/isycdamy5rsi0vq55fxz0438srx5z0hv-gnutar-1.34/bin:/nix/store/w9pbk66kf43wrpdcijjzc8vf8cymnaxy-gzip-1.12/bin:/nix/store/r84vlmw71jw2piv03n0jdhav2zsdvm6n-bzip2-1.0.8-bin/bin:/nix/store/cgbpv0vvz31qayvbf3bif2iznp46wcgl-gnumake-4.3/bin:/nix/store/kga2r02rmyxl14sg96nxbdhifq3rb8lc-bash-5.1-p16/bin:/nix/store/f1gvh42512gjf3378nmlw2vrx491yndq-patch-2.7.6/bin:/nix/store/y8z2dmbxdapyzpcrhfbdv875l5v7ddpi-xz-5.2.7-bin/bin:/nix/store/cmnq5bja0h0an1k3cyb36blkfci8nxaf-file-5.43/bin:/home/amy/github/wain-tools:/home/amy/github/bin:/home/amy/github/wain-tools:/home/amy/github/bin:/home/amy/github/wain-tools:/home/amy/github/bin:/run/wrappers/bin:/home/amy/.nix-profile/bin:/etc/profiles/per-user/amy/bin:/nix/var/nix/profiles/default/bin:/run/current-system/sw/bin)
|
||
$ ./hello-flake
|
||
bash: line 138: ./hello-flake: No such file or directory</pre>
|
||
</div>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>Alternatively, we could use <code>nix run</code>.</p>
|
||
</div>
|
||
<div class="literalblock">
|
||
<div class="content">
|
||
<pre class="nowrap">$ nix run
|
||
path '/home/amy/codeberg/nix-book/source' does not contain a 'flake.nix', searching up
|
||
error: path '/home/amy/codeberg/nix-book' is not part of a flake (neither it nor its parent directories contain a 'flake.nix' file)</pre>
|
||
</div>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>Note, however, that <code>nix run</code> rebuilt the package in the Nix store and
|
||
ran <em>that</em>. It did not alter the copy in the <code>result</code> directory, as
|
||
we’ll see next.</p>
|
||
</div>
|
||
<div class="literalblock">
|
||
<div class="content">
|
||
<pre class="nowrap">$ cat result/bin/hello-flake
|
||
cat: result/bin/hello-flake: No such file or directory</pre>
|
||
</div>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>If we want to update the version in <code>result</code>, we need <code>nix build</code> again.</p>
|
||
</div>
|
||
<div class="literalblock">
|
||
<div class="content">
|
||
<pre class="nowrap">$ nix build
|
||
path '/home/amy/codeberg/nix-book/source' does not contain a 'flake.nix', searching up
|
||
error: path '/home/amy/codeberg/nix-book' is not part of a flake (neither it nor its parent directories contain a 'flake.nix' file)
|
||
$ cat result/bin/hello-flake
|
||
cat: result/bin/hello-flake: No such file or directory</pre>
|
||
</div>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>Let’s <code>git commit</code> the changes and verify that the warning goes away. We
|
||
don’t need to <code>git push</code> the changes until we’re ready to share them.</p>
|
||
</div>
|
||
<div class="literalblock">
|
||
<div class="content">
|
||
<pre class="nowrap">$ git commit hello-flake flake.nix -m 'added bovine feature'
|
||
error: pathspec 'hello-flake' did not match any file(s) known to git
|
||
error: pathspec 'flake.nix' did not match any file(s) known to git
|
||
$ nix run
|
||
path '/home/amy/codeberg/nix-book/source' does not contain a 'flake.nix', searching up
|
||
error: path '/home/amy/codeberg/nix-book' is not part of a flake (neither it nor its parent directories contain a 'flake.nix' file)</pre>
|
||
</div>
|
||
</div>
|
||
<div class="sect2">
|
||
<h3 id="_development_workflows">7.1. Development workflows</h3>
|
||
<div class="paragraph">
|
||
<p>If you’re getting confused about when to use the different commands,
|
||
it’s because there’s more than one way to use Nix. I tend to think of it
|
||
as two different development workflows.</p>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>My usual, <em>high-level workflow</em> is quite simple.</p>
|
||
</div>
|
||
<div class="olist arabic">
|
||
<ol class="arabic">
|
||
<li>
|
||
<p><code>nix run</code> to re-build (if necessary) and run the executable.</p>
|
||
</li>
|
||
<li>
|
||
<p>Fix any problems in <code>flake.nix</code> or the source code.</p>
|
||
</li>
|
||
<li>
|
||
<p>Repeat until the package works properly.</p>
|
||
</li>
|
||
</ol>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>In the high-level workflow, I don’t use a development shell because I
|
||
don’t need to directly invoke development tools such as compilers and
|
||
linkers. Nix invokes them for me according to the output definition in
|
||
<code>flake.nix</code>.</p>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>Occasionally I want to work at a lower level, and invoke compiler,
|
||
linkers, etc. directly. Perhaps want to work on one component without
|
||
rebuilding the entire package. Or perhaps I’m confused by some error
|
||
message, so I want to temporarily bypass Nix and ``talk'' directly to
|
||
the compiler. In this case I temporarily switch to a <em>low-level
|
||
workflow</em>.</p>
|
||
</div>
|
||
<div class="olist arabic">
|
||
<ol class="arabic">
|
||
<li>
|
||
<p><code>nix develop</code> to enter a development shell with any development tools
|
||
I need (e.g. compilers, linkers, documentation generators).</p>
|
||
</li>
|
||
<li>
|
||
<p>Directly invoke tools such as compilers.</p>
|
||
</li>
|
||
<li>
|
||
<p>Fix any problems in <code>flake.nix</code> or the source code.</p>
|
||
</li>
|
||
<li>
|
||
<p>Directly invoke the executable. Note that the location of the
|
||
executable depends on the development tools – It probably isn’t
|
||
<code>result</code>!</p>
|
||
</li>
|
||
<li>
|
||
<p>Repeat until the package works properly.</p>
|
||
</li>
|
||
</ol>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>I generally only use <code>nix build</code> if I just want to build the package but
|
||
not execute anything (perhaps it’s just a library).</p>
|
||
</div>
|
||
</div>
|
||
<div class="sect2">
|
||
<h3 id="_this_all_seems_like_a_hassle">7.2. This all seems like a hassle!</h3>
|
||
<div class="paragraph">
|
||
<p>It is a bit annoying to modify <code>flake.nix</code> and ether rebuild or reload
|
||
the development environment every time you need another tool. However,
|
||
this Nix way of doing things ensures that all of your dependencies, down
|
||
to the exact versions, are captured in <code>flake.lock</code>, and that anyone
|
||
else will be able to reproduce the development environment.</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="sect1">
|
||
<h2 id="_a_new_flake_from_scratch_python">8. A new flake from scratch (Python)</h2>
|
||
<div class="sectionbody">
|
||
<div class="paragraph">
|
||
<p>At last we are ready to create a flake from scratch! Start with an empty
|
||
directory and create a git repository.</p>
|
||
</div>
|
||
<div class="literalblock">
|
||
<div class="content">
|
||
<pre class="nowrap">$ cd ~/tutorial-practice
|
||
bash: line 8: cd: /home/amy/tutorial-practice: No such file or directory
|
||
$ mkdir hello-python
|
||
mkdir: cannot create directory ‘hello-python’: File exists
|
||
$ cd hello-python
|
||
$ git init
|
||
Reinitialized existing Git repository in /home/amy/codeberg/nix-book/source/hello-python/.git/</pre>
|
||
</div>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>Next, we’ll create a simple Python program.</p>
|
||
</div>
|
||
<div class="literalblock">
|
||
<div class="content">
|
||
<pre class="nowrap">$ cat hello.py
|
||
#!/usr/bin/env python
|
||
|
||
def main():
|
||
print("Hello from inside a Python program built with a Nix flake!")
|
||
|
||
if __name__ == "__main__":
|
||
main()</pre>
|
||
</div>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>Before we pachage the program, let’s verify that it runs. We’re going to
|
||
need Python. By now you’ve probably figured out that we can write a
|
||
<code>flake.nix</code> and define a development shell that includes Python. We’ll
|
||
do that shortly, but first I want to show you a handy shortcut. We can
|
||
lauch a <em>temporary</em> shell with any Nix packages we want. This is
|
||
convenient when you just want to try out some new software and you’re
|
||
not sure if you’ll use it again. It’s also convenient when you’re not
|
||
ready to write <code>flake.nix</code> (perhaps you’re not sure what tools and
|
||
packages you need), and you want to experiment a bit first.</p>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>The command to enter a temporary shell is</p>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p><code>nix-shell -p</code> <em>packages</em></p>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>If there are multiple packages, they should be separated by spaces. Note
|
||
that the command used here is <code>nix-shell</code> with a hyphen, not <code>nix shell</code>
|
||
with a space; those are two different commands. In fact there are
|
||
hyphenated and non-hyphenated versions of many Nix commands, and yes,
|
||
it’s confusing. The non-hyphenated commands were introduced when support
|
||
for flakes was added to Nix. I predict that eventually all hyphenated
|
||
commands will be replaced with non-hyphenated versions. Until then, a
|
||
useful rule of thumb is that non-hyphenated commands are for for working
|
||
directly with flakes; hyphenated commands are for everything else.</p>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>Let’s enter a shell with Python so we can test the program.</p>
|
||
</div>
|
||
<div class="literalblock">
|
||
<div class="content">
|
||
<pre class="nowrap">$ nix-shell -p python3
|
||
$ python hello.py
|
||
Hello from inside a Python program built with a Nix flake!</pre>
|
||
</div>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>Next, create a Python script to build the package. We’ll use Python’s
|
||
setuptools, but you can use other build tools. For more information on
|
||
setuptools, see the
|
||
<a href="https://packaging.python.org/en/latest/guides/distributing-packages-using-setuptools/">Python
|
||
Packaging User Guide</a>, especially the section on
|
||
<a href="https://packaging.python.org/en/latest/guides/distributing-packages-using-setuptools/#setup-args">setup
|
||
args</a>.</p>
|
||
</div>
|
||
<div class="literalblock">
|
||
<div class="content">
|
||
<pre class="nowrap">$ cat setup.py
|
||
#!/usr/bin/env python
|
||
|
||
from setuptools import setup
|
||
|
||
setup(
|
||
name='hello-flake-python',
|
||
version='0.1.0',
|
||
py_modules=['hello'],
|
||
entry_points={
|
||
'console_scripts': ['hello-flake-python = hello:main']
|
||
},
|
||
)</pre>
|
||
</div>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>We won’t write <code>flake.nix</code> just yet. First we’ll try building the
|
||
package manually.</p>
|
||
</div>
|
||
<div class="literalblock">
|
||
<div class="content">
|
||
<pre class="nowrap">$ python -m build
|
||
/nix/store/rpri9nb8xpwwqakyrqbg8zdslkjs2hd3-python3-3.10.11/bin/python: No module named build</pre>
|
||
</div>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>The missing module error happens because we don’t have <code>build</code> available
|
||
in the temporary shell. We can fix that by adding <code>`build'' to the
|
||
temporary shell. When you need support for both a language and some of
|
||
its packages, it’s best to use one of the Nix functions that are
|
||
specific to the programming language and build system. For Python, we
|
||
can use the `withPackages</code> function.</p>
|
||
</div>
|
||
<div class="literalblock">
|
||
<div class="content">
|
||
<pre class="nowrap">$ nix-shell -p "python3.withPackages (ps: with ps; [ build ])"</pre>
|
||
</div>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>Note that we’re now inside a temporary shell inside the previous
|
||
temporary shell! To get back to the original shell, we have to <code>exit</code>
|
||
twice. Alternatively, we could have done <code>exit</code> followed by the
|
||
<code>nix-shell</code> command.</p>
|
||
</div>
|
||
<div class="literalblock">
|
||
<div class="content">
|
||
<pre class="nowrap">$ python -m build</pre>
|
||
</div>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>After a lot of output messages, the build succeeds.</p>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>Now we should write <code>flake.nix</code>. We already know how to write most of
|
||
the flake from the examples we did earlier. The two parts that will be
|
||
different are the development shell and the package builder.</p>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>Let’s start with the development shell. It seems logical to write
|
||
something like the following.</p>
|
||
</div>
|
||
<div class="literalblock">
|
||
<div class="content">
|
||
<pre class="nowrap"> devShells = rec {
|
||
default = pkgs.mkShell {
|
||
packages = [ (python.withPackages (ps: with ps; [ build ])) ];
|
||
};
|
||
};</pre>
|
||
</div>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>Note that we need the parentheses to prevent <code>python.withPackages</code> and
|
||
the argument from being processed as two separate tokens. Suppose we
|
||
wanted to work with <code>virtualenv</code> and <code>pip</code> instead of <code>build</code>. We could
|
||
write something like the following.</p>
|
||
</div>
|
||
<div class="literalblock">
|
||
<div class="content">
|
||
<pre class="nowrap"> devShells = rec {
|
||
default = pkgs.mkShell {
|
||
packages = [
|
||
# Python plus helper tools
|
||
(python.withPackages (ps: with ps; [
|
||
virtualenv # Virtualenv
|
||
pip # The pip installer
|
||
]))
|
||
];
|
||
};
|
||
};</pre>
|
||
</div>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>For the package builder, we can use the <code>buildPythonApplication</code>
|
||
function.</p>
|
||
</div>
|
||
<div class="literalblock">
|
||
<div class="content">
|
||
<pre class="nowrap"> packages = rec {
|
||
hello = python.pkgs.buildPythonApplication {
|
||
name = "hello-flake-python";
|
||
buildInputs = with python.pkgs; [ pip ];
|
||
src = ./.;
|
||
};
|
||
default = hello;
|
||
};</pre>
|
||
</div>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>If you put all the pieces together, your <code>flake.nix</code> should look
|
||
something like this.</p>
|
||
</div>
|
||
<div class="literalblock">
|
||
<div class="content">
|
||
<pre class="nowrap">$ cat flake.nix
|
||
{
|
||
# See https://github.com/mhwombat/nix-for-numbskulls/blob/main/flakes.md
|
||
# for a brief overview of what each section in a flake should or can contain.
|
||
|
||
description = "a very simple and friendly flake written in Python";
|
||
|
||
inputs = {
|
||
nixpkgs.url = "github:NixOS/nixpkgs";
|
||
flake-utils.url = "github:numtide/flake-utils";
|
||
};
|
||
|
||
outputs = { self, nixpkgs, flake-utils }:
|
||
flake-utils.lib.eachDefaultSystem (system:
|
||
let
|
||
pkgs = import nixpkgs { inherit system; };
|
||
python = pkgs.python3;
|
||
in
|
||
{
|
||
devShells = rec {
|
||
default = pkgs.mkShell {
|
||
packages = [
|
||
# Python plus helper tools
|
||
(python.withPackages (ps: with ps; [
|
||
virtualenv # Virtualenv
|
||
pip # The pip installer
|
||
]))
|
||
];
|
||
};
|
||
};
|
||
|
||
packages = rec {
|
||
hello = python.pkgs.buildPythonApplication {
|
||
name = "hello-flake-python";
|
||
|
||
buildInputs = with python.pkgs; [ pip ];
|
||
|
||
src = ./.;
|
||
};
|
||
default = hello;
|
||
};
|
||
|
||
apps = rec {
|
||
hello = flake-utils.lib.mkApp { drv = self.packages.${system}.hello; };
|
||
default = hello;
|
||
};
|
||
}
|
||
);
|
||
}</pre>
|
||
</div>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>Let’s try out the new flake.</p>
|
||
</div>
|
||
<div class="literalblock">
|
||
<div class="content">
|
||
<pre class="nowrap">$ nix run
|
||
Hello from inside a Python program built with a Nix flake!</pre>
|
||
</div>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>Why can’t it find <code>flake.nix</code>? Nix flakes only ``see'' files that are
|
||
part of the repository. We need to add all of the important files to the
|
||
repo before building or running the flake.</p>
|
||
</div>
|
||
<div class="literalblock">
|
||
<div class="content">
|
||
<pre class="nowrap">$ git add flake.nix setup.py hello.py
|
||
$ nix run
|
||
Hello from inside a Python program built with a Nix flake!</pre>
|
||
</div>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>We’d like to share this package with others, but first we should do some
|
||
cleanup. When the package was built (automatically by the <code>nix run</code>
|
||
command), it created a <code>flake.lock</code> file. We need to add this to the
|
||
repo, and commit all important files.</p>
|
||
</div>
|
||
<div class="literalblock">
|
||
<div class="content">
|
||
<pre class="nowrap">$ git add flake.lock
|
||
$ git commit -a -m 'initial commit'
|
||
On branch master
|
||
Untracked files:
|
||
(use "git add <file>..." to include in what will be committed)
|
||
dist/
|
||
hello_flake_python.egg-info/
|
||
|
||
nothing added to commit but untracked files present (use "git add" to track)</pre>
|
||
</div>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>You can test that your package is properly configured by going to
|
||
another directory and running it from there.</p>
|
||
</div>
|
||
<div class="literalblock">
|
||
<div class="content">
|
||
<pre class="nowrap">$ cd ~
|
||
$ nix run ~/tutorial-practice/hello-python
|
||
path '/home/amy/tutorial-practice/hello-python' does not contain a 'flake.nix', searching up
|
||
error: getting status of '/home/amy/tutorial-practice/hello-python': No such file or directory</pre>
|
||
</div>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>If you move the project to a public repo, anyone can run it. Recall from
|
||
the beginning of the tutorial that you were able to run <code>hello-flake</code>
|
||
directly from my repo with the following command.</p>
|
||
</div>
|
||
<div class="literalblock">
|
||
<div class="content">
|
||
<pre class="nowrap">nix run "git+https://codeberg.org/mhwombat/hello-flake"</pre>
|
||
</div>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>Modify the URL accordingly and invite someone else to run your new
|
||
Python flake.</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="sect1">
|
||
<h2 id="_nix_shell_recipes">9. Nix shell recipes</h2>
|
||
<div class="sectionbody">
|
||
<div class="sect2">
|
||
<h3 id="_shell_with_access_to_a_package_from_the_nixpkgsnixos_repo">9.1. Shell with access to a package from the Nixpkgs/NixOS repo</h3>
|
||
<div class="paragraph">
|
||
<p>This shell provides access to two packages from nixpkgs: hello and
|
||
cowsay.</p>
|
||
</div>
|
||
<div class="listingblock">
|
||
<div class="content">
|
||
<pre class="pygments highlight nowrap"><code data-lang="nix"><table class="linenotable"><tr><td class="linenos"><div class="linenodiv"><pre><span class="normal">1</span>
|
||
<span class="normal">2</span>
|
||
<span class="normal">3</span>
|
||
<span class="normal">4</span>
|
||
<span class="normal">5</span>
|
||
<span class="normal">6</span>
|
||
<span class="normal">7</span></pre></div></td><td class="code"><pre><span></span><span class="tok-k">with</span> <span class="tok-p">(</span><span class="tok-nb">import</span> <span class="tok-l"><nixpkgs></span> <span class="tok-p">{});</span>
|
||
mkShell <span class="tok-p">{</span>
|
||
<span class="tok-ss">buildInputs =</span> <span class="tok-p">[</span>
|
||
hello
|
||
cowsay
|
||
<span class="tok-p">];</span>
|
||
<span class="tok-p">}</span>
|
||
</pre></td></tr></table></code></pre>
|
||
</div>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>Here’s a demonstration using the shell.</p>
|
||
</div>
|
||
<div class="literalblock">
|
||
<div class="content">
|
||
<pre class="nowrap">$ nix-shell
|
||
error: getting status of '/home/amy/codeberg/nix-book/source/shell-recipes/default.nix': No such file or directory
|
||
$ hello
|
||
bash: line 19: hello: command not found
|
||
Hello, world!
|
||
$ cowsay "moo"
|
||
bash: line 22: cowsay: command not found
|
||
_____
|
||
< moo >
|
||
-----
|
||
\ ^__^
|
||
\ (oo)\_______
|
||
(__)\ )\/\
|
||
||----w |
|
||
|| ||</pre>
|
||
</div>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>The command-line equivalent would be <code>nix-shell -p hello cowsay</code></p>
|
||
</div>
|
||
</div>
|
||
<div class="sect2">
|
||
<h3 id="_shell_with_access_to_a_package_defined_in_a_remote_git_repo">9.2. Shell with access to a package defined in a remote git repo</h3>
|
||
<div class="listingblock">
|
||
<div class="content">
|
||
<pre class="pygments highlight nowrap"><code data-lang="nix"><table class="linenotable"><tr><td class="linenos"><div class="linenodiv"><pre><span class="normal"> 1</span>
|
||
<span class="normal"> 2</span>
|
||
<span class="normal"> 3</span>
|
||
<span class="normal"> 4</span>
|
||
<span class="normal"> 5</span>
|
||
<span class="normal"> 6</span>
|
||
<span class="normal"> 7</span>
|
||
<span class="normal"> 8</span>
|
||
<span class="normal"> 9</span>
|
||
<span class="normal">10</span></pre></div></td><td class="code"><pre><span></span><span class="tok-k">with</span> <span class="tok-p">(</span><span class="tok-nb">import</span> <span class="tok-l"><nixpkgs></span> <span class="tok-p">{});</span>
|
||
<span class="tok-k">let</span>
|
||
<span class="tok-ss">hello =</span> <span class="tok-nb">import</span> <span class="tok-p">(</span><span class="tok-nb">builtins</span><span class="tok-o">.</span>fetchGit <span class="tok-p">{</span>
|
||
<span class="tok-ss">url =</span> <span class="tok-s2">"https://codeberg.org/mhwombat/hello-nix"</span><span class="tok-p">;</span>
|
||
<span class="tok-ss">rev =</span> <span class="tok-s2">"aa2c87f8b89578b069b09fdb2be30a0c9d8a77d8"</span><span class="tok-p">;</span>
|
||
<span class="tok-p">});</span>
|
||
<span class="tok-k">in</span>
|
||
mkShell <span class="tok-p">{</span>
|
||
<span class="tok-ss">buildInputs =</span> <span class="tok-p">[</span> hello <span class="tok-p">];</span>
|
||
<span class="tok-p">}</span>
|
||
</pre></td></tr></table></code></pre>
|
||
</div>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>Here’s a demonstration using the shell.</p>
|
||
</div>
|
||
<div class="literalblock">
|
||
<div class="content">
|
||
<pre class="nowrap">$ nix-shell
|
||
error: getting status of '/home/amy/codeberg/nix-book/source/shell-recipes/default.nix': No such file or directory
|
||
$ hello-nix
|
||
bash: line 48: hello-nix: command not found
|
||
Hello from your nix package!</pre>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="sect2">
|
||
<h3 id="_shell_with_access_to_a_flake">9.3. Shell with access to a flake</h3>
|
||
<div class="listingblock">
|
||
<div class="content">
|
||
<pre class="pygments highlight nowrap"><code data-lang="nix"><table class="linenotable"><tr><td class="linenos"><div class="linenodiv"><pre><span class="normal"> 1</span>
|
||
<span class="normal"> 2</span>
|
||
<span class="normal"> 3</span>
|
||
<span class="normal"> 4</span>
|
||
<span class="normal"> 5</span>
|
||
<span class="normal"> 6</span>
|
||
<span class="normal"> 7</span>
|
||
<span class="normal"> 8</span>
|
||
<span class="normal"> 9</span>
|
||
<span class="normal">10</span>
|
||
<span class="normal">11</span></pre></div></td><td class="code"><pre><span></span><span class="tok-k">with</span> <span class="tok-p">(</span><span class="tok-nb">import</span> <span class="tok-l"><nixpkgs></span> <span class="tok-p">{});</span>
|
||
<span class="tok-k">let</span>
|
||
<span class="tok-ss">hello =</span> <span class="tok-p">(</span><span class="tok-nb">builtins</span><span class="tok-o">.</span>getFlake <span class="tok-l">git+https://codeberg.org/mhwombat/hello-flake</span><span class="tok-p">)</span><span class="tok-o">.</span>packages<span class="tok-o">.</span><span class="tok-err">$</span><span class="tok-p">{</span><span class="tok-nb">builtins</span><span class="tok-o">.</span>currentSystem<span class="tok-p">}</span><span class="tok-o">.</span>default<span class="tok-p">;</span>
|
||
<span class="tok-c1"># For older flakes, you might need an expression like this...</span>
|
||
<span class="tok-c1"># hello = (builtins.getFlake git+https://codeberg.org/mhwombat/hello-flake).defaultPackage.${builtins.currentSystem};</span>
|
||
<span class="tok-k">in</span>
|
||
mkShell <span class="tok-p">{</span>
|
||
<span class="tok-ss">buildInputs =</span> <span class="tok-p">[</span>
|
||
hello
|
||
<span class="tok-p">];</span>
|
||
<span class="tok-p">}</span>
|
||
</pre></td></tr></table></code></pre>
|
||
</div>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>Here’s a demonstration using the shell.</p>
|
||
</div>
|
||
<div class="literalblock">
|
||
<div class="content">
|
||
<pre class="nowrap">$ nix-shell
|
||
error: getting status of '/home/amy/codeberg/nix-book/source/shell-recipes/default.nix': No such file or directory
|
||
$ hello-flake
|
||
bash: line 65: hello-flake: command not found
|
||
Hello from your flake!</pre>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="sect2">
|
||
<h3 id="_shell_with_access_to_a_specific_revision_of_a_flake">9.4. Shell with access to a specific revision of a flake</h3>
|
||
<div class="listingblock">
|
||
<div class="content">
|
||
<pre class="pygments highlight nowrap"><code data-lang="nix"><table class="linenotable"><tr><td class="linenos"><div class="linenodiv"><pre><span class="normal">1</span>
|
||
<span class="normal">2</span>
|
||
<span class="normal">3</span>
|
||
<span class="normal">4</span>
|
||
<span class="normal">5</span>
|
||
<span class="normal">6</span>
|
||
<span class="normal">7</span>
|
||
<span class="normal">8</span>
|
||
<span class="normal">9</span></pre></div></td><td class="code"><pre><span></span><span class="tok-k">with</span> <span class="tok-p">(</span><span class="tok-nb">import</span> <span class="tok-l"><nixpkgs></span> <span class="tok-p">{});</span>
|
||
<span class="tok-k">let</span>
|
||
<span class="tok-ss">hello =</span> <span class="tok-p">(</span><span class="tok-nb">builtins</span><span class="tok-o">.</span>getFlake <span class="tok-l">git+https://codeberg.org/mhwombat/hello-flake?ref=main&rev=3aa43dbe7be878dde7b2bdcbe992fe1705da3150</span><span class="tok-p">)</span><span class="tok-o">.</span>packages<span class="tok-o">.</span><span class="tok-err">$</span><span class="tok-p">{</span><span class="tok-nb">builtins</span><span class="tok-o">.</span>currentSystem<span class="tok-p">}</span><span class="tok-o">.</span>default<span class="tok-p">;</span>
|
||
<span class="tok-k">in</span>
|
||
mkShell <span class="tok-p">{</span>
|
||
<span class="tok-ss">buildInputs =</span> <span class="tok-p">[</span>
|
||
hello
|
||
<span class="tok-p">];</span>
|
||
<span class="tok-p">}</span>
|
||
</pre></td></tr></table></code></pre>
|
||
</div>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>Here’s a demonstration using the shell.</p>
|
||
</div>
|
||
<div class="literalblock">
|
||
<div class="content">
|
||
<pre class="nowrap">$ nix-shell
|
||
error: getting status of '/home/amy/codeberg/nix-book/source/shell-recipes/default.nix': No such file or directory
|
||
$ hello-flake
|
||
bash: line 82: hello-flake: command not found
|
||
Hello from your flake!</pre>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="sect2">
|
||
<h3 id="_shell_with_access_to_a_haskell_package_on_your_local_computer">9.5. Shell with access to a Haskell package on your local computer</h3>
|
||
<div class="paragraph">
|
||
<p>This shell provides access to three Haskell packages that are on my hard
|
||
drive.</p>
|
||
</div>
|
||
<div class="listingblock">
|
||
<div class="content">
|
||
<pre class="pygments highlight nowrap"><code data-lang="nix"><table class="linenotable"><tr><td class="linenos"><div class="linenodiv"><pre><span class="normal"> 1</span>
|
||
<span class="normal"> 2</span>
|
||
<span class="normal"> 3</span>
|
||
<span class="normal"> 4</span>
|
||
<span class="normal"> 5</span>
|
||
<span class="normal"> 6</span>
|
||
<span class="normal"> 7</span>
|
||
<span class="normal"> 8</span>
|
||
<span class="normal"> 9</span>
|
||
<span class="normal">10</span>
|
||
<span class="normal">11</span>
|
||
<span class="normal">12</span>
|
||
<span class="normal">13</span>
|
||
<span class="normal">14</span></pre></div></td><td class="code"><pre><span></span><span class="tok-k">with</span> <span class="tok-p">(</span><span class="tok-nb">import</span> <span class="tok-l"><nixpkgs></span> <span class="tok-p">{});</span>
|
||
<span class="tok-k">let</span>
|
||
<span class="tok-ss">pandoc-linear-table =</span> haskellPackages<span class="tok-o">.</span>callCabal2nix <span class="tok-s2">"pandoc-linear-table"</span> <span class="tok-l">/home/amy/github/pandoc-linear-table</span> <span class="tok-p">{};</span>
|
||
<span class="tok-ss">pandoc-logic-proof =</span> haskellPackages<span class="tok-o">.</span>callCabal2nix <span class="tok-s2">"pandoc-logic-proof"</span> <span class="tok-l">/home/amy/github/pandoc-logic-proof</span> <span class="tok-p">{};</span>
|
||
<span class="tok-ss">pandoc-columns =</span> haskellPackages<span class="tok-o">.</span>callCabal2nix <span class="tok-s2">"pandoc-columns"</span> <span class="tok-l">/home/amy/github/pandoc-columns</span> <span class="tok-p">{};</span>
|
||
<span class="tok-k">in</span>
|
||
mkShell <span class="tok-p">{</span>
|
||
<span class="tok-ss">buildInputs =</span> <span class="tok-p">[</span>
|
||
pandoc
|
||
pandoc-linear-table
|
||
pandoc-logic-proof
|
||
pandoc-columns
|
||
<span class="tok-p">];</span>
|
||
<span class="tok-p">}</span>
|
||
</pre></td></tr></table></code></pre>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="sect2">
|
||
<h3 id="_shell_with_access_to_a_haskell_package_on_your_local_computer_with_interdependencies">9.6. Shell with access to a Haskell package on your local computer, with interdependencies</h3>
|
||
<div class="paragraph">
|
||
<p>This shell provides access to four Haskell packages that are on my hard
|
||
drive. The fourth package depends on the first three to build.</p>
|
||
</div>
|
||
<div class="listingblock">
|
||
<div class="content">
|
||
<pre class="pygments highlight nowrap"><code data-lang="nix"><table class="linenotable"><tr><td class="linenos"><div class="linenodiv"><pre><span class="normal"> 1</span>
|
||
<span class="normal"> 2</span>
|
||
<span class="normal"> 3</span>
|
||
<span class="normal"> 4</span>
|
||
<span class="normal"> 5</span>
|
||
<span class="normal"> 6</span>
|
||
<span class="normal"> 7</span>
|
||
<span class="normal"> 8</span>
|
||
<span class="normal"> 9</span>
|
||
<span class="normal">10</span>
|
||
<span class="normal">11</span>
|
||
<span class="normal">12</span>
|
||
<span class="normal">13</span>
|
||
<span class="normal">14</span>
|
||
<span class="normal">15</span>
|
||
<span class="normal">16</span>
|
||
<span class="normal">17</span>
|
||
<span class="normal">18</span>
|
||
<span class="normal">19</span></pre></div></td><td class="code"><pre><span></span><span class="tok-k">with</span> <span class="tok-p">(</span><span class="tok-nb">import</span> <span class="tok-l"><nixpkgs></span> <span class="tok-p">{});</span>
|
||
<span class="tok-k">let</span>
|
||
<span class="tok-ss">pandoc-linear-table =</span> haskellPackages<span class="tok-o">.</span>callCabal2nix <span class="tok-s2">"pandoc-linear-table"</span> <span class="tok-l">/home/amy/github/pandoc-linear-table</span> <span class="tok-p">{};</span>
|
||
<span class="tok-ss">pandoc-logic-proof =</span> haskellPackages<span class="tok-o">.</span>callCabal2nix <span class="tok-s2">"pandoc-logic-proof"</span> <span class="tok-l">/home/amy/github/pandoc-logic-proof</span> <span class="tok-p">{};</span>
|
||
<span class="tok-ss">pandoc-columns =</span> haskellPackages<span class="tok-o">.</span>callCabal2nix <span class="tok-s2">"pandoc-columns"</span> <span class="tok-l">/home/amy/github/pandoc-columns</span> <span class="tok-p">{};</span>
|
||
<span class="tok-ss">pandoc-maths-web =</span> haskellPackages<span class="tok-o">.</span>callCabal2nix <span class="tok-s2">"pandoc-maths-web"</span> <span class="tok-l">/home/amy/github/pandoc-maths-web</span>
|
||
<span class="tok-p">{</span>
|
||
<span class="tok-k">inherit</span> pandoc-linear-table pandoc-logic-proof pandoc-columns<span class="tok-p">;</span>
|
||
<span class="tok-p">};</span>
|
||
<span class="tok-k">in</span>
|
||
mkShell <span class="tok-p">{</span>
|
||
<span class="tok-ss">buildInputs =</span> <span class="tok-p">[</span>
|
||
pandoc
|
||
pandoc-linear-table
|
||
pandoc-logic-proof
|
||
pandoc-columns
|
||
pandoc-maths-web
|
||
<span class="tok-p">];</span>
|
||
<span class="tok-p">}</span>
|
||
</pre></td></tr></table></code></pre>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="sect2">
|
||
<h3 id="_shell_with_an_environment_variable_set">9.7. Shell with an environment variable set</h3>
|
||
<div class="paragraph">
|
||
<p>This shell has the environment variable FOO set to ``bar''</p>
|
||
</div>
|
||
<div class="listingblock">
|
||
<div class="content">
|
||
<pre class="pygments highlight nowrap"><code data-lang="nix"><table class="linenotable"><tr><td class="linenos"><div class="linenodiv"><pre><span class="normal">1</span>
|
||
<span class="normal">2</span>
|
||
<span class="normal">3</span>
|
||
<span class="normal">4</span>
|
||
<span class="normal">5</span>
|
||
<span class="normal">6</span></pre></div></td><td class="code"><pre><span></span><span class="tok-k">with</span> <span class="tok-p">(</span><span class="tok-nb">import</span> <span class="tok-l"><nixpkgs></span> <span class="tok-p">{});</span>
|
||
mkShell <span class="tok-p">{</span>
|
||
<span class="tok-ss">shellHook =</span> <span class="tok-s1">''</span>
|
||
<span class="tok-s1"> export FOO="bar"</span>
|
||
<span class="tok-s1"> ''</span><span class="tok-p">;</span>
|
||
<span class="tok-p">}</span>
|
||
</pre></td></tr></table></code></pre>
|
||
</div>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>Here’s a demonstration using the shell.</p>
|
||
</div>
|
||
<div class="literalblock">
|
||
<div class="content">
|
||
<pre class="nowrap">$ nix-shell
|
||
error: getting status of '/home/amy/codeberg/nix-book/source/shell-recipes/default.nix': No such file or directory
|
||
$ echo $FOO
|
||
|
||
bar</pre>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="sect1">
|
||
<h2 id="_nix_shell_shebangs">10. Nix-shell shebangs</h2>
|
||
<div class="sectionbody">
|
||
<div class="paragraph">
|
||
<p>You can use <code>nix-shell</code> to run scripts in arbitrary languages, providing
|
||
the necessary dependencies. This is particularly convenient for
|
||
standalone scripts because you don’t need to create a repo and write a
|
||
separate <code>flake.nix</code>.</p>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>The script should start with two <code>`shebang'' (</code>#!<code>) commands. The first
|
||
should invoke `nix-shell</code>. The second should declares the scrpt
|
||
interpreter and any dependencies. Here are some examples.</p>
|
||
</div>
|
||
<div class="sect2">
|
||
<h3 id="_a_bash_script_depending_on_a_package_in_the_nixpkgs_repo">10.1. A Bash script depending on a package in the nixpkgs repo.</h3>
|
||
<div class="paragraph">
|
||
<p>Script:</p>
|
||
</div>
|
||
<div class="listingblock">
|
||
<div class="content">
|
||
<pre class="pygments highlight nowrap"><code data-lang="bash"><table class="linenotable"><tr><td class="linenos"><div class="linenodiv"><pre><span class="normal">1</span>
|
||
<span class="normal">2</span>
|
||
<span class="normal">3</span></pre></div></td><td class="code"><pre><span></span><span class="tok-ch">#! /usr/bin/env nix-shell</span>
|
||
<span class="tok-c1">#! nix-shell -i bash -p cowsay</span>
|
||
cowsay <span class="tok-s2">"Pretty cool, huh?"</span>
|
||
</pre></td></tr></table></code></pre>
|
||
</div>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>Output:</p>
|
||
</div>
|
||
<div class="literalblock">
|
||
<div class="content">
|
||
<pre class="nowrap"> ___________________
|
||
< Pretty cool, huh? >
|
||
-------------------
|
||
\ ^__^
|
||
\ (oo)\_______
|
||
(__)\ )\/\
|
||
||----w |
|
||
|| ||</pre>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="sect2">
|
||
<h3 id="_a_python_script_depending_on_a_package_in_the_nixpkgs_repo">10.2. A Python script depending on a package in the nixpkgs repo.</h3>
|
||
<div class="paragraph">
|
||
<p>Script:</p>
|
||
</div>
|
||
<div class="listingblock">
|
||
<div class="content">
|
||
<pre class="pygments highlight nowrap"><code data-lang="bash"><table class="linenotable"><tr><td class="linenos"><div class="linenodiv"><pre><span class="normal"> 1</span>
|
||
<span class="normal"> 2</span>
|
||
<span class="normal"> 3</span>
|
||
<span class="normal"> 4</span>
|
||
<span class="normal"> 5</span>
|
||
<span class="normal"> 6</span>
|
||
<span class="normal"> 7</span>
|
||
<span class="normal"> 8</span>
|
||
<span class="normal"> 9</span>
|
||
<span class="normal">10</span>
|
||
<span class="normal">11</span></pre></div></td><td class="code"><pre><span></span><span class="tok-ch">#! /usr/bin/env nix-shell</span>
|
||
<span class="tok-c1">#! nix-shell -i python3 -p python3Packages.html-sanitizer</span>
|
||
|
||
from html_sanitizer import Sanitizer
|
||
<span class="tok-nv">sanitizer</span> <span class="tok-o">=</span> Sanitizer<span class="tok-o">()</span> <span class="tok-c1"># default configuration</span>
|
||
|
||
<span class="tok-nv">original</span><span class="tok-o">=</span><span class="tok-s1">'<span style="font-weight:bold">some text</span>'</span>
|
||
print<span class="tok-o">(</span><span class="tok-s1">'original: '</span>, original<span class="tok-o">)</span>
|
||
|
||
<span class="tok-nv">sanitized</span><span class="tok-o">=</span>sanitizer.sanitize<span class="tok-o">(</span>original<span class="tok-o">)</span>
|
||
print<span class="tok-o">(</span><span class="tok-s1">'sanitized: '</span>, sanitized<span class="tok-o">)</span>
|
||
</pre></td></tr></table></code></pre>
|
||
</div>
|
||
</div>
|
||
<div class="paragraph">
|
||
<p>Output:</p>
|
||
</div>
|
||
<div class="literalblock">
|
||
<div class="content">
|
||
<pre class="nowrap">original: <span style="font-weight:bold">some text</span>
|
||
sanitized: <strong>some text</strong></pre>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div id="footnotes">
|
||
<hr>
|
||
<div class="footnote" id="_footnotedef_1">
|
||
<a href="#_footnoteref_1">1</a>. For an introduction to the Nix language, see <a href="https://nixos.org/guides/nix-language.html">Nix language basics</a>.
|
||
</div>
|
||
<div class="footnote" id="_footnotedef_2">
|
||
<a href="#_footnoteref_2">2</a>. For more information on the standard environment, see the <a href="https://nixos.org/manual/nixpkgs/stable/#sec-tools-of-stdenv">Nixpkgs manual</a>
|
||
</div>
|
||
</div>
|
||
<div id="footer">
|
||
<div id="footer-text">
|
||
Last updated 1980-01-01 00:00:00 UTC
|
||
</div>
|
||
</div>
|
||
</body>
|
||
</html> |