primops: Move toXML to primops/toXML.cc

Change-Id: I8da29345903015e38b9832f0d9b4011dc5bbb17b
This commit is contained in:
Tom Hubrecht 2024-05-30 10:14:49 +02:00
parent a93af3f92f
commit c96f5bcdd2
3 changed files with 116 additions and 104 deletions

View file

@ -103,6 +103,7 @@ libexpr_sources = files(
'primops/path.cc', 'primops/path.cc',
'primops/string.cc', 'primops/string.cc',
'primops/system.cc', 'primops/system.cc',
'primops/toXML.cc',
'primops/types.cc', 'primops/types.cc',
'value/context.cc', 'value/context.cc',
) )

View file

@ -186,110 +186,6 @@ static void derivationStrictInternal(EvalState & state, const std::string & name
/* Convert the argument (which can be any Nix expression) to an XML /* Convert the argument (which can be any Nix expression) to an XML
representation returned in a string. Not all Nix expressions can representation returned in a string. Not all Nix expressions can
be sensibly or completely represented (e.g., functions). */ be sensibly or completely represented (e.g., functions). */
static void prim_toXML(EvalState & state, const PosIdx pos, Value * * args, Value & v)
{
std::ostringstream out;
NixStringContext context;
printValueAsXML(state, true, false, *args[0], out, context, pos);
v.mkString(out.str(), context);
}
static RegisterPrimOp primop_toXML({
.name = "__toXML",
.args = {"e"},
.doc = R"(
Return a string containing an XML representation of *e*. The main
application for `toXML` is to communicate information with the
builder in a more structured format than plain environment
variables.
Here is an example where this is the case:
```nix
{ stdenv, fetchurl, libxslt, jira, uberwiki }:
stdenv.mkDerivation (rec {
name = "web-server";
buildInputs = [ libxslt ];
builder = builtins.toFile "builder.sh" "
source $stdenv/setup
mkdir $out
echo "$servlets" | xsltproc ${stylesheet} - > $out/server-conf.xml
";
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='/'>
<Configure>
<xsl:for-each select='/expr/list/attrs'>
<Call name='addWebApplication'>
<Arg><xsl:value-of select=\"attr[@name = 'path']/string/@value\" /></Arg>
<Arg><xsl:value-of select=\"attr[@name = 'war']/path/@value\" /></Arg>
</Call>
</xsl:for-each>
</Configure>
</xsl:template>
</xsl:stylesheet>
";
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
(). 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 wouldnt 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
<?xml version='1.0' encoding='utf-8'?>
<expr>
<list>
<attrs>
<attr name="path">
<string value="/bugtracker" />
</attr>
<attr name="war">
<path value="/nix/store/d1jh9pasa7k2...-jira/lib/atlassian-jira.war" />
</attr>
</attrs>
<attrs>
<attr name="path">
<string value="/wiki" />
</attr>
<attr name="war">
<path value="/nix/store/y6423b1yi4sx...-uberwiki/uberwiki.war" />
</attr>
</attrs>
</list>
</expr>
```
Note that we used 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}`.
)",
.fun = prim_toXML,
});
/* Store a string in the Nix store as a source file that can be used /* Store a string in the Nix store as a source file that can be used

View file

@ -0,0 +1,115 @@
#include "primops.hh"
#include "value-to-xml.hh"
namespace nix {
/**
* builtins.toXML
*/
static void prim_toXML(EvalState & state, const PosIdx pos, Value ** args, Value & v)
{
std::ostringstream out;
NixStringContext context;
printValueAsXML(state, true, false, *args[0], out, context, pos);
v.mkString(out.str(), context);
}
static RegisterPrimOp primop_toXML({
.name = "__toXML",
.args = {"e"},
.doc = R"(
Return a string containing an XML representation of *e*. The main
application for `toXML` is to communicate information with the
builder in a more structured format than plain environment
variables.
Here is an example where this is the case:
```nix
{ stdenv, fetchurl, libxslt, jira, uberwiki }:
stdenv.mkDerivation (rec {
name = "web-server";
buildInputs = [ libxslt ];
builder = builtins.toFile "builder.sh" "
source $stdenv/setup
mkdir $out
echo "$servlets" | xsltproc ${stylesheet} - > $out/server-conf.xml
";
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='/'>
<Configure>
<xsl:for-each select='/expr/list/attrs'>
<Call name='addWebApplication'>
<Arg><xsl:value-of select=\"attr[@name = 'path']/string/@value\" /></Arg>
<Arg><xsl:value-of select=\"attr[@name = 'war']/path/@value\" /></Arg>
</Call>
</xsl:for-each>
</Configure>
</xsl:template>
</xsl:stylesheet>
";
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
(). 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 wouldnt 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
<?xml version='1.0' encoding='utf-8'?>
<expr>
<list>
<attrs>
<attr name="path">
<string value="/bugtracker" />
</attr>
<attr name="war">
<path value="/nix/store/d1jh9pasa7k2...-jira/lib/atlassian-jira.war" />
</attr>
</attrs>
<attrs>
<attr name="path">
<string value="/wiki" />
</attr>
<attr name="war">
<path value="/nix/store/y6423b1yi4sx...-uberwiki/uberwiki.war" />
</attr>
</attrs>
</list>
</expr>
```
Note that we used 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}`.
)",
.fun = prim_toXML,
});
}