From 7ed0229d1abd4414144c7af396842462ce6fc1eb Mon Sep 17 00:00:00 2001 From: Sergei Zimmerman Date: Tue, 12 Aug 2025 14:22:30 +0300 Subject: [PATCH 1/6] tests/functional/lang: Add more tests for TOML timestamps Current test suite doesn't cover the subsecond formatting at all and toml11 is quite finicky with that. We should at the very least test its behavior to avoid silent breakages on updates. --- .../lang/eval-okay-fromTOML-timestamps.exp | 2 +- .../lang/eval-okay-fromTOML-timestamps.nix | 46 ++++++++++++++++++- 2 files changed, 45 insertions(+), 3 deletions(-) diff --git a/tests/functional/lang/eval-okay-fromTOML-timestamps.exp b/tests/functional/lang/eval-okay-fromTOML-timestamps.exp index 08b3c69a6..56e610533 100644 --- a/tests/functional/lang/eval-okay-fromTOML-timestamps.exp +++ b/tests/functional/lang/eval-okay-fromTOML-timestamps.exp @@ -1 +1 @@ -{ "1234" = "value"; "127.0.0.1" = "value"; a = { b = { c = { }; }; }; arr1 = [ 1 2 3 ]; arr2 = [ "red" "yellow" "green" ]; arr3 = [ [ 1 2 ] [ 3 4 5 ] ]; arr4 = [ "all" "strings" "are the same" "type" ]; arr5 = [ [ 1 2 ] [ "a" "b" "c" ] ]; arr7 = [ 1 2 3 ]; arr8 = [ 1 2 ]; bare-key = "value"; bare_key = "value"; bin1 = 214; bool1 = true; bool2 = false; "character encoding" = "value"; d = { e = { f = { }; }; }; dog = { "tater.man" = { type = { name = "pug"; }; }; }; flt1 = 1; flt2 = 3.1415; flt3 = -0.01; flt4 = 5e+22; flt5 = 1e+06; flt6 = -0.02; flt7 = 6.626e-34; flt8 = 9.22462e+06; fruit = [ { name = "apple"; physical = { color = "red"; shape = "round"; }; variety = [ { name = "red delicious"; } { name = "granny smith"; } ]; } { name = "banana"; variety = [ { name = "plantain"; } ]; } ]; g = { h = { i = { }; }; }; hex1 = 3735928559; hex2 = 3735928559; hex3 = 3735928559; int1 = 99; int2 = 42; int3 = 0; int4 = -17; int5 = 1000; int6 = 5349221; int7 = 12345; j = { "ʞ" = { l = { }; }; }; key = "value"; key2 = "value"; ld1 = { _type = "timestamp"; value = "1979-05-27"; }; ldt1 = { _type = "timestamp"; value = "1979-05-27T07:32:00"; }; ldt2 = { _type = "timestamp"; value = "1979-05-27T00:32:00.999999"; }; lt1 = { _type = "timestamp"; value = "07:32:00"; }; lt2 = { _type = "timestamp"; value = "00:32:00.999999"; }; name = "Orange"; oct1 = 342391; oct2 = 493; odt1 = { _type = "timestamp"; value = "1979-05-27T07:32:00Z"; }; odt2 = { _type = "timestamp"; value = "1979-05-27T00:32:00-07:00"; }; odt3 = { _type = "timestamp"; value = "1979-05-27T00:32:00.999999-07:00"; }; odt4 = { _type = "timestamp"; value = "1979-05-27T07:32:00Z"; }; physical = { color = "orange"; shape = "round"; }; products = [ { name = "Hammer"; sku = 738594937; } { } { color = "gray"; name = "Nail"; sku = 284758393; } ]; "quoted \"value\"" = "value"; site = { "google.com" = true; }; str = "I'm a string. \"You can quote me\". Name\tJosé\nLocation\tSF."; table-1 = { key1 = "some string"; key2 = 123; }; table-2 = { key1 = "another string"; key2 = 456; }; x = { y = { z = { w = { animal = { type = { name = "pug"; }; }; name = { first = "Tom"; last = "Preston-Werner"; }; point = { x = 1; y = 2; }; }; }; }; }; "ʎǝʞ" = "value"; } +{ "1234" = "value"; "127.0.0.1" = "value"; a = { b = { c = { }; }; }; arr1 = [ 1 2 3 ]; arr2 = [ "red" "yellow" "green" ]; arr3 = [ [ 1 2 ] [ 3 4 5 ] ]; arr4 = [ "all" "strings" "are the same" "type" ]; arr5 = [ [ 1 2 ] [ "a" "b" "c" ] ]; arr7 = [ 1 2 3 ]; arr8 = [ 1 2 ]; bare-key = "value"; bare_key = "value"; bin1 = 214; bool1 = true; bool2 = false; "character encoding" = "value"; d = { e = { f = { }; }; }; dog = { "tater.man" = { type = { name = "pug"; }; }; }; flt1 = 1; flt2 = 3.1415; flt3 = -0.01; flt4 = 5e+22; flt5 = 1e+06; flt6 = -0.02; flt7 = 6.626e-34; flt8 = 9.22462e+06; fruit = [ { name = "apple"; physical = { color = "red"; shape = "round"; }; variety = [ { name = "red delicious"; } { name = "granny smith"; } ]; } { name = "banana"; variety = [ { name = "plantain"; } ]; } ]; g = { h = { i = { }; }; }; hex1 = 3735928559; hex2 = 3735928559; hex3 = 3735928559; int1 = 99; int2 = 42; int3 = 0; int4 = -17; int5 = 1000; int6 = 5349221; int7 = 12345; j = { "ʞ" = { l = { }; }; }; key = "value"; key2 = "value"; ld1 = { _type = "timestamp"; value = "1979-05-27"; }; ldt1 = { _type = "timestamp"; value = "1979-05-27T07:32:00"; }; ldt10 = { _type = "timestamp"; value = "1979-05-27T00:32:00.123456789"; }; ldt11 = { _type = "timestamp"; value = "1979-05-27T00:32:00.123456789"; }; ldt2 = { _type = "timestamp"; value = "1979-05-27T07:32:00.100"; }; ldt3 = { _type = "timestamp"; value = "1979-05-27T07:32:00.120"; }; ldt4 = { _type = "timestamp"; value = "1979-05-27T07:32:00.123"; }; ldt5 = { _type = "timestamp"; value = "1979-05-27T00:32:00.123400"; }; ldt6 = { _type = "timestamp"; value = "1979-05-27T00:32:00.123450"; }; ldt7 = { _type = "timestamp"; value = "1979-05-27T00:32:00.123456"; }; ldt8 = { _type = "timestamp"; value = "1979-05-27T00:32:00.123456700"; }; ldt9 = { _type = "timestamp"; value = "1979-05-27T00:32:00.123456780"; }; lt1 = { _type = "timestamp"; value = "07:32:00"; }; lt10 = { _type = "timestamp"; value = "00:32:00.123456789"; }; lt11 = { _type = "timestamp"; value = "00:32:00.123456789"; }; lt2 = { _type = "timestamp"; value = "00:32:00.100"; }; lt3 = { _type = "timestamp"; value = "00:32:00.120"; }; lt4 = { _type = "timestamp"; value = "00:32:00.123"; }; lt5 = { _type = "timestamp"; value = "00:32:00.123400"; }; lt6 = { _type = "timestamp"; value = "00:32:00.123450"; }; lt7 = { _type = "timestamp"; value = "00:32:00.123456"; }; lt8 = { _type = "timestamp"; value = "00:32:00.123456700"; }; lt9 = { _type = "timestamp"; value = "00:32:00.123456780"; }; name = "Orange"; oct1 = 342391; oct2 = 493; odt1 = { _type = "timestamp"; value = "1979-05-27T07:32:00Z"; }; odt10 = { _type = "timestamp"; value = "1979-05-27T07:32:00.123456Z"; }; odt11 = { _type = "timestamp"; value = "1979-05-27T07:32:00.123456700Z"; }; odt12 = { _type = "timestamp"; value = "1979-05-27T07:32:00.123456780Z"; }; odt13 = { _type = "timestamp"; value = "1979-05-27T07:32:00.123456789Z"; }; odt14 = { _type = "timestamp"; value = "1979-05-27T07:32:00.123456789Z"; }; odt2 = { _type = "timestamp"; value = "1979-05-27T00:32:00-07:00"; }; odt3 = { _type = "timestamp"; value = "1979-05-27T00:32:00.999999-07:00"; }; odt4 = { _type = "timestamp"; value = "1979-05-27T07:32:00Z"; }; odt5 = { _type = "timestamp"; value = "1979-05-27T07:32:00.100Z"; }; odt6 = { _type = "timestamp"; value = "1979-05-27T07:32:00.120Z"; }; odt7 = { _type = "timestamp"; value = "1979-05-27T07:32:00.123Z"; }; odt8 = { _type = "timestamp"; value = "1979-05-27T07:32:00.123400Z"; }; odt9 = { _type = "timestamp"; value = "1979-05-27T07:32:00.123450Z"; }; physical = { color = "orange"; shape = "round"; }; products = [ { name = "Hammer"; sku = 738594937; } { } { color = "gray"; name = "Nail"; sku = 284758393; } ]; "quoted \"value\"" = "value"; site = { "google.com" = true; }; str = "I'm a string. \"You can quote me\". Name\tJosé\nLocation\tSF."; table-1 = { key1 = "some string"; key2 = 123; }; table-2 = { key1 = "another string"; key2 = 456; }; x = { y = { z = { w = { animal = { type = { name = "pug"; }; }; name = { first = "Tom"; last = "Preston-Werner"; }; point = { x = 1; y = 2; }; }; }; }; }; "ʎǝʞ" = "value"; } diff --git a/tests/functional/lang/eval-okay-fromTOML-timestamps.nix b/tests/functional/lang/eval-okay-fromTOML-timestamps.nix index 74cff9470..d8f3a03e9 100644 --- a/tests/functional/lang/eval-okay-fromTOML-timestamps.nix +++ b/tests/functional/lang/eval-okay-fromTOML-timestamps.nix @@ -55,11 +55,53 @@ builtins.fromTOML '' odt2 = 1979-05-27T00:32:00-07:00 odt3 = 1979-05-27T00:32:00.999999-07:00 odt4 = 1979-05-27 07:32:00Z + # milliseconds + odt5 = 1979-05-27 07:32:00.1Z + odt6 = 1979-05-27 07:32:00.12Z + odt7 = 1979-05-27 07:32:00.123Z + # microseconds + odt8 = 1979-05-27t07:32:00.1234Z + odt9 = 1979-05-27t07:32:00.12345Z + odt10 = 1979-05-27t07:32:00.123456Z + # nanoseconds + odt11 = 1979-05-27 07:32:00.1234567Z + odt12 = 1979-05-27 07:32:00.12345678Z + odt13 = 1979-05-27 07:32:00.123456789Z + # no more precision after nanoseconds + odt14 = 1979-05-27t07:32:00.1234567891Z + ldt1 = 1979-05-27T07:32:00 - ldt2 = 1979-05-27T00:32:00.999999 + # milliseconds + ldt2 = 1979-05-27T07:32:00.1 + ldt3 = 1979-05-27T07:32:00.12 + ldt4 = 1979-05-27T07:32:00.123 + # microseconds + ldt5 = 1979-05-27t00:32:00.1234 + ldt6 = 1979-05-27t00:32:00.12345 + ldt7 = 1979-05-27t00:32:00.123456 + # nanoseconds + ldt8 = 1979-05-27 00:32:00.1234567 + ldt9 = 1979-05-27 00:32:00.12345678 + ldt10 = 1979-05-27 00:32:00.123456789 + # no more precision after nanoseconds + ldt11 = 1979-05-27t00:32:00.1234567891 + ld1 = 1979-05-27 lt1 = 07:32:00 - lt2 = 00:32:00.999999 + # milliseconds + lt2 = 00:32:00.1 + lt3 = 00:32:00.12 + lt4 = 00:32:00.123 + # microseconds + lt5 = 00:32:00.1234 + lt6 = 00:32:00.12345 + lt7 = 00:32:00.123456 + # nanoseconds + lt8 = 00:32:00.1234567 + lt9 = 00:32:00.12345678 + lt10 = 00:32:00.123456789 + # no more precision after nanoseconds + lt11 = 00:32:00.1234567891 arr1 = [ 1, 2, 3 ] arr2 = [ "red", "yellow", "green" ] From df4e55ffc13c413e270af134227115a20a2341ba Mon Sep 17 00:00:00 2001 From: Sergei Zimmerman Date: Tue, 12 Aug 2025 14:31:34 +0300 Subject: [PATCH 2/6] libexpr: Remove extra trailing semicolons (NFC) This looks really weird after the reformat. --- src/libexpr/primops/fromTOML.cc | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/libexpr/primops/fromTOML.cc b/src/libexpr/primops/fromTOML.cc index 533739592..18a988d67 100644 --- a/src/libexpr/primops/fromTOML.cc +++ b/src/libexpr/primops/fromTOML.cc @@ -35,7 +35,6 @@ static void prim_fromTOML(EvalState & state, const PosIdx pos, Value ** args, Va v.mkAttrs(attrs); } break; - ; case toml::value_t::array: { auto array = toml::get>(t); @@ -44,25 +43,20 @@ static void prim_fromTOML(EvalState & state, const PosIdx pos, Value ** args, Va visit(*(v = state.allocValue()), array[n]); v.mkList(list); } break; - ; case toml::value_t::boolean: v.mkBool(toml::get(t)); break; - ; case toml::value_t::integer: v.mkInt(toml::get(t)); break; - ; case toml::value_t::floating: v.mkFloat(toml::get(t)); break; - ; case toml::value_t::string: { auto s = toml::get(t); forceNoNullByte(s); v.mkString(s); } break; - ; case toml::value_t::local_datetime: case toml::value_t::offset_datetime: case toml::value_t::local_date: @@ -80,11 +74,9 @@ static void prim_fromTOML(EvalState & state, const PosIdx pos, Value ** args, Va throw std::runtime_error("Dates and times are not supported"); } } break; - ; case toml::value_t::empty: v.mkNull(); break; - ; } }; From a80a5c4dba0d944fab8f5ed57a343869ae96bf16 Mon Sep 17 00:00:00 2001 From: Sergei Zimmerman Date: Tue, 12 Aug 2025 14:54:53 +0300 Subject: [PATCH 3/6] libexpr: Use recursive lambda instead of std::function There's no reason to use a std::function for recursive lambdas since there are polymorphic lambdas. --- src/libexpr/primops/fromTOML.cc | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/libexpr/primops/fromTOML.cc b/src/libexpr/primops/fromTOML.cc index 18a988d67..9ade6705e 100644 --- a/src/libexpr/primops/fromTOML.cc +++ b/src/libexpr/primops/fromTOML.cc @@ -13,9 +13,7 @@ static void prim_fromTOML(EvalState & state, const PosIdx pos, Value ** args, Va std::istringstream tomlStream(std::string{toml}); - std::function visit; - - visit = [&](Value & v, toml::value t) { + auto visit = [&](auto & self, Value & v, toml::value t) -> void { switch (t.type()) { case toml::value_t::table: { auto table = toml::get(t); @@ -30,7 +28,7 @@ static void prim_fromTOML(EvalState & state, const PosIdx pos, Value ** args, Va for (auto & elem : table) { forceNoNullByte(elem.first); - visit(attrs.alloc(elem.first), elem.second); + self(self, attrs.alloc(elem.first), elem.second); } v.mkAttrs(attrs); @@ -40,7 +38,7 @@ static void prim_fromTOML(EvalState & state, const PosIdx pos, Value ** args, Va auto list = state.buildList(array.size()); for (const auto & [n, v] : enumerate(list)) - visit(*(v = state.allocValue()), array[n]); + self(self, *(v = state.allocValue()), array[n]); v.mkList(list); } break; case toml::value_t::boolean: @@ -81,7 +79,7 @@ static void prim_fromTOML(EvalState & state, const PosIdx pos, Value ** args, Va }; try { - visit(val, toml::parse(tomlStream, "fromTOML" /* the "filename" */)); + visit(visit, val, toml::parse(tomlStream, "fromTOML" /* the "filename" */)); } catch (std::exception & e) { // TODO: toml::syntax_error state.error("while parsing TOML: %s", e.what()).atPos(pos).debugThrow(); } From d8fc55a46e0c09241131097dbf1d6fa09e0a9808 Mon Sep 17 00:00:00 2001 From: Sergei Zimmerman Date: Tue, 12 Aug 2025 14:58:01 +0300 Subject: [PATCH 4/6] libexpr: Use table.size() instead of unnecessary loop --- src/libexpr/primops/fromTOML.cc | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/src/libexpr/primops/fromTOML.cc b/src/libexpr/primops/fromTOML.cc index 9ade6705e..c6b50ce8e 100644 --- a/src/libexpr/primops/fromTOML.cc +++ b/src/libexpr/primops/fromTOML.cc @@ -17,14 +17,7 @@ static void prim_fromTOML(EvalState & state, const PosIdx pos, Value ** args, Va switch (t.type()) { case toml::value_t::table: { auto table = toml::get(t); - - size_t size = 0; - for (auto & i : table) { - (void) i; - size++; - } - - auto attrs = state.buildBindings(size); + auto attrs = state.buildBindings(table.size()); for (auto & elem : table) { forceNoNullByte(elem.first); From dc769d72cb8ad22a0f89768682b5499a9d2b3d8b Mon Sep 17 00:00:00 2001 From: Sergei Zimmerman Date: Tue, 12 Aug 2025 16:11:54 +0300 Subject: [PATCH 5/6] libexpr: Canonicalize TOML timestamps for toml11 > 4.0 This addresses several changes from toml11 4.0 bump in nixpkgs [1]. 1. Added more regression tests for timestamp formats. Special attention needs to be paid to the precision of the subsecond range for local-time. Prior versions select the closest (upwards) multiple of 3 with a hard cap of 9 digits. 2. Normalize local datetime and offset datetime to always use the uppercase separator `T`. This is actually the issue surfaced in [2]. This canonicalization is basically a requirement by (a certain reading) of rfc3339 section 5.6 [3]. 3. If using toml11 >= 4.0 also keep the old behavior wrt to the number of digits used for subsecond part of the local-time. Newer versions cap it at 6 digits unconditionally. [1]: https://www.github.com/NixOS/nixpkgs/pull/331649 [2]: https://www.github.com/NixOS/nix/issues/11441 [3]: https://datatracker.ietf.org/doc/html/rfc3339 --- src/libexpr/meson.build | 6 +++ src/libexpr/primops/fromTOML.cc | 94 ++++++++++++++++++++++++++++++++- 2 files changed, 99 insertions(+), 1 deletion(-) diff --git a/src/libexpr/meson.build b/src/libexpr/meson.build index e1a12106d..82a116708 100644 --- a/src/libexpr/meson.build +++ b/src/libexpr/meson.build @@ -71,6 +71,12 @@ toml11 = dependency( method : 'cmake', include_type : 'system', ) + +configdata_priv.set( + 'HAVE_TOML11_4', + toml11.version().version_compare('>= 4.0.0').to_int(), +) + deps_other += toml11 config_priv_h = configure_file( diff --git a/src/libexpr/primops/fromTOML.cc b/src/libexpr/primops/fromTOML.cc index c6b50ce8e..7d98a5de9 100644 --- a/src/libexpr/primops/fromTOML.cc +++ b/src/libexpr/primops/fromTOML.cc @@ -1,12 +1,91 @@ #include "nix/expr/primops.hh" #include "nix/expr/eval-inline.hh" +#include "expr-config-private.hh" + #include #include namespace nix { +#if HAVE_TOML11_4 + +/** + * This is what toml11 < 4.0 did when choosing the subsecond precision. + * TOML 1.0.0 spec doesn't define how sub-millisecond ranges should be handled and calls it + * implementation defined behavior. For a lack of a better choice we stick with what older versions + * of toml11 did [1]. + * + * [1]: https://github.com/ToruNiina/toml11/blob/dcfe39a783a94e8d52c885e5883a6fbb21529019/toml/datetime.hpp#L282 + */ +static size_t normalizeSubsecondPrecision(toml::local_time lt) +{ + auto millis = lt.millisecond; + auto micros = lt.microsecond; + auto nanos = lt.nanosecond; + if (millis != 0 || micros != 0 || nanos != 0) { + if (micros != 0 || nanos != 0) { + if (nanos != 0) + return 9; + return 6; + } + return 3; + } + return 0; +} + +/** + * Normalize date/time formats to serialize to the same strings as versions prior to toml11 4.0. + * + * Several things to consider: + * + * 1. Sub-millisecond range is represented the same way as in toml11 versions prior to 4.0. Precisioun is rounded + * towards the next multiple of 3 or capped at 9 digits. + * 2. Seconds must be specified. This may become optional in (yet unreleased) TOML 1.1.0, but 1.0.0 defined local time + * in terms of RFC3339 [1]. + * 3. date-time separator (`t`, `T` or space ` `) is canonicalized to an upper T. This is compliant with RFC3339 + * [1] 5.6: + * > Applications that generate this format SHOULD use upper case letters. + * + * [1]: https://datatracker.ietf.org/doc/html/rfc3339#section-5.6 + */ +static void normalizeDatetimeFormat(toml::value & t) +{ + if (t.is_local_datetime()) { + auto & ldt = t.as_local_datetime(); + t.as_local_datetime_fmt() = { + .delimiter = toml::datetime_delimiter_kind::upper_T, + // https://datatracker.ietf.org/doc/html/rfc3339#section-5.6 + .has_seconds = true, // Mandated by TOML 1.0.0 + .subsecond_precision = normalizeSubsecondPrecision(ldt.time), + }; + return; + } + + if (t.is_offset_datetime()) { + auto & odt = t.as_offset_datetime(); + t.as_offset_datetime_fmt() = { + .delimiter = toml::datetime_delimiter_kind::upper_T, + // https://datatracker.ietf.org/doc/html/rfc3339#section-5.6 + .has_seconds = true, // Mandated by TOML 1.0.0 + .subsecond_precision = normalizeSubsecondPrecision(odt.time), + }; + return; + } + + if (t.is_local_time()) { + auto & lt = t.as_local_time(); + t.as_local_time_fmt() = { + .has_seconds = true, // Mandated by TOML 1.0.0 + .subsecond_precision = normalizeSubsecondPrecision(lt), + }; + return; + } +} + +#endif + static void prim_fromTOML(EvalState & state, const PosIdx pos, Value ** args, Value & val) { auto toml = state.forceStringNoCtx(*args[0], pos, "while evaluating the argument passed to builtins.fromTOML"); @@ -53,6 +132,9 @@ static void prim_fromTOML(EvalState & state, const PosIdx pos, Value ** args, Va case toml::value_t::local_date: case toml::value_t::local_time: { if (experimentalFeatureSettings.isEnabled(Xp::ParseTomlTimestamps)) { +#if HAVE_TOML11_4 + normalizeDatetimeFormat(t); +#endif auto attrs = state.buildBindings(2); attrs.alloc("_type").mkString("timestamp"); std::ostringstream s; @@ -72,7 +154,17 @@ static void prim_fromTOML(EvalState & state, const PosIdx pos, Value ** args, Va }; try { - visit(visit, val, toml::parse(tomlStream, "fromTOML" /* the "filename" */)); + visit( + visit, + val, + toml::parse( + tomlStream, + "fromTOML" /* the "filename" */ +#if HAVE_TOML11_4 + , + toml::spec::v(1, 0, 0) // Be explicit that we are parsing TOML 1.0.0 without extensions +#endif + )); } catch (std::exception & e) { // TODO: toml::syntax_error state.error("while parsing TOML: %s", e.what()).atPos(pos).debugThrow(); } From 320b2c74ef5c67af1f96c7cbcb8028921aee78c7 Mon Sep 17 00:00:00 2001 From: Sergei Zimmerman Date: Tue, 12 Aug 2025 16:23:00 +0300 Subject: [PATCH 6/6] packaging: Build with toml11 4.4.0 --- packaging/dependencies.nix | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/packaging/dependencies.nix b/packaging/dependencies.nix index 17ba06b4d..23d020f37 100644 --- a/packaging/dependencies.nix +++ b/packaging/dependencies.nix @@ -64,6 +64,16 @@ scope: { NIX_CFLAGS_COMPILE = "-DINITIAL_MARK_STACK_SIZE=1048576"; }); + toml11 = pkgs.toml11.overrideAttrs rec { + version = "4.4.0"; + src = pkgs.fetchFromGitHub { + owner = "ToruNiina"; + repo = "toml11"; + tag = "v${version}"; + hash = "sha256-sgWKYxNT22nw376ttGsTdg0AMzOwp8QH3E8mx0BZJTQ="; + }; + }; + # TODO Hack until https://github.com/NixOS/nixpkgs/issues/45462 is fixed. boost = (pkgs.boost.override {