mirror of
https://github.com/privatevoid-net/nix-super.git
synced 2024-11-22 14:06:16 +02:00
Get rid of callouts since Markdown doesn't support them
This commit is contained in:
parent
efff6cf163
commit
13df1faf25
12 changed files with 233 additions and 251 deletions
|
@ -6,20 +6,25 @@
|
|||
|
||||
<title>Arguments and Variables</title>
|
||||
|
||||
<example xml:id='ex-hello-composition'>
|
||||
<para>The Nix expression in <xref linkend='ex-hello-nix' /> is a
|
||||
function; it is missing some arguments that have to be filled in
|
||||
somewhere. In the Nix Packages collection this is done in the file
|
||||
<filename>pkgs/top-level/all-packages.nix</filename>, where all Nix
|
||||
expressions for packages are imported and called with the appropriate
|
||||
arguments. Here are some fragments of
|
||||
<filename>all-packages.nix</filename>, with annotations of what they
|
||||
mean:</para>
|
||||
|
||||
<title>Composing GNU Hello
|
||||
(<filename>all-packages.nix</filename>)</title>
|
||||
<programlisting>
|
||||
...
|
||||
|
||||
rec { <co xml:id='ex-hello-composition-co-1' />
|
||||
rec { ①
|
||||
|
||||
hello = import ../applications/misc/hello/ex-1 <co xml:id='ex-hello-composition-co-2' /> { <co xml:id='ex-hello-composition-co-3' />
|
||||
hello = import ../applications/misc/hello/ex-1 ② { ③
|
||||
inherit fetchurl stdenv perl;
|
||||
};
|
||||
|
||||
perl = import ../development/interpreters/perl { <co xml:id='ex-hello-composition-co-4' />
|
||||
perl = import ../development/interpreters/perl { ④
|
||||
inherit fetchurl stdenv;
|
||||
};
|
||||
|
||||
|
@ -31,31 +36,19 @@ rec { <co xml:id='ex-hello-composition-co-1' />
|
|||
|
||||
}
|
||||
</programlisting>
|
||||
</example>
|
||||
|
||||
<para>The Nix expression in <xref linkend='ex-hello-nix' /> is a
|
||||
function; it is missing some arguments that have to be filled in
|
||||
somewhere. In the Nix Packages collection this is done in the file
|
||||
<filename>pkgs/top-level/all-packages.nix</filename>, where all
|
||||
Nix expressions for packages are imported and called with the
|
||||
appropriate arguments. <xref linkend='ex-hello-composition' /> shows
|
||||
some fragments of
|
||||
<filename>all-packages.nix</filename>.</para>
|
||||
|
||||
<calloutlist>
|
||||
|
||||
<callout arearefs='ex-hello-composition-co-1'>
|
||||
<orderedlist>
|
||||
|
||||
<listitem>
|
||||
<para>This file defines a set of attributes, all of which are
|
||||
concrete derivations (i.e., not functions). In fact, we define a
|
||||
<emphasis>mutually recursive</emphasis> set of attributes. That
|
||||
is, the attributes can refer to each other. This is precisely
|
||||
what we want since we want to <quote>plug</quote> the
|
||||
various packages into each other.</para>
|
||||
</listitem>
|
||||
|
||||
</callout>
|
||||
|
||||
<callout arearefs='ex-hello-composition-co-2'>
|
||||
<listitem>
|
||||
|
||||
<para>Here we <emphasis>import</emphasis> the Nix expression for
|
||||
GNU Hello. The import operation just loads and returns the
|
||||
|
@ -71,9 +64,9 @@ some fragments of
|
|||
When you try to import a directory, Nix automatically appends
|
||||
<filename>/default.nix</filename> to the file name.</para>
|
||||
|
||||
</callout>
|
||||
</listitem>
|
||||
|
||||
<callout arearefs='ex-hello-composition-co-3'>
|
||||
<listitem>
|
||||
|
||||
<para>This is where the actual composition takes place. Here we
|
||||
<emphasis>call</emphasis> the function imported from
|
||||
|
@ -107,15 +100,15 @@ hello = callPackage ../applications/misc/hello/ex-1 { stdenv = myStdenv; };
|
|||
|
||||
</para></note>
|
||||
|
||||
</callout>
|
||||
</listitem>
|
||||
|
||||
<callout arearefs='ex-hello-composition-co-4'>
|
||||
<listitem>
|
||||
|
||||
<para>Likewise, we have to instantiate Perl,
|
||||
<varname>fetchurl</varname>, and the standard environment.</para>
|
||||
|
||||
</callout>
|
||||
</listitem>
|
||||
|
||||
</calloutlist>
|
||||
</orderedlist>
|
||||
|
||||
</section>
|
||||
</section>
|
||||
|
|
|
@ -6,32 +6,30 @@
|
|||
|
||||
<title>Build Script</title>
|
||||
|
||||
<example xml:id='ex-hello-builder'><title>Build script for GNU Hello
|
||||
(<filename>builder.sh</filename>)</title>
|
||||
<programlisting>
|
||||
source $stdenv/setup <co xml:id='ex-hello-builder-co-1' />
|
||||
|
||||
PATH=$perl/bin:$PATH <co xml:id='ex-hello-builder-co-2' />
|
||||
|
||||
tar xvfz $src <co xml:id='ex-hello-builder-co-3' />
|
||||
cd hello-*
|
||||
./configure --prefix=$out <co xml:id='ex-hello-builder-co-4' />
|
||||
make <co xml:id='ex-hello-builder-co-5' />
|
||||
make install</programlisting>
|
||||
</example>
|
||||
|
||||
<para><xref linkend='ex-hello-builder' /> shows the builder referenced
|
||||
<para>Here is the builder referenced
|
||||
from Hello's Nix expression (stored in
|
||||
<filename>pkgs/applications/misc/hello/ex-1/builder.sh</filename>).
|
||||
The builder can actually be made a lot shorter by using the
|
||||
<filename>pkgs/applications/misc/hello/ex-1/builder.sh</filename>):</para>
|
||||
|
||||
<programlisting>
|
||||
source $stdenv/setup ①
|
||||
|
||||
PATH=$perl/bin:$PATH ②
|
||||
|
||||
tar xvfz $src ③
|
||||
cd hello-*
|
||||
./configure --prefix=$out ④
|
||||
make ⑤
|
||||
make install</programlisting>
|
||||
|
||||
<para>The builder can actually be made a lot shorter by using the
|
||||
<emphasis>generic builder</emphasis> functions provided by
|
||||
<varname>stdenv</varname>, but here we write out the build steps to
|
||||
elucidate what a builder does. It performs the following
|
||||
steps:</para>
|
||||
|
||||
<calloutlist>
|
||||
<orderedlist>
|
||||
|
||||
<callout arearefs='ex-hello-builder-co-1'>
|
||||
<listitem>
|
||||
|
||||
<para>When Nix runs a builder, it initially completely clears the
|
||||
environment (except for the attributes declared in the
|
||||
|
@ -52,9 +50,9 @@ steps:</para>
|
|||
attribute in <xref linkend='ex-hello-nix' />, but
|
||||
<varname>mkDerivation</varname> adds it automatically.)</para>
|
||||
|
||||
</callout>
|
||||
</listitem>
|
||||
|
||||
<callout arearefs='ex-hello-builder-co-2'>
|
||||
<listitem>
|
||||
|
||||
<para>Since Hello needs Perl, we have to make sure that Perl is in
|
||||
the <literal>PATH</literal>. The <literal>perl</literal> environment
|
||||
|
@ -63,9 +61,9 @@ steps:</para>
|
|||
<filename><replaceable>$perl</replaceable>/bin</filename> is the
|
||||
directory containing the Perl interpreter.</para>
|
||||
|
||||
</callout>
|
||||
</listitem>
|
||||
|
||||
<callout arearefs='ex-hello-builder-co-3'>
|
||||
<listitem>
|
||||
|
||||
<para>Now we have to unpack the sources. The
|
||||
<varname>src</varname> attribute was bound to the result of
|
||||
|
@ -82,9 +80,9 @@ steps:</para>
|
|||
always newly created, so you don't have to worry about files from
|
||||
previous builds interfering with the current build.</para>
|
||||
|
||||
</callout>
|
||||
</listitem>
|
||||
|
||||
<callout arearefs='ex-hello-builder-co-4'>
|
||||
<listitem>
|
||||
|
||||
<para>GNU Hello is a typical Autoconf-based package, so we first
|
||||
have to run its <filename>configure</filename> script. In Nix
|
||||
|
@ -98,17 +96,17 @@ steps:</para>
|
|||
<literal>--prefix=$out</literal> to cause Hello to be installed in
|
||||
the expected location.</para>
|
||||
|
||||
</callout>
|
||||
</listitem>
|
||||
|
||||
<callout arearefs='ex-hello-builder-co-5'>
|
||||
<listitem>
|
||||
|
||||
<para>Finally we build Hello (<literal>make</literal>) and install
|
||||
it into the location specified by <literal>out</literal>
|
||||
(<literal>make install</literal>).</para>
|
||||
|
||||
</callout>
|
||||
</listitem>
|
||||
|
||||
</calloutlist>
|
||||
</orderedlist>
|
||||
|
||||
<para>If you are wondering about the absence of error checking on the
|
||||
result of various commands called in the builder: this is because the
|
||||
|
@ -116,4 +114,4 @@ shell script is evaluated with Bash's <option>-e</option> option,
|
|||
which causes the script to be aborted if any command fails without an
|
||||
error check.</para>
|
||||
|
||||
</section>
|
||||
</section>
|
||||
|
|
|
@ -1511,39 +1511,9 @@ in foo</programlisting>
|
|||
<!-- TODO: more formally describe the schema of the XML
|
||||
representation -->
|
||||
|
||||
<para><xref linkend='ex-toxml' /> shows an example where this is
|
||||
the case. The builder is supposed to generate the configuration
|
||||
file for a <link xlink:href='http://jetty.mortbay.org/'>Jetty
|
||||
servlet container</link>. A servlet container contains a number
|
||||
of servlets (<filename>*.war</filename> files) each exported under
|
||||
a specific URI prefix. So the servlet configuration is a list of
|
||||
sets containing the <varname>path</varname> and
|
||||
<varname>war</varname> of the servlet (<xref
|
||||
linkend='ex-toxml-co-servlets' />). This kind of information is
|
||||
difficult to communicate with the normal method of passing
|
||||
information through an environment variable, which just
|
||||
concatenates everything together into a string (which might just
|
||||
work in this case, but wouldn’t work if fields are optional or
|
||||
contain lists themselves). Instead the Nix expression is
|
||||
converted to an XML representation with
|
||||
<function>toXML</function>, which is unambiguous and can easily be
|
||||
processed with the appropriate tools. For instance, in the
|
||||
example an XSLT stylesheet (<xref linkend='ex-toxml-co-stylesheet'
|
||||
/>) is applied to it (<xref linkend='ex-toxml-co-apply' />) to
|
||||
generate the XML configuration file for the Jetty server. The XML
|
||||
representation produced from <xref linkend='ex-toxml-co-servlets'
|
||||
/> by <function>toXML</function> is shown in <xref
|
||||
linkend='ex-toxml-result' />.</para>
|
||||
|
||||
<para>Note that <xref linkend='ex-toxml' /> uses the <function
|
||||
linkend='builtin-toFile'>toFile</function> built-in to write the
|
||||
builder and the stylesheet “inline” in the Nix expression. The
|
||||
path of the stylesheet is spliced into the builder at
|
||||
<literal>xsltproc ${stylesheet}
|
||||
<replaceable>...</replaceable></literal>.</para>
|
||||
|
||||
<example xml:id='ex-toxml'><title>Passing information to a builder
|
||||
using <function>toXML</function></title>
|
||||
<para>Here is an example where this is
|
||||
the case:
|
||||
</para>
|
||||
|
||||
<programlisting><![CDATA[
|
||||
{ stdenv, fetchurl, libxslt, jira, uberwiki }:
|
||||
|
@ -1556,10 +1526,10 @@ stdenv.mkDerivation (rec {
|
|||
builder = builtins.toFile "builder.sh" "
|
||||
source $stdenv/setup
|
||||
mkdir $out
|
||||
echo "$servlets" | xsltproc ${stylesheet} - > $out/server-conf.xml]]> <co xml:id='ex-toxml-co-apply' /> <![CDATA[
|
||||
echo "$servlets" | xsltproc ${stylesheet} - > $out/server-conf.xml]]> ① <![CDATA[
|
||||
";
|
||||
|
||||
stylesheet = builtins.toFile "stylesheet.xsl"]]> <co xml:id='ex-toxml-co-stylesheet' /> <![CDATA[
|
||||
stylesheet = builtins.toFile "stylesheet.xsl"]]> ② <![CDATA[
|
||||
"<?xml version='1.0' encoding='UTF-8'?>
|
||||
<xsl:stylesheet xmlns:xsl='http://www.w3.org/1999/XSL/Transform' version='1.0'>
|
||||
<xsl:template match='/'>
|
||||
|
@ -1575,16 +1545,32 @@ stdenv.mkDerivation (rec {
|
|||
</xsl:stylesheet>
|
||||
";
|
||||
|
||||
servlets = builtins.toXML []]> <co xml:id='ex-toxml-co-servlets' /> <![CDATA[
|
||||
servlets = builtins.toXML []]> ③ <![CDATA[
|
||||
{ path = "/bugtracker"; war = jira + "/lib/atlassian-jira.war"; }
|
||||
{ path = "/wiki"; war = uberwiki + "/uberwiki.war"; }
|
||||
];
|
||||
})]]></programlisting>
|
||||
|
||||
</example>
|
||||
|
||||
<example xml:id='ex-toxml-result'><title>XML representation produced by
|
||||
<function>toXML</function></title>
|
||||
<para>The builder is supposed to generate the configuration file
|
||||
for a <link xlink:href='http://jetty.mortbay.org/'>Jetty servlet
|
||||
container</link>. A servlet container contains a number of
|
||||
servlets (<filename>*.war</filename> files) each exported under a
|
||||
specific URI prefix. So the servlet configuration is a list of
|
||||
sets containing the <varname>path</varname> and
|
||||
<varname>war</varname> of the servlet (<xref
|
||||
linkend='ex-toxml-co-servlets' />). This kind of information is
|
||||
difficult to communicate with the normal method of passing
|
||||
information through an environment variable, which just
|
||||
concatenates everything together into a string (which might just
|
||||
work in this case, but wouldn’t work if fields are optional or
|
||||
contain lists themselves). Instead the Nix expression is
|
||||
converted to an XML representation with
|
||||
<function>toXML</function>, which is unambiguous and can easily be
|
||||
processed with the appropriate tools. For instance, in the
|
||||
example an XSLT stylesheet (at point ②) is applied to it (at point
|
||||
①) to generate the XML configuration file for the Jetty server.
|
||||
The XML representation produced at point ③ by
|
||||
<function>toXML</function> is as follows:</para>
|
||||
|
||||
<programlisting><![CDATA[<?xml version='1.0' encoding='utf-8'?>
|
||||
<expr>
|
||||
|
@ -1608,7 +1594,11 @@ stdenv.mkDerivation (rec {
|
|||
</list>
|
||||
</expr>]]></programlisting>
|
||||
|
||||
</example>
|
||||
<para>Note that <xref linkend='ex-toxml' /> uses the <function
|
||||
linkend='builtin-toFile'>toFile</function> built-in to write the
|
||||
builder and the stylesheet “inline” in the Nix expression. The
|
||||
path of the stylesheet is spliced into the builder using the
|
||||
syntax <literal>xsltproc ${stylesheet}</literal>.</para>
|
||||
|
||||
</listitem>
|
||||
|
||||
|
|
|
@ -6,33 +6,31 @@
|
|||
|
||||
<title>Expression Syntax</title>
|
||||
|
||||
<example xml:id='ex-hello-nix'><title>Nix expression for GNU Hello
|
||||
(<filename>default.nix</filename>)</title>
|
||||
<programlisting>
|
||||
{ stdenv, fetchurl, perl }: <co xml:id='ex-hello-nix-co-1' />
|
||||
<para>Here is a Nix expression for GNU Hello:</para>
|
||||
|
||||
stdenv.mkDerivation { <co xml:id='ex-hello-nix-co-2' />
|
||||
name = "hello-2.1.1"; <co xml:id='ex-hello-nix-co-3' />
|
||||
builder = ./builder.sh; <co xml:id='ex-hello-nix-co-4' />
|
||||
src = fetchurl { <co xml:id='ex-hello-nix-co-5' />
|
||||
<programlisting>
|
||||
{ stdenv, fetchurl, perl }: ①
|
||||
|
||||
stdenv.mkDerivation { ②
|
||||
name = "hello-2.1.1"; ③
|
||||
builder = ./builder.sh; ④
|
||||
src = fetchurl { ⑤
|
||||
url = "ftp://ftp.nluug.nl/pub/gnu/hello/hello-2.1.1.tar.gz";
|
||||
sha256 = "1md7jsfd8pa45z73bz1kszpp01yw6x5ljkjk2hx7wl800any6465";
|
||||
};
|
||||
inherit perl; <co xml:id='ex-hello-nix-co-6' />
|
||||
inherit perl; ⑥
|
||||
}</programlisting>
|
||||
</example>
|
||||
|
||||
<para><xref linkend='ex-hello-nix' /> shows a Nix expression for GNU
|
||||
Hello. It's actually already in the Nix Packages collection in
|
||||
<para>This file is actually already in the Nix Packages collection in
|
||||
<filename>pkgs/applications/misc/hello/ex-1/default.nix</filename>.
|
||||
It is customary to place each package in a separate directory and call
|
||||
the single Nix expression in that directory
|
||||
<filename>default.nix</filename>. The file has the following elements
|
||||
(referenced from the figure by number):
|
||||
|
||||
<calloutlist>
|
||||
<orderedlist>
|
||||
|
||||
<callout arearefs='ex-hello-nix-co-1'>
|
||||
<listitem>
|
||||
|
||||
<para>This states that the expression is a
|
||||
<emphasis>function</emphasis> that expects to be called with three
|
||||
|
@ -57,9 +55,9 @@ the single Nix expression in that directory
|
|||
function; when given the required arguments, the body should
|
||||
describe how to build an instance of the Hello package.</para>
|
||||
|
||||
</callout>
|
||||
</listitem>
|
||||
|
||||
<callout arearefs='ex-hello-nix-co-2'>
|
||||
<listitem>
|
||||
|
||||
<para>So we have to build a package. Building something from
|
||||
other stuff is called a <emphasis>derivation</emphasis> in Nix (as
|
||||
|
@ -76,9 +74,9 @@ the single Nix expression in that directory
|
|||
<replaceable>nameN</replaceable> =
|
||||
<replaceable>exprN</replaceable>; }</literal>.</para>
|
||||
|
||||
</callout>
|
||||
</listitem>
|
||||
|
||||
<callout arearefs='ex-hello-nix-co-3'>
|
||||
<listitem>
|
||||
|
||||
<para>The attribute <varname>name</varname> specifies the symbolic
|
||||
name and version of the package. Nix doesn't really care about
|
||||
|
@ -87,9 +85,9 @@ the single Nix expression in that directory
|
|||
packages. This attribute is required by
|
||||
<varname>mkDerivation</varname>.</para>
|
||||
|
||||
</callout>
|
||||
</listitem>
|
||||
|
||||
<callout arearefs='ex-hello-nix-co-4'>
|
||||
<listitem>
|
||||
|
||||
<para>The attribute <varname>builder</varname> specifies the
|
||||
builder. This attribute can sometimes be omitted, in which case
|
||||
|
@ -101,9 +99,9 @@ the single Nix expression in that directory
|
|||
<command>./builder.sh</command> refers to the shell script shown
|
||||
in <xref linkend='ex-hello-builder' />, discussed below.</para>
|
||||
|
||||
</callout>
|
||||
</listitem>
|
||||
|
||||
<callout arearefs='ex-hello-nix-co-5'>
|
||||
<listitem>
|
||||
|
||||
<para>The builder has to know what the sources of the package
|
||||
are. Here, the attribute <varname>src</varname> is bound to the
|
||||
|
@ -120,9 +118,9 @@ the single Nix expression in that directory
|
|||
customary, and it's also expected by the default builder (which we
|
||||
don't use in this example).</para>
|
||||
|
||||
</callout>
|
||||
</listitem>
|
||||
|
||||
<callout arearefs='ex-hello-nix-co-6'>
|
||||
<listitem>
|
||||
|
||||
<para>Since the derivation requires Perl, we have to pass the
|
||||
value of the <varname>perl</varname> function argument to the
|
||||
|
@ -139,9 +137,9 @@ perl = perl;</programlisting>
|
|||
causes the specified attributes to be bound to whatever variables
|
||||
with the same name happen to be in scope.</para>
|
||||
|
||||
</callout>
|
||||
</listitem>
|
||||
|
||||
</calloutlist>
|
||||
</orderedlist>
|
||||
|
||||
</para>
|
||||
|
||||
|
|
|
@ -20,23 +20,21 @@ make install</programlisting>
|
|||
The builders for almost all Unix packages look like this — set up some
|
||||
environment variables, unpack the sources, configure, build, and
|
||||
install. For this reason the standard environment provides some Bash
|
||||
functions that automate the build process. A builder using the
|
||||
generic build facilities in shown in <xref linkend='ex-hello-builder2'
|
||||
/>.</para>
|
||||
functions that automate the build process. Here is what a builder
|
||||
using the generic build facilities looks like:</para>
|
||||
|
||||
<example xml:id='ex-hello-builder2'><title>Build script using the generic
|
||||
build functions</title>
|
||||
<programlisting>
|
||||
buildInputs="$perl" <co xml:id='ex-hello-builder2-co-1' />
|
||||
buildInputs="$perl" ①
|
||||
|
||||
source $stdenv/setup <co xml:id='ex-hello-builder2-co-2' />
|
||||
source $stdenv/setup ②
|
||||
|
||||
genericBuild <co xml:id='ex-hello-builder2-co-3' /></programlisting>
|
||||
</example>
|
||||
genericBuild ③</programlisting>
|
||||
|
||||
<calloutlist>
|
||||
<para>Here is what each line means:
|
||||
|
||||
<callout arearefs='ex-hello-builder2-co-1'>
|
||||
<orderedlist>
|
||||
|
||||
<listitem>
|
||||
|
||||
<para>The <literal>buildInputs</literal> variable tells
|
||||
<filename>setup</filename> to use the indicated packages as
|
||||
|
@ -54,16 +52,16 @@ genericBuild <co xml:id='ex-hello-builder2-co-3' /></programlisting>
|
|||
inputs.</para></footnote>
|
||||
</para>
|
||||
|
||||
</callout>
|
||||
</listitem>
|
||||
|
||||
<callout arearefs='ex-hello-builder2-co-2'>
|
||||
<listitem arearefs='ex-hello-builder2-co-2'>
|
||||
|
||||
<para>The function <function>genericBuild</function> is defined in
|
||||
the file <literal>$stdenv/setup</literal>.</para>
|
||||
|
||||
</callout>
|
||||
</listitem>
|
||||
|
||||
<callout arearefs='ex-hello-builder2-co-3'>
|
||||
<listitem arearefs='ex-hello-builder2-co-3'>
|
||||
|
||||
<para>The final step calls the shell function
|
||||
<function>genericBuild</function>, which performs the steps that
|
||||
|
@ -73,9 +71,11 @@ genericBuild <co xml:id='ex-hello-builder2-co-3' /></programlisting>
|
|||
<command>bzip2</command>, etc. It can be customised in many ways;
|
||||
see the Nixpkgs manual for details.</para>
|
||||
|
||||
</callout>
|
||||
</listitem>
|
||||
|
||||
</calloutlist>
|
||||
</orderedlist>
|
||||
|
||||
</para>
|
||||
|
||||
<para>Discerning readers will note that the
|
||||
<literal>buildInputs</literal> could just as well have been set in the Nix
|
||||
|
|
|
@ -278,7 +278,9 @@ evaluate to a Boolean value. If it evaluates to
|
|||
<literal>true</literal>, <replaceable>e2</replaceable> is returned;
|
||||
otherwise expression evaluation is aborted and a backtrace is printed.</para>
|
||||
|
||||
<example xml:id='ex-subversion-nix'><title>Nix expression for Subversion</title>
|
||||
<para>Here is a Nix expression for the Subversion package that shows
|
||||
how assertions can be used:.</para>
|
||||
|
||||
<programlisting>
|
||||
{ localServer ? false
|
||||
, httpServer ? false
|
||||
|
@ -290,9 +292,9 @@ otherwise expression evaluation is aborted and a backtrace is printed.</para>
|
|||
, openssl ? null, httpd ? null, db4 ? null, expat, swig ? null, j2sdk ? null
|
||||
}:
|
||||
|
||||
assert localServer -> db4 != null; <co xml:id='ex-subversion-nix-co-1' />
|
||||
assert httpServer -> httpd != null && httpd.expat == expat; <co xml:id='ex-subversion-nix-co-2' />
|
||||
assert sslSupport -> openssl != null && (httpServer -> httpd.openssl == openssl); <co xml:id='ex-subversion-nix-co-3' />
|
||||
assert localServer -> db4 != null; ①
|
||||
assert httpServer -> httpd != null && httpd.expat == expat; ②
|
||||
assert sslSupport -> openssl != null && (httpServer -> httpd.openssl == openssl); ③
|
||||
assert pythonBindings -> swig != null && swig.pythonSupport;
|
||||
assert javaSwigBindings -> swig != null && swig.javaSupport;
|
||||
assert javahlBindings -> j2sdk != null;
|
||||
|
@ -300,26 +302,24 @@ assert javahlBindings -> j2sdk != null;
|
|||
stdenv.mkDerivation {
|
||||
name = "subversion-1.1.1";
|
||||
...
|
||||
openssl = if sslSupport then openssl else null; <co xml:id='ex-subversion-nix-co-4' />
|
||||
openssl = if sslSupport then openssl else null; ④
|
||||
...
|
||||
}</programlisting>
|
||||
</example>
|
||||
|
||||
<para><xref linkend='ex-subversion-nix' /> show how assertions are
|
||||
used in the Nix expression for Subversion.</para>
|
||||
<para>The points of interest are:</para>
|
||||
|
||||
<calloutlist>
|
||||
<orderedlist>
|
||||
|
||||
<callout arearefs='ex-subversion-nix-co-1'>
|
||||
<listitem>
|
||||
<para>This assertion states that if Subversion is to have support
|
||||
for local repositories, then Berkeley DB is needed. So if the
|
||||
Subversion function is called with the
|
||||
<varname>localServer</varname> argument set to
|
||||
<literal>true</literal> but the <varname>db4</varname> argument
|
||||
set to <literal>null</literal>, then the evaluation fails.</para>
|
||||
</callout>
|
||||
</listitem>
|
||||
|
||||
<callout arearefs='ex-subversion-nix-co-2'>
|
||||
<listitem>
|
||||
<para>This is a more subtle condition: if Subversion is built with
|
||||
Apache (<literal>httpServer</literal>) support, then the Expat
|
||||
library (an XML library) used by Subversion should be same as the
|
||||
|
@ -327,27 +327,27 @@ used in the Nix expression for Subversion.</para>
|
|||
Subversion code ends up being linked with Apache code, and if the
|
||||
Expat libraries do not match, a build- or runtime link error or
|
||||
incompatibility might occur.</para>
|
||||
</callout>
|
||||
</listitem>
|
||||
|
||||
<callout arearefs='ex-subversion-nix-co-3'>
|
||||
<listitem>
|
||||
<para>This assertion says that in order for Subversion to have SSL
|
||||
support (so that it can access <literal>https</literal> URLs), an
|
||||
OpenSSL library must be passed. Additionally, it says that
|
||||
<emphasis>if</emphasis> Apache support is enabled, then Apache's
|
||||
OpenSSL should match Subversion's. (Note that if Apache support
|
||||
is not enabled, we don't care about Apache's OpenSSL.)</para>
|
||||
</callout>
|
||||
</listitem>
|
||||
|
||||
<callout arearefs='ex-subversion-nix-co-4'>
|
||||
<listitem>
|
||||
<para>The conditional here is not really related to assertions,
|
||||
but is worth pointing out: it ensures that if SSL support is
|
||||
disabled, then the Subversion derivation is not dependent on
|
||||
OpenSSL, even if a non-<literal>null</literal> value was passed.
|
||||
This prevents an unnecessary rebuild of Subversion if OpenSSL
|
||||
changes.</para>
|
||||
</callout>
|
||||
</listitem>
|
||||
|
||||
</calloutlist>
|
||||
</orderedlist>
|
||||
|
||||
</simplesect>
|
||||
|
||||
|
|
|
@ -1,14 +1,21 @@
|
|||
# Arguments and Variables
|
||||
|
||||
The Nix expression in [???](#ex-hello-nix) is a function; it is missing
|
||||
some arguments that have to be filled in somewhere. In the Nix Packages
|
||||
collection this is done in the file `pkgs/top-level/all-packages.nix`,
|
||||
where all Nix expressions for packages are imported and called with the
|
||||
appropriate arguments. Here are some fragments of `all-packages.nix`,
|
||||
with annotations of what they mean:
|
||||
|
||||
...
|
||||
|
||||
rec {
|
||||
rec { ①
|
||||
|
||||
hello = import ../applications/misc/hello/ex-1 {
|
||||
hello = import ../applications/misc/hello/ex-1 ② { ③
|
||||
inherit fetchurl stdenv perl;
|
||||
};
|
||||
|
||||
perl = import ../development/interpreters/perl {
|
||||
perl = import ../development/interpreters/perl { ④
|
||||
inherit fetchurl stdenv;
|
||||
};
|
||||
|
||||
|
@ -20,20 +27,13 @@
|
|||
|
||||
}
|
||||
|
||||
The Nix expression in [???](#ex-hello-nix) is a function; it is missing
|
||||
some arguments that have to be filled in somewhere. In the Nix Packages
|
||||
collection this is done in the file `pkgs/top-level/all-packages.nix`,
|
||||
where all Nix expressions for packages are imported and called with the
|
||||
appropriate arguments. [example\_title](#ex-hello-composition) shows
|
||||
some fragments of `all-packages.nix`.
|
||||
|
||||
- This file defines a set of attributes, all of which are concrete
|
||||
1. This file defines a set of attributes, all of which are concrete
|
||||
derivations (i.e., not functions). In fact, we define a *mutually
|
||||
recursive* set of attributes. That is, the attributes can refer to
|
||||
each other. This is precisely what we want since we want to “plug”
|
||||
the various packages into each other.
|
||||
|
||||
- Here we *import* the Nix expression for GNU Hello. The import
|
||||
2. Here we *import* the Nix expression for GNU Hello. The import
|
||||
operation just loads and returns the specified Nix expression. In
|
||||
fact, we could just have put the contents of [???](#ex-hello-nix) in
|
||||
`all-packages.nix` at this point. That would be completely
|
||||
|
@ -44,7 +44,7 @@ some fragments of `all-packages.nix`.
|
|||
import a directory, Nix automatically appends `/default.nix` to the
|
||||
file name.
|
||||
|
||||
- This is where the actual composition takes place. Here we *call* the
|
||||
3. This is where the actual composition takes place. Here we *call* the
|
||||
function imported from `../applications/misc/hello/ex-1` with a set
|
||||
containing the things that the function expects, namely `fetchurl`,
|
||||
`stdenv`, and `perl`. We use inherit again to use the attributes
|
||||
|
@ -68,5 +68,5 @@ some fragments of `all-packages.nix`.
|
|||
>
|
||||
> hello = callPackage ../applications/misc/hello/ex-1 { stdenv = myStdenv; };
|
||||
|
||||
- Likewise, we have to instantiate Perl, `fetchurl`, and the standard
|
||||
4. Likewise, we have to instantiate Perl, `fetchurl`, and the standard
|
||||
environment.
|
||||
|
|
|
@ -1,23 +1,23 @@
|
|||
# Build Script
|
||||
|
||||
source $stdenv/setup
|
||||
Here is the builder referenced from Hello's Nix expression (stored in
|
||||
`pkgs/applications/misc/hello/ex-1/builder.sh`):
|
||||
|
||||
source $stdenv/setup ①
|
||||
|
||||
PATH=$perl/bin:$PATH
|
||||
PATH=$perl/bin:$PATH ②
|
||||
|
||||
tar xvfz $src
|
||||
tar xvfz $src ③
|
||||
cd hello-*
|
||||
./configure --prefix=$out
|
||||
make
|
||||
./configure --prefix=$out ④
|
||||
make ⑤
|
||||
make install
|
||||
|
||||
[example\_title](#ex-hello-builder) shows the builder referenced from
|
||||
Hello's Nix expression (stored in
|
||||
`pkgs/applications/misc/hello/ex-1/builder.sh`). The builder can
|
||||
actually be made a lot shorter by using the *generic builder* functions
|
||||
provided by `stdenv`, but here we write out the build steps to elucidate
|
||||
what a builder does. It performs the following steps:
|
||||
The builder can actually be made a lot shorter by using the *generic
|
||||
builder* functions provided by `stdenv`, but here we write out the build
|
||||
steps to elucidate what a builder does. It performs the following steps:
|
||||
|
||||
- When Nix runs a builder, it initially completely clears the
|
||||
1. When Nix runs a builder, it initially completely clears the
|
||||
environment (except for the attributes declared in the derivation).
|
||||
For instance, the `PATH` variable is empty\[1\]. This is done to
|
||||
prevent undeclared inputs from being used in the build process. If
|
||||
|
@ -31,13 +31,13 @@ what a builder does. It performs the following steps:
|
|||
attribute in [???](#ex-hello-nix), but `mkDerivation` adds it
|
||||
automatically.)
|
||||
|
||||
- Since Hello needs Perl, we have to make sure that Perl is in the
|
||||
2. Since Hello needs Perl, we have to make sure that Perl is in the
|
||||
`PATH`. The `perl` environment variable points to the location of
|
||||
the Perl package (since it was passed in as an attribute to the
|
||||
derivation), so `$perl/bin` is the directory containing the Perl
|
||||
interpreter.
|
||||
|
||||
- Now we have to unpack the sources. The `src` attribute was bound to
|
||||
3. Now we have to unpack the sources. The `src` attribute was bound to
|
||||
the result of fetching the Hello source tarball from the network, so
|
||||
the `src` environment variable points to the location in the Nix
|
||||
store to which the tarball was downloaded. After unpacking, we `cd`
|
||||
|
@ -50,7 +50,7 @@ what a builder does. It performs the following steps:
|
|||
have to worry about files from previous builds interfering with the
|
||||
current build.
|
||||
|
||||
- GNU Hello is a typical Autoconf-based package, so we first have to
|
||||
4. GNU Hello is a typical Autoconf-based package, so we first have to
|
||||
run its `configure` script. In Nix every package is stored in a
|
||||
separate location in the Nix store, for instance
|
||||
`/nix/store/9a54ba97fb71b65fda531012d0443ce2-hello-2.1.1`. Nix
|
||||
|
@ -60,7 +60,7 @@ what a builder does. It performs the following steps:
|
|||
`--prefix=$out` to cause Hello to be installed in the expected
|
||||
location.
|
||||
|
||||
- Finally we build Hello (`make`) and install it into the location
|
||||
5. Finally we build Hello (`make`) and install it into the location
|
||||
specified by `out` (`make install`).
|
||||
|
||||
If you are wondering about the absence of error checking on the result
|
||||
|
|
|
@ -735,31 +735,7 @@ For instance, `derivation` is also available as `builtins.derivation`.
|
|||
builder in a more structured format than plain environment
|
||||
variables.
|
||||
|
||||
[example\_title](#ex-toxml) shows an example where this is the case.
|
||||
The builder is supposed to generate the configuration file for a
|
||||
[Jetty servlet container](http://jetty.mortbay.org/). A servlet
|
||||
container contains a number of servlets (`*.war` files) each
|
||||
exported under a specific URI prefix. So the servlet configuration
|
||||
is a list of sets containing the `path` and `war` of the servlet
|
||||
([co\_title](#ex-toxml-co-servlets)). This kind of information is
|
||||
difficult to communicate with the normal method of passing
|
||||
information through an environment variable, which just concatenates
|
||||
everything together into a string (which might just work in this
|
||||
case, but wouldn’t work if fields are optional or contain lists
|
||||
themselves). Instead the Nix expression is converted to an XML
|
||||
representation with `toXML`, which is unambiguous and can easily be
|
||||
processed with the appropriate tools. For instance, in the example
|
||||
an XSLT stylesheet ([co\_title](#ex-toxml-co-stylesheet)) is applied
|
||||
to it ([co\_title](#ex-toxml-co-apply)) to generate the XML
|
||||
configuration file for the Jetty server. The XML representation
|
||||
produced from [co\_title](#ex-toxml-co-servlets) by `toXML` is shown
|
||||
in [example\_title](#ex-toxml-result).
|
||||
|
||||
Note that [example\_title](#ex-toxml) uses the `toFile` built-in to
|
||||
write the builder and the stylesheet “inline” in the Nix expression.
|
||||
The path of the stylesheet is spliced into the builder at `xsltproc
|
||||
${stylesheet}
|
||||
...`.
|
||||
Here is an example where this is the case:
|
||||
|
||||
{ stdenv, fetchurl, libxslt, jira, uberwiki }:
|
||||
|
||||
|
@ -771,10 +747,10 @@ For instance, `derivation` is also available as `builtins.derivation`.
|
|||
builder = builtins.toFile "builder.sh" "
|
||||
source $stdenv/setup
|
||||
mkdir $out
|
||||
echo "$servlets" | xsltproc ${stylesheet} - > $out/server-conf.xml
|
||||
echo "$servlets" | xsltproc ${stylesheet} - > $out/server-conf.xml ①
|
||||
";
|
||||
|
||||
stylesheet = builtins.toFile "stylesheet.xsl"
|
||||
stylesheet = builtins.toFile "stylesheet.xsl" ②
|
||||
"<?xml version='1.0' encoding='UTF-8'?>
|
||||
<xsl:stylesheet xmlns:xsl='http://www.w3.org/1999/XSL/Transform' version='1.0'>
|
||||
<xsl:template match='/'>
|
||||
|
@ -790,12 +766,29 @@ For instance, `derivation` is also available as `builtins.derivation`.
|
|||
</xsl:stylesheet>
|
||||
";
|
||||
|
||||
servlets = builtins.toXML [
|
||||
servlets = builtins.toXML [ ③
|
||||
{ path = "/bugtracker"; war = jira + "/lib/atlassian-jira.war"; }
|
||||
{ path = "/wiki"; war = uberwiki + "/uberwiki.war"; }
|
||||
];
|
||||
})
|
||||
|
||||
The builder is supposed to generate the configuration file for a
|
||||
[Jetty servlet container](http://jetty.mortbay.org/). A servlet
|
||||
container contains a number of servlets (`*.war` files) each
|
||||
exported under a specific URI prefix. So the servlet configuration
|
||||
is a list of sets containing the `path` and `war` of the servlet
|
||||
([???](#ex-toxml-co-servlets)). This kind of information is
|
||||
difficult to communicate with the normal method of passing
|
||||
information through an environment variable, which just concatenates
|
||||
everything together into a string (which might just work in this
|
||||
case, but wouldn’t work if fields are optional or contain lists
|
||||
themselves). Instead the Nix expression is converted to an XML
|
||||
representation with `toXML`, which is unambiguous and can easily be
|
||||
processed with the appropriate tools. For instance, in the example
|
||||
an XSLT stylesheet (at point ②) is applied to it (at point ①) to
|
||||
generate the XML configuration file for the Jetty server. The XML
|
||||
representation produced at point ③ by `toXML` is as follows:
|
||||
|
||||
<?xml version='1.0' encoding='utf-8'?>
|
||||
<expr>
|
||||
<list>
|
||||
|
@ -817,6 +810,11 @@ For instance, `derivation` is also available as `builtins.derivation`.
|
|||
</attrs>
|
||||
</list>
|
||||
</expr>
|
||||
|
||||
Note that [???](#ex-toxml) uses the `toFile` built-in to write the
|
||||
builder and the stylesheet “inline” in the Nix expression. The path
|
||||
of the stylesheet is spliced into the builder using the syntax
|
||||
`xsltproc ${stylesheet}`.
|
||||
|
||||
- `builtins.trace` e1 e2
|
||||
Evaluate e1 and print its abstract syntax representation on standard
|
||||
|
|
|
@ -1,25 +1,26 @@
|
|||
# Expression Syntax
|
||||
|
||||
{ stdenv, fetchurl, perl }:
|
||||
Here is a Nix expression for GNU Hello:
|
||||
|
||||
{ stdenv, fetchurl, perl }: ①
|
||||
|
||||
stdenv.mkDerivation {
|
||||
name = "hello-2.1.1";
|
||||
builder = ./builder.sh;
|
||||
src = fetchurl {
|
||||
stdenv.mkDerivation { ②
|
||||
name = "hello-2.1.1"; ③
|
||||
builder = ./builder.sh; ④
|
||||
src = fetchurl { ⑤
|
||||
url = "ftp://ftp.nluug.nl/pub/gnu/hello/hello-2.1.1.tar.gz";
|
||||
sha256 = "1md7jsfd8pa45z73bz1kszpp01yw6x5ljkjk2hx7wl800any6465";
|
||||
};
|
||||
inherit perl;
|
||||
inherit perl; ⑥
|
||||
}
|
||||
|
||||
[example\_title](#ex-hello-nix) shows a Nix expression for GNU Hello.
|
||||
It's actually already in the Nix Packages collection in
|
||||
This file is actually already in the Nix Packages collection in
|
||||
`pkgs/applications/misc/hello/ex-1/default.nix`. It is customary to
|
||||
place each package in a separate directory and call the single Nix
|
||||
expression in that directory `default.nix`. The file has the following
|
||||
elements (referenced from the figure by number):
|
||||
|
||||
- This states that the expression is a *function* that expects to be
|
||||
1. This states that the expression is a *function* that expects to be
|
||||
called with three arguments: `stdenv`, `fetchurl`, and `perl`. They
|
||||
are needed to build Hello, but we don't know how to build them here;
|
||||
that's why they are function arguments. `stdenv` is a package that
|
||||
|
@ -37,7 +38,7 @@ elements (referenced from the figure by number):
|
|||
the required arguments, the body should describe how to build an
|
||||
instance of the Hello package.
|
||||
|
||||
- So we have to build a package. Building something from other stuff
|
||||
2. So we have to build a package. Building something from other stuff
|
||||
is called a *derivation* in Nix (as opposed to sources, which are
|
||||
built by humans instead of computers). We perform a derivation by
|
||||
calling `stdenv.mkDerivation`. `mkDerivation` is a function provided
|
||||
|
@ -50,13 +51,13 @@ elements (referenced from the figure by number):
|
|||
nameN =
|
||||
exprN; }`.
|
||||
|
||||
- The attribute `name` specifies the symbolic name and version of the
|
||||
3. The attribute `name` specifies the symbolic name and version of the
|
||||
package. Nix doesn't really care about these things, but they are
|
||||
used by for instance `nix-env
|
||||
-q` to show a “human-readable” name for packages. This attribute is
|
||||
required by `mkDerivation`.
|
||||
|
||||
- The attribute `builder` specifies the builder. This attribute can
|
||||
4. The attribute `builder` specifies the builder. This attribute can
|
||||
sometimes be omitted, in which case `mkDerivation` will fill in a
|
||||
default builder (which does a `configure; make; make install`, in
|
||||
essence). Hello is sufficiently simple that the default builder
|
||||
|
@ -64,7 +65,7 @@ elements (referenced from the figure by number):
|
|||
educational purposes. The value `./builder.sh` refers to the shell
|
||||
script shown in [???](#ex-hello-builder), discussed below.
|
||||
|
||||
- The builder has to know what the sources of the package are. Here,
|
||||
5. The builder has to know what the sources of the package are. Here,
|
||||
the attribute `src` is bound to the result of a call to the
|
||||
`fetchurl` function. Given a URL and a SHA-256 hash of the expected
|
||||
contents of the file at that URL, this function builds a derivation
|
||||
|
@ -77,7 +78,7 @@ elements (referenced from the figure by number):
|
|||
However, `src` is customary, and it's also expected by the default
|
||||
builder (which we don't use in this example).
|
||||
|
||||
- Since the derivation requires Perl, we have to pass the value of the
|
||||
6. Since the derivation requires Perl, we have to pass the value of the
|
||||
`perl` function argument to the builder. All attributes in the set
|
||||
are actually passed as environment variables to the builder, so
|
||||
declaring an attribute
|
||||
|
|
|
@ -13,24 +13,26 @@ like this:
|
|||
The builders for almost all Unix packages look like this — set up some
|
||||
environment variables, unpack the sources, configure, build, and
|
||||
install. For this reason the standard environment provides some Bash
|
||||
functions that automate the build process. A builder using the generic
|
||||
build facilities in shown in [example\_title](#ex-hello-builder2).
|
||||
functions that automate the build process. Here is what a builder using
|
||||
the generic build facilities looks like:
|
||||
|
||||
buildInputs="$perl"
|
||||
buildInputs="$perl" ①
|
||||
|
||||
source $stdenv/setup
|
||||
source $stdenv/setup ②
|
||||
|
||||
genericBuild
|
||||
genericBuild ③
|
||||
|
||||
- The `buildInputs` variable tells `setup` to use the indicated
|
||||
Here is what each line means:
|
||||
|
||||
1. The `buildInputs` variable tells `setup` to use the indicated
|
||||
packages as “inputs”. This means that if a package provides a `bin`
|
||||
subdirectory, it's added to `PATH`; if it has a `include`
|
||||
subdirectory, it's added to GCC's header search path; and so
|
||||
on.\[1\]
|
||||
|
||||
- The function `genericBuild` is defined in the file `$stdenv/setup`.
|
||||
2. The function `genericBuild` is defined in the file `$stdenv/setup`.
|
||||
|
||||
- The final step calls the shell function `genericBuild`, which
|
||||
3. The final step calls the shell function `genericBuild`, which
|
||||
performs the steps that were done explicitly in
|
||||
[???](#ex-hello-builder). The generic builder is smart enough to
|
||||
figure out whether to unpack the sources using `gzip`, `bzip2`, etc.
|
||||
|
|
|
@ -203,6 +203,9 @@ where e1 is an expression that should evaluate to a Boolean value. If it
|
|||
evaluates to `true`, e2 is returned; otherwise expression evaluation is
|
||||
aborted and a backtrace is printed.
|
||||
|
||||
Here is a Nix expression for the Subversion package that shows how
|
||||
assertions can be used:.
|
||||
|
||||
{ localServer ? false
|
||||
, httpServer ? false
|
||||
, sslSupport ? false
|
||||
|
@ -213,9 +216,9 @@ aborted and a backtrace is printed.
|
|||
, openssl ? null, httpd ? null, db4 ? null, expat, swig ? null, j2sdk ? null
|
||||
}:
|
||||
|
||||
assert localServer -> db4 != null;
|
||||
assert httpServer -> httpd != null && httpd.expat == expat;
|
||||
assert sslSupport -> openssl != null && (httpServer -> httpd.openssl == openssl);
|
||||
assert localServer -> db4 != null; ①
|
||||
assert httpServer -> httpd != null && httpd.expat == expat; ②
|
||||
assert sslSupport -> openssl != null && (httpServer -> httpd.openssl == openssl); ③
|
||||
assert pythonBindings -> swig != null && swig.pythonSupport;
|
||||
assert javaSwigBindings -> swig != null && swig.javaSupport;
|
||||
assert javahlBindings -> j2sdk != null;
|
||||
|
@ -223,33 +226,32 @@ aborted and a backtrace is printed.
|
|||
stdenv.mkDerivation {
|
||||
name = "subversion-1.1.1";
|
||||
...
|
||||
openssl = if sslSupport then openssl else null;
|
||||
openssl = if sslSupport then openssl else null; ④
|
||||
...
|
||||
}
|
||||
|
||||
[example\_title](#ex-subversion-nix) show how assertions are used in the
|
||||
Nix expression for Subversion.
|
||||
The points of interest are:
|
||||
|
||||
- This assertion states that if Subversion is to have support for
|
||||
1. This assertion states that if Subversion is to have support for
|
||||
local repositories, then Berkeley DB is needed. So if the Subversion
|
||||
function is called with the `localServer` argument set to `true` but
|
||||
the `db4` argument set to `null`, then the evaluation fails.
|
||||
|
||||
- This is a more subtle condition: if Subversion is built with Apache
|
||||
2. This is a more subtle condition: if Subversion is built with Apache
|
||||
(`httpServer`) support, then the Expat library (an XML library) used
|
||||
by Subversion should be same as the one used by Apache. This is
|
||||
because in this configuration Subversion code ends up being linked
|
||||
with Apache code, and if the Expat libraries do not match, a build-
|
||||
or runtime link error or incompatibility might occur.
|
||||
|
||||
- This assertion says that in order for Subversion to have SSL support
|
||||
3. This assertion says that in order for Subversion to have SSL support
|
||||
(so that it can access `https` URLs), an OpenSSL library must be
|
||||
passed. Additionally, it says that *if* Apache support is enabled,
|
||||
then Apache's OpenSSL should match Subversion's. (Note that if
|
||||
Apache support is not enabled, we don't care about Apache's
|
||||
OpenSSL.)
|
||||
|
||||
- The conditional here is not really related to assertions, but is
|
||||
4. The conditional here is not really related to assertions, but is
|
||||
worth pointing out: it ensures that if SSL support is disabled, then
|
||||
the Subversion derivation is not dependent on OpenSSL, even if a
|
||||
non-`null` value was passed. This prevents an unnecessary rebuild of
|
||||
|
|
Loading…
Reference in a new issue