2023-12-12 23:57:36 +02:00
|
|
|
#include <limits>
|
2023-05-09 17:45:12 +03:00
|
|
|
#include <unordered_set>
|
Remove 100s of CPU time (10%) from build times (1465s -> 1302s)
Result's from Mic92's framework 13th Gen Intel Core i7-1360P:
Before: 3595.92s user 183.01s system 1360% cpu 4:37.74 total
After: 3486.07s user 168.93s system 1354% cpu 4:29.79 total
I saw that boost/lexical_cast was costing about 100s in CPU time on our
compiles. We can fix this trivially by doing explicit template
instantiation in exactly one place and eliminating all other includes of
it, which is a code improvement anyway by hiding the boost.
Before:
```
lix/lix2 » ClangBuildAnalyzer --analyze buildtimeold.bin
Analyzing build trace from 'buildtimeold.bin'...
**** Time summary:
Compilation (551 times):
Parsing (frontend): 1465.3 s
Codegen & opts (backend): 1110.9 s
<snip>
**** Expensive headers:
178153 ms: ../src/libcmd/installable-value.hh (included 52 times, avg 3426 ms), included via:
40x: command.hh
5x: command-installable-value.hh
3x: installable-flake.hh
2x: <direct include>
2x: installable-attr-path.hh
176217 ms: ../src/libutil/error.hh (included 246 times, avg 716 ms), included via:
36x: command.hh installable-value.hh installables.hh derived-path.hh config.hh experimental-features.hh
12x: globals.hh config.hh experimental-features.hh
11x: file-system.hh file-descriptor.hh
6x: serialise.hh strings.hh
6x: <direct include>
6x: archive.hh serialise.hh strings.hh
...
173243 ms: ../src/libstore/store-api.hh (included 152 times, avg 1139 ms), included via:
55x: <direct include>
39x: command.hh installable-value.hh installables.hh
7x: libexpr.hh
4x: local-store.hh
4x: command-installable-value.hh installable-value.hh installables.hh
3x: binary-cache-store.hh
...
170482 ms: ../src/libutil/serialise.hh (included 201 times, avg 848 ms), included via:
37x: command.hh installable-value.hh installables.hh built-path.hh realisation.hh hash.hh
14x: store-api.hh nar-info.hh hash.hh
11x: <direct include>
7x: primops.hh eval.hh attr-set.hh nixexpr.hh value.hh source-path.hh archive.hh
7x: libexpr.hh value.hh source-path.hh archive.hh
6x: fetchers.hh hash.hh
...
169397 ms: ../src/libcmd/installables.hh (included 53 times, avg 3196 ms), included via:
40x: command.hh installable-value.hh
5x: command-installable-value.hh installable-value.hh
3x: installable-flake.hh installable-value.hh
2x: <direct include>
1x: installable-derived-path.hh
1x: installable-value.hh
...
159740 ms: ../src/libutil/strings.hh (included 221 times, avg 722 ms), included via:
37x: command.hh installable-value.hh installables.hh built-path.hh realisation.hh hash.hh serialise.hh
19x: <direct include>
14x: store-api.hh nar-info.hh hash.hh serialise.hh
11x: serialise.hh
7x: primops.hh eval.hh attr-set.hh nixexpr.hh value.hh source-path.hh archive.hh serialise.hh
7x: libexpr.hh value.hh source-path.hh archive.hh serialise.hh
...
156796 ms: ../src/libcmd/command.hh (included 51 times, avg 3074 ms), included via:
42x: <direct include>
7x: command-installable-value.hh
2x: installable-attr-path.hh
150392 ms: ../src/libutil/types.hh (included 251 times, avg 599 ms), included via:
36x: command.hh installable-value.hh installables.hh path.hh
11x: file-system.hh
10x: globals.hh
6x: fetchers.hh
6x: serialise.hh strings.hh error.hh
5x: archive.hh
...
133101 ms: /nix/store/644b90j1vms44nr18yw3520pzkrg4dd1-boost-1.81.0-dev/include/boost/lexical_cast.hpp (included 226 times, avg 588 ms), included via
:
37x: command.hh installable-value.hh installables.hh built-path.hh realisation.hh hash.hh serialise.hh strings.hh
19x: file-system.hh
11x: store-api.hh nar-info.hh hash.hh serialise.hh strings.hh
7x: primops.hh eval.hh attr-set.hh nixexpr.hh value.hh source-path.hh archive.hh serialise.hh strings.hh
7x: libexpr.hh value.hh source-path.hh archive.hh serialise.hh strings.hh
6x: eval.hh attr-set.hh nixexpr.hh value.hh source-path.hh archive.hh serialise.hh strings.hh
...
132887 ms: /nix/store/h2abv2l8irqj942i5rq9wbrj42kbsh5y-gcc-12.3.0/include/c++/12.3.0/memory (included 262 times, avg 507 ms), included via:
36x: command.hh installable-value.hh installables.hh path.hh types.hh ref.hh
16x: gtest.h
11x: file-system.hh types.hh ref.hh
10x: globals.hh types.hh ref.hh
10x: json.hpp
6x: serialise.hh
...
done in 0.6s.
```
After:
```
lix/lix2 » maintainers/buildtime_report.sh build
Processing all files and saving to '/home/jade/lix/lix2/maintainers/../buildtime.bin'...
done in 0.6s. Run 'ClangBuildAnalyzer --analyze /home/jade/lix/lix2/maintainers/../buildtime.bin' to analyze it.
Analyzing build trace from '/home/jade/lix/lix2/maintainers/../buildtime.bin'...
**** Time summary:
Compilation (551 times):
Parsing (frontend): 1302.1 s
Codegen & opts (backend): 956.3 s
<snip>
**** Expensive headers:
178145 ms: ../src/libutil/error.hh (included 246 times, avg 724 ms), included via:
36x: command.hh installable-value.hh installables.hh derived-path.hh config.hh experimental-features.hh
12x: globals.hh config.hh experimental-features.hh
11x: file-system.hh file-descriptor.hh
6x: <direct include>
6x: serialise.hh strings.hh
6x: fetchers.hh hash.hh serialise.hh strings.hh
...
154043 ms: ../src/libcmd/installable-value.hh (included 52 times, avg 2962 ms), included via:
40x: command.hh
5x: command-installable-value.hh
3x: installable-flake.hh
2x: <direct include>
2x: installable-attr-path.hh
153593 ms: ../src/libstore/store-api.hh (included 152 times, avg 1010 ms), included via:
55x: <direct include>
39x: command.hh installable-value.hh installables.hh
7x: libexpr.hh
4x: local-store.hh
4x: command-installable-value.hh installable-value.hh installables.hh
3x: binary-cache-store.hh
...
149948 ms: ../src/libutil/types.hh (included 251 times, avg 597 ms), included via:
36x: command.hh installable-value.hh installables.hh path.hh
11x: file-system.hh
10x: globals.hh
6x: fetchers.hh
6x: serialise.hh strings.hh error.hh
5x: archive.hh
...
144560 ms: ../src/libcmd/installables.hh (included 53 times, avg 2727 ms), included via:
40x: command.hh installable-value.hh
5x: command-installable-value.hh installable-value.hh
3x: installable-flake.hh installable-value.hh
2x: <direct include>
1x: installable-value.hh
1x: installable-derived-path.hh
...
136585 ms: ../src/libcmd/command.hh (included 51 times, avg 2678 ms), included via:
42x: <direct include>
7x: command-installable-value.hh
2x: installable-attr-path.hh
133394 ms: /nix/store/h2abv2l8irqj942i5rq9wbrj42kbsh5y-gcc-12.3.0/include/c++/12.3.0/memory (included 262 times, avg 509 ms), included via:
36x: command.hh installable-value.hh installables.hh path.hh types.hh ref.hh
16x: gtest.h
11x: file-system.hh types.hh ref.hh
10x: globals.hh types.hh ref.hh
10x: json.hpp
6x: serialise.hh
...
89315 ms: ../src/libstore/derived-path.hh (included 178 times, avg 501 ms), included via:
37x: command.hh installable-value.hh installables.hh
25x: store-api.hh realisation.hh
7x: primops.hh eval.hh attr-set.hh nixexpr.hh value.hh context.hh
6x: eval.hh attr-set.hh nixexpr.hh value.hh context.hh
6x: libexpr.hh value.hh context.hh
6x: shared.hh
...
87347 ms: /nix/store/h2abv2l8irqj942i5rq9wbrj42kbsh5y-gcc-12.3.0/include/c++/12.3.0/ostream (included 273 times, avg 319 ms), included via:
35x: command.hh installable-value.hh installables.hh path.hh types.hh ref.hh memory unique_ptr.h
12x: regex sstream istream
10x: file-system.hh types.hh ref.hh memory unique_ptr.h
10x: gtest.h memory unique_ptr.h
10x: globals.hh types.hh ref.hh memory unique_ptr.h
6x: fetchers.hh types.hh ref.hh memory unique_ptr.h
...
85249 ms: ../src/libutil/config.hh (included 213 times, avg 400 ms), included via:
37x: command.hh installable-value.hh installables.hh derived-path.hh
20x: globals.hh
20x: logging.hh
16x: store-api.hh logging.hh
6x: <direct include>
6x: eval.hh attr-set.hh nixexpr.hh value.hh context.hh derived-path.hh
...
done in 0.5s.
```
Adapated from https://git.lix.systems/lix-project/lix/commit/18aa3e1d570b4ecbb9962376e5fba5757dad8da9
2024-05-30 07:12:34 +03:00
|
|
|
#include <sstream>
|
2023-04-09 23:42:20 +03:00
|
|
|
|
2023-12-12 23:57:36 +02:00
|
|
|
#include "print.hh"
|
|
|
|
#include "ansicolor.hh"
|
|
|
|
#include "signals.hh"
|
|
|
|
#include "store-api.hh"
|
|
|
|
#include "terminal.hh"
|
|
|
|
#include "english.hh"
|
2023-12-07 02:03:01 +02:00
|
|
|
#include "eval.hh"
|
2023-12-12 23:57:36 +02:00
|
|
|
|
2023-04-09 23:42:20 +03:00
|
|
|
namespace nix {
|
|
|
|
|
2023-12-12 23:57:36 +02:00
|
|
|
void printElided(
|
|
|
|
std::ostream & output,
|
|
|
|
unsigned int value,
|
|
|
|
const std::string_view single,
|
|
|
|
const std::string_view plural,
|
|
|
|
bool ansiColors)
|
|
|
|
{
|
|
|
|
if (ansiColors)
|
|
|
|
output << ANSI_FAINT;
|
2023-12-06 22:42:53 +02:00
|
|
|
output << "«";
|
2023-12-12 23:57:36 +02:00
|
|
|
pluralize(output, value, single, plural);
|
|
|
|
output << " elided»";
|
|
|
|
if (ansiColors)
|
|
|
|
output << ANSI_NORMAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2023-04-09 23:42:20 +03:00
|
|
|
std::ostream &
|
2023-12-12 23:57:36 +02:00
|
|
|
printLiteralString(std::ostream & str, const std::string_view string, size_t maxLength, bool ansiColors)
|
2023-04-15 21:56:51 +03:00
|
|
|
{
|
2023-12-12 23:57:36 +02:00
|
|
|
size_t charsPrinted = 0;
|
|
|
|
if (ansiColors)
|
|
|
|
str << ANSI_MAGENTA;
|
2023-04-09 23:42:20 +03:00
|
|
|
str << "\"";
|
|
|
|
for (auto i = string.begin(); i != string.end(); ++i) {
|
2023-12-12 23:57:36 +02:00
|
|
|
if (charsPrinted >= maxLength) {
|
2023-12-06 22:42:53 +02:00
|
|
|
str << "\" ";
|
2023-12-12 23:57:36 +02:00
|
|
|
printElided(str, string.length() - charsPrinted, "byte", "bytes", ansiColors);
|
|
|
|
return str;
|
|
|
|
}
|
|
|
|
|
2023-04-09 23:42:20 +03:00
|
|
|
if (*i == '\"' || *i == '\\') str << "\\" << *i;
|
|
|
|
else if (*i == '\n') str << "\\n";
|
|
|
|
else if (*i == '\r') str << "\\r";
|
|
|
|
else if (*i == '\t') str << "\\t";
|
|
|
|
else if (*i == '$' && *(i+1) == '{') str << "\\" << *i;
|
|
|
|
else str << *i;
|
2023-12-12 23:57:36 +02:00
|
|
|
charsPrinted++;
|
2023-04-09 23:42:20 +03:00
|
|
|
}
|
|
|
|
str << "\"";
|
2023-12-12 23:57:36 +02:00
|
|
|
if (ansiColors)
|
|
|
|
str << ANSI_NORMAL;
|
2023-04-09 23:42:20 +03:00
|
|
|
return str;
|
|
|
|
}
|
|
|
|
|
2023-12-12 23:57:36 +02:00
|
|
|
std::ostream &
|
|
|
|
printLiteralString(std::ostream & str, const std::string_view string)
|
|
|
|
{
|
|
|
|
return printLiteralString(str, string, std::numeric_limits<size_t>::max(), false);
|
|
|
|
}
|
|
|
|
|
2023-04-09 23:42:20 +03:00
|
|
|
std::ostream &
|
2023-04-16 13:56:31 +03:00
|
|
|
printLiteralBool(std::ostream & str, bool boolean)
|
2023-04-15 21:56:51 +03:00
|
|
|
{
|
2023-04-09 23:42:20 +03:00
|
|
|
str << (boolean ? "true" : "false");
|
|
|
|
return str;
|
|
|
|
}
|
|
|
|
|
2023-05-09 17:45:12 +03:00
|
|
|
// Returns `true' is a string is a reserved keyword which requires quotation
|
|
|
|
// when printing attribute set field names.
|
|
|
|
//
|
|
|
|
// This list should generally be kept in sync with `./lexer.l'.
|
|
|
|
// You can test if a keyword needs to be added by running:
|
|
|
|
// $ nix eval --expr '{ <KEYWORD> = 1; }'
|
|
|
|
// For example `or' doesn't need to be quoted.
|
|
|
|
bool isReservedKeyword(const std::string_view str)
|
|
|
|
{
|
|
|
|
static const std::unordered_set<std::string_view> reservedKeywords = {
|
|
|
|
"if", "then", "else", "assert", "with", "let", "in", "rec", "inherit"
|
|
|
|
};
|
|
|
|
return reservedKeywords.contains(str);
|
|
|
|
}
|
|
|
|
|
2023-04-16 15:07:35 +03:00
|
|
|
std::ostream &
|
|
|
|
printIdentifier(std::ostream & str, std::string_view s) {
|
|
|
|
if (s.empty())
|
|
|
|
str << "\"\"";
|
2023-05-09 17:45:12 +03:00
|
|
|
else if (isReservedKeyword(s))
|
2023-04-16 15:07:35 +03:00
|
|
|
str << '"' << s << '"';
|
|
|
|
else {
|
|
|
|
char c = s[0];
|
|
|
|
if (!((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_')) {
|
|
|
|
printLiteralString(str, s);
|
|
|
|
return str;
|
|
|
|
}
|
|
|
|
for (auto c : s)
|
|
|
|
if (!((c >= 'a' && c <= 'z') ||
|
|
|
|
(c >= 'A' && c <= 'Z') ||
|
|
|
|
(c >= '0' && c <= '9') ||
|
|
|
|
c == '_' || c == '\'' || c == '-')) {
|
|
|
|
printLiteralString(str, s);
|
|
|
|
return str;
|
|
|
|
}
|
|
|
|
str << s;
|
|
|
|
}
|
|
|
|
return str;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool isVarName(std::string_view s)
|
|
|
|
{
|
|
|
|
if (s.size() == 0) return false;
|
2023-05-09 17:45:12 +03:00
|
|
|
if (isReservedKeyword(s)) return false;
|
2023-04-16 15:07:35 +03:00
|
|
|
char c = s[0];
|
|
|
|
if ((c >= '0' && c <= '9') || c == '-' || c == '\'') return false;
|
|
|
|
for (auto & i : s)
|
|
|
|
if (!((i >= 'a' && i <= 'z') ||
|
|
|
|
(i >= 'A' && i <= 'Z') ||
|
|
|
|
(i >= '0' && i <= '9') ||
|
|
|
|
i == '_' || i == '-' || i == '\''))
|
|
|
|
return false;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::ostream &
|
|
|
|
printAttributeName(std::ostream & str, std::string_view name) {
|
|
|
|
if (isVarName(name))
|
|
|
|
str << name;
|
|
|
|
else
|
|
|
|
printLiteralString(str, name);
|
|
|
|
return str;
|
|
|
|
}
|
|
|
|
|
2023-12-12 23:57:36 +02:00
|
|
|
bool isImportantAttrName(const std::string& attrName)
|
|
|
|
{
|
|
|
|
return attrName == "type" || attrName == "_type";
|
|
|
|
}
|
|
|
|
|
|
|
|
typedef std::pair<std::string, Value *> AttrPair;
|
|
|
|
|
|
|
|
struct ImportantFirstAttrNameCmp
|
|
|
|
{
|
|
|
|
|
|
|
|
bool operator()(const AttrPair& lhs, const AttrPair& rhs) const
|
|
|
|
{
|
|
|
|
auto lhsIsImportant = isImportantAttrName(lhs.first);
|
|
|
|
auto rhsIsImportant = isImportantAttrName(rhs.first);
|
|
|
|
return std::forward_as_tuple(!lhsIsImportant, lhs.first)
|
|
|
|
< std::forward_as_tuple(!rhsIsImportant, rhs.first);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2024-02-04 05:17:22 +02:00
|
|
|
typedef std::set<const void *> ValuesSeen;
|
2024-02-04 10:40:30 +02:00
|
|
|
typedef std::vector<std::pair<std::string, Value *>> AttrVec;
|
2023-12-12 23:57:36 +02:00
|
|
|
|
|
|
|
class Printer
|
|
|
|
{
|
|
|
|
private:
|
|
|
|
std::ostream & output;
|
|
|
|
EvalState & state;
|
|
|
|
PrintOptions options;
|
|
|
|
std::optional<ValuesSeen> seen;
|
2024-06-29 15:01:10 +03:00
|
|
|
size_t totalAttrsPrinted = 0;
|
2024-06-29 15:02:28 +03:00
|
|
|
size_t totalListItemsPrinted = 0;
|
2024-02-04 10:40:30 +02:00
|
|
|
std::string indent;
|
|
|
|
|
|
|
|
void increaseIndent()
|
|
|
|
{
|
2024-02-08 20:17:20 +02:00
|
|
|
if (options.shouldPrettyPrint()) {
|
2024-02-04 10:40:30 +02:00
|
|
|
indent.append(options.prettyIndent, ' ');
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void decreaseIndent()
|
|
|
|
{
|
2024-02-08 20:17:20 +02:00
|
|
|
if (options.shouldPrettyPrint()) {
|
2024-02-08 20:11:45 +02:00
|
|
|
assert(indent.size() >= options.prettyIndent);
|
2024-02-04 10:40:30 +02:00
|
|
|
indent.resize(indent.size() - options.prettyIndent);
|
|
|
|
}
|
|
|
|
}
|
2023-12-12 23:57:36 +02:00
|
|
|
|
2024-02-08 20:17:33 +02:00
|
|
|
/**
|
|
|
|
* Print a space (for separating items or attributes).
|
|
|
|
*
|
|
|
|
* If pretty-printing is enabled, a newline and the current `indent` is
|
|
|
|
* printed instead.
|
|
|
|
*/
|
|
|
|
void printSpace(bool prettyPrint)
|
|
|
|
{
|
|
|
|
if (prettyPrint) {
|
|
|
|
output << "\n" << indent;
|
|
|
|
} else {
|
|
|
|
output << " ";
|
|
|
|
}
|
|
|
|
}
|
2023-12-12 23:57:36 +02:00
|
|
|
|
|
|
|
void printRepeated()
|
|
|
|
{
|
|
|
|
if (options.ansiColors)
|
|
|
|
output << ANSI_MAGENTA;
|
|
|
|
output << "«repeated»";
|
|
|
|
if (options.ansiColors)
|
|
|
|
output << ANSI_NORMAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
void printNullptr()
|
|
|
|
{
|
|
|
|
if (options.ansiColors)
|
|
|
|
output << ANSI_MAGENTA;
|
|
|
|
output << "«nullptr»";
|
|
|
|
if (options.ansiColors)
|
|
|
|
output << ANSI_NORMAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
void printElided(unsigned int value, const std::string_view single, const std::string_view plural)
|
|
|
|
{
|
|
|
|
::nix::printElided(output, value, single, plural, options.ansiColors);
|
|
|
|
}
|
|
|
|
|
|
|
|
void printInt(Value & v)
|
|
|
|
{
|
|
|
|
if (options.ansiColors)
|
|
|
|
output << ANSI_CYAN;
|
2024-03-25 19:20:18 +02:00
|
|
|
output << v.integer();
|
2023-12-12 23:57:36 +02:00
|
|
|
if (options.ansiColors)
|
|
|
|
output << ANSI_NORMAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
void printFloat(Value & v)
|
|
|
|
{
|
|
|
|
if (options.ansiColors)
|
|
|
|
output << ANSI_CYAN;
|
2024-03-25 19:20:18 +02:00
|
|
|
output << v.fpoint();
|
2023-12-12 23:57:36 +02:00
|
|
|
if (options.ansiColors)
|
|
|
|
output << ANSI_NORMAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
void printBool(Value & v)
|
|
|
|
{
|
|
|
|
if (options.ansiColors)
|
|
|
|
output << ANSI_CYAN;
|
2024-03-25 19:20:18 +02:00
|
|
|
printLiteralBool(output, v.boolean());
|
2023-12-12 23:57:36 +02:00
|
|
|
if (options.ansiColors)
|
|
|
|
output << ANSI_NORMAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
void printString(Value & v)
|
|
|
|
{
|
|
|
|
printLiteralString(output, v.string_view(), options.maxStringLength, options.ansiColors);
|
|
|
|
}
|
|
|
|
|
|
|
|
void printPath(Value & v)
|
|
|
|
{
|
|
|
|
if (options.ansiColors)
|
|
|
|
output << ANSI_GREEN;
|
|
|
|
output << v.path().to_string(); // !!! escaping?
|
|
|
|
if (options.ansiColors)
|
|
|
|
output << ANSI_NORMAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
void printNull()
|
|
|
|
{
|
|
|
|
if (options.ansiColors)
|
|
|
|
output << ANSI_CYAN;
|
|
|
|
output << "null";
|
|
|
|
if (options.ansiColors)
|
|
|
|
output << ANSI_NORMAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
void printDerivation(Value & v)
|
|
|
|
{
|
2024-05-22 19:35:44 +03:00
|
|
|
std::optional<StorePath> storePath;
|
|
|
|
if (auto i = v.attrs()->get(state.sDrvPath)) {
|
|
|
|
NixStringContext context;
|
|
|
|
storePath = state.coerceToStorePath(i->pos, *i->value, context, "while evaluating the drvPath of a derivation");
|
|
|
|
}
|
|
|
|
|
2024-06-05 17:19:01 +03:00
|
|
|
/* This unfortunately breaks printing nested values because of
|
2024-05-22 19:35:44 +03:00
|
|
|
how the pretty printer is used (when pretting printing and warning
|
|
|
|
to same terminal / std stream). */
|
|
|
|
#if 0
|
|
|
|
if (storePath && !storePath->isDerivation())
|
|
|
|
warn(
|
|
|
|
"drvPath attribute '%s' is not a valid store path to a derivation, this value not work properly",
|
|
|
|
state.store->printStorePath(*storePath));
|
|
|
|
#endif
|
2023-12-12 23:57:36 +02:00
|
|
|
|
2024-03-10 04:28:04 +02:00
|
|
|
if (options.ansiColors)
|
|
|
|
output << ANSI_GREEN;
|
|
|
|
output << "«derivation";
|
2024-05-22 19:35:44 +03:00
|
|
|
if (storePath) {
|
|
|
|
output << " " << state.store->printStorePath(*storePath);
|
2023-12-12 23:57:36 +02:00
|
|
|
}
|
2024-03-10 04:28:04 +02:00
|
|
|
output << "»";
|
|
|
|
if (options.ansiColors)
|
|
|
|
output << ANSI_NORMAL;
|
2023-12-12 23:57:36 +02:00
|
|
|
}
|
|
|
|
|
2024-07-14 15:28:21 +03:00
|
|
|
/**
|
|
|
|
* @note This may force items.
|
|
|
|
*/
|
2024-02-04 10:40:30 +02:00
|
|
|
bool shouldPrettyPrintAttrs(AttrVec & v)
|
|
|
|
{
|
2024-02-08 20:17:20 +02:00
|
|
|
if (!options.shouldPrettyPrint() || v.empty()) {
|
2024-02-04 10:40:30 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Pretty-print attrsets with more than one item.
|
|
|
|
if (v.size() > 1) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
auto item = v[0].second;
|
|
|
|
if (!item) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2024-07-14 15:28:21 +03:00
|
|
|
// It is ok to force the item(s) here, because they will be printed anyway.
|
|
|
|
state.forceValue(*item, item->determinePos(noPos));
|
|
|
|
|
2024-02-04 10:40:30 +02:00
|
|
|
// Pretty-print single-item attrsets only if they contain nested
|
|
|
|
// structures.
|
|
|
|
auto itemType = item->type();
|
|
|
|
return itemType == nList || itemType == nAttrs || itemType == nThunk;
|
|
|
|
}
|
|
|
|
|
2023-12-12 23:57:36 +02:00
|
|
|
void printAttrs(Value & v, size_t depth)
|
|
|
|
{
|
2024-03-25 19:20:18 +02:00
|
|
|
if (seen && !seen->insert(v.attrs()).second) {
|
2023-12-12 23:57:36 +02:00
|
|
|
printRepeated();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (options.force && options.derivationPaths && state.isDerivation(v)) {
|
|
|
|
printDerivation(v);
|
|
|
|
} else if (depth < options.maxDepth) {
|
2024-02-04 10:40:30 +02:00
|
|
|
increaseIndent();
|
|
|
|
output << "{";
|
2023-12-12 23:57:36 +02:00
|
|
|
|
2024-02-04 10:40:30 +02:00
|
|
|
AttrVec sorted;
|
2024-03-25 19:20:18 +02:00
|
|
|
for (auto & i : *v.attrs())
|
2023-12-12 23:57:36 +02:00
|
|
|
sorted.emplace_back(std::pair(state.symbols[i.name], i.value));
|
|
|
|
|
|
|
|
if (options.maxAttrs == std::numeric_limits<size_t>::max())
|
|
|
|
std::sort(sorted.begin(), sorted.end());
|
|
|
|
else
|
|
|
|
std::sort(sorted.begin(), sorted.end(), ImportantFirstAttrNameCmp());
|
|
|
|
|
2024-02-04 10:40:30 +02:00
|
|
|
auto prettyPrint = shouldPrettyPrintAttrs(sorted);
|
|
|
|
|
2024-06-29 14:48:17 +03:00
|
|
|
size_t currentAttrsPrinted = 0;
|
|
|
|
|
2023-12-12 23:57:36 +02:00
|
|
|
for (auto & i : sorted) {
|
2024-02-08 20:17:33 +02:00
|
|
|
printSpace(prettyPrint);
|
2024-02-04 10:40:30 +02:00
|
|
|
|
2024-06-29 15:01:10 +03:00
|
|
|
if (totalAttrsPrinted >= options.maxAttrs) {
|
2024-06-29 14:48:17 +03:00
|
|
|
printElided(sorted.size() - currentAttrsPrinted, "attribute", "attributes");
|
2023-12-12 23:57:36 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
printAttributeName(output, i.first);
|
|
|
|
output << " = ";
|
|
|
|
print(*i.second, depth + 1);
|
2024-02-04 10:40:30 +02:00
|
|
|
output << ";";
|
2024-06-29 15:01:10 +03:00
|
|
|
totalAttrsPrinted++;
|
2024-06-29 14:48:17 +03:00
|
|
|
currentAttrsPrinted++;
|
2023-12-12 23:57:36 +02:00
|
|
|
}
|
|
|
|
|
2024-02-04 10:40:30 +02:00
|
|
|
decreaseIndent();
|
2024-02-08 20:17:33 +02:00
|
|
|
printSpace(prettyPrint);
|
2023-12-12 23:57:36 +02:00
|
|
|
output << "}";
|
2024-02-04 10:40:30 +02:00
|
|
|
} else {
|
2023-12-12 23:57:36 +02:00
|
|
|
output << "{ ... }";
|
2024-02-04 10:40:30 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-07-14 15:31:20 +03:00
|
|
|
/**
|
|
|
|
* @note This may force items.
|
|
|
|
*/
|
2024-02-04 10:40:30 +02:00
|
|
|
bool shouldPrettyPrintList(std::span<Value * const> list)
|
|
|
|
{
|
2024-02-08 20:17:20 +02:00
|
|
|
if (!options.shouldPrettyPrint() || list.empty()) {
|
2024-02-04 10:40:30 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Pretty-print lists with more than one item.
|
|
|
|
if (list.size() > 1) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
auto item = list[0];
|
|
|
|
if (!item) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2024-07-14 15:31:20 +03:00
|
|
|
// It is ok to force the item(s) here, because they will be printed anyway.
|
|
|
|
state.forceValue(*item, item->determinePos(noPos));
|
|
|
|
|
2024-02-04 10:40:30 +02:00
|
|
|
// Pretty-print single-item lists only if they contain nested
|
|
|
|
// structures.
|
|
|
|
auto itemType = item->type();
|
|
|
|
return itemType == nList || itemType == nAttrs || itemType == nThunk;
|
2023-12-12 23:57:36 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void printList(Value & v, size_t depth)
|
|
|
|
{
|
|
|
|
if (seen && v.listSize() && !seen->insert(&v).second) {
|
|
|
|
printRepeated();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (depth < options.maxDepth) {
|
2024-02-04 10:40:30 +02:00
|
|
|
increaseIndent();
|
|
|
|
output << "[";
|
|
|
|
auto listItems = v.listItems();
|
|
|
|
auto prettyPrint = shouldPrettyPrintList(listItems);
|
2024-06-29 15:08:43 +03:00
|
|
|
|
|
|
|
size_t currentListItemsPrinted = 0;
|
|
|
|
|
2024-02-04 10:40:30 +02:00
|
|
|
for (auto elem : listItems) {
|
2024-02-08 20:17:33 +02:00
|
|
|
printSpace(prettyPrint);
|
2024-02-04 10:40:30 +02:00
|
|
|
|
2024-06-29 15:02:28 +03:00
|
|
|
if (totalListItemsPrinted >= options.maxListItems) {
|
2024-06-29 15:08:43 +03:00
|
|
|
printElided(listItems.size() - currentListItemsPrinted, "item", "items");
|
2023-12-12 23:57:36 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (elem) {
|
|
|
|
print(*elem, depth + 1);
|
|
|
|
} else {
|
|
|
|
printNullptr();
|
|
|
|
}
|
2024-06-29 15:02:28 +03:00
|
|
|
totalListItemsPrinted++;
|
2024-06-29 15:08:43 +03:00
|
|
|
currentListItemsPrinted++;
|
2023-12-12 23:57:36 +02:00
|
|
|
}
|
2024-02-04 10:40:30 +02:00
|
|
|
|
|
|
|
decreaseIndent();
|
2024-02-08 20:17:33 +02:00
|
|
|
printSpace(prettyPrint);
|
2024-02-04 10:40:30 +02:00
|
|
|
output << "]";
|
|
|
|
} else {
|
|
|
|
output << "[ ... ]";
|
2023-12-12 23:57:36 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void printFunction(Value & v)
|
|
|
|
{
|
|
|
|
if (options.ansiColors)
|
|
|
|
output << ANSI_BLUE;
|
|
|
|
output << "«";
|
|
|
|
|
|
|
|
if (v.isLambda()) {
|
|
|
|
output << "lambda";
|
2024-03-25 19:20:18 +02:00
|
|
|
if (v.payload.lambda.fun) {
|
|
|
|
if (v.payload.lambda.fun->name) {
|
|
|
|
output << " " << state.symbols[v.payload.lambda.fun->name];
|
2023-12-12 23:57:36 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
std::ostringstream s;
|
2024-03-25 19:20:18 +02:00
|
|
|
s << state.positions[v.payload.lambda.fun->pos];
|
2023-12-12 23:57:36 +02:00
|
|
|
output << " @ " << filterANSIEscapes(s.str());
|
|
|
|
}
|
|
|
|
} else if (v.isPrimOp()) {
|
2024-03-25 19:20:18 +02:00
|
|
|
if (v.primOp())
|
|
|
|
output << *v.primOp();
|
2023-12-12 23:57:36 +02:00
|
|
|
else
|
|
|
|
output << "primop";
|
|
|
|
} else if (v.isPrimOpApp()) {
|
|
|
|
output << "partially applied ";
|
|
|
|
auto primOp = v.primOpAppPrimOp();
|
|
|
|
if (primOp)
|
|
|
|
output << *primOp;
|
|
|
|
else
|
|
|
|
output << "primop";
|
|
|
|
} else {
|
|
|
|
abort();
|
|
|
|
}
|
|
|
|
|
|
|
|
output << "»";
|
|
|
|
if (options.ansiColors)
|
|
|
|
output << ANSI_NORMAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
void printThunk(Value & v)
|
|
|
|
{
|
|
|
|
if (v.isBlackhole()) {
|
|
|
|
// Although we know for sure that it's going to be an infinite recursion
|
|
|
|
// when this value is accessed _in the current context_, it's likely
|
|
|
|
// that the user will misinterpret a simpler «infinite recursion» output
|
|
|
|
// as a definitive statement about the value, while in fact it may be
|
|
|
|
// a valid value after `builtins.trace` and perhaps some other steps
|
|
|
|
// have completed.
|
|
|
|
if (options.ansiColors)
|
|
|
|
output << ANSI_RED;
|
|
|
|
output << "«potential infinite recursion»";
|
|
|
|
if (options.ansiColors)
|
|
|
|
output << ANSI_NORMAL;
|
|
|
|
} else if (v.isThunk() || v.isApp()) {
|
|
|
|
if (options.ansiColors)
|
|
|
|
output << ANSI_MAGENTA;
|
|
|
|
output << "«thunk»";
|
|
|
|
if (options.ansiColors)
|
|
|
|
output << ANSI_NORMAL;
|
|
|
|
} else {
|
|
|
|
abort();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void printExternal(Value & v)
|
|
|
|
{
|
2024-03-25 19:20:18 +02:00
|
|
|
v.external()->print(output);
|
2023-12-12 23:57:36 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void printUnknown()
|
|
|
|
{
|
|
|
|
if (options.ansiColors)
|
|
|
|
output << ANSI_RED;
|
|
|
|
output << "«unknown»";
|
|
|
|
if (options.ansiColors)
|
|
|
|
output << ANSI_NORMAL;
|
|
|
|
}
|
|
|
|
|
2024-02-04 05:18:42 +02:00
|
|
|
void printError_(Error & e)
|
2023-12-12 23:57:36 +02:00
|
|
|
{
|
|
|
|
if (options.ansiColors)
|
|
|
|
output << ANSI_RED;
|
2024-02-04 05:19:23 +02:00
|
|
|
output << "«error: " << filterANSIEscapes(e.info().msg.str(), true) << "»";
|
2023-12-12 23:57:36 +02:00
|
|
|
if (options.ansiColors)
|
|
|
|
output << ANSI_NORMAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
void print(Value & v, size_t depth)
|
|
|
|
{
|
|
|
|
output.flush();
|
|
|
|
checkInterrupt();
|
|
|
|
|
2024-03-10 04:28:04 +02:00
|
|
|
try {
|
|
|
|
if (options.force) {
|
2023-12-12 23:57:36 +02:00
|
|
|
state.forceValue(v, v.determinePos(noPos));
|
|
|
|
}
|
|
|
|
|
2024-03-10 04:28:04 +02:00
|
|
|
switch (v.type()) {
|
2023-12-12 23:57:36 +02:00
|
|
|
|
2024-03-10 04:28:04 +02:00
|
|
|
case nInt:
|
|
|
|
printInt(v);
|
|
|
|
break;
|
2023-12-12 23:57:36 +02:00
|
|
|
|
2024-03-10 04:28:04 +02:00
|
|
|
case nFloat:
|
|
|
|
printFloat(v);
|
|
|
|
break;
|
2023-12-12 23:57:36 +02:00
|
|
|
|
2024-03-10 04:28:04 +02:00
|
|
|
case nBool:
|
|
|
|
printBool(v);
|
|
|
|
break;
|
2023-12-12 23:57:36 +02:00
|
|
|
|
2024-03-10 04:28:04 +02:00
|
|
|
case nString:
|
|
|
|
printString(v);
|
|
|
|
break;
|
2023-12-12 23:57:36 +02:00
|
|
|
|
2024-03-10 04:28:04 +02:00
|
|
|
case nPath:
|
|
|
|
printPath(v);
|
|
|
|
break;
|
2023-12-12 23:57:36 +02:00
|
|
|
|
2024-03-10 04:28:04 +02:00
|
|
|
case nNull:
|
|
|
|
printNull();
|
|
|
|
break;
|
2023-12-12 23:57:36 +02:00
|
|
|
|
2024-03-10 04:28:04 +02:00
|
|
|
case nAttrs:
|
|
|
|
printAttrs(v, depth);
|
|
|
|
break;
|
2023-12-12 23:57:36 +02:00
|
|
|
|
2024-03-10 04:28:04 +02:00
|
|
|
case nList:
|
|
|
|
printList(v, depth);
|
|
|
|
break;
|
2023-12-12 23:57:36 +02:00
|
|
|
|
2024-03-10 04:28:04 +02:00
|
|
|
case nFunction:
|
|
|
|
printFunction(v);
|
|
|
|
break;
|
2023-12-12 23:57:36 +02:00
|
|
|
|
2024-03-10 04:28:04 +02:00
|
|
|
case nThunk:
|
|
|
|
printThunk(v);
|
|
|
|
break;
|
2023-12-12 23:57:36 +02:00
|
|
|
|
2024-03-10 04:28:04 +02:00
|
|
|
case nExternal:
|
|
|
|
printExternal(v);
|
|
|
|
break;
|
2023-12-12 23:57:36 +02:00
|
|
|
|
2024-03-10 04:28:04 +02:00
|
|
|
default:
|
|
|
|
printUnknown();
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
} catch (Error & e) {
|
|
|
|
if (options.errors == ErrorPrintBehavior::Throw
|
|
|
|
|| (options.errors == ErrorPrintBehavior::ThrowTopLevel
|
|
|
|
&& depth == 0)) {
|
|
|
|
throw;
|
|
|
|
}
|
|
|
|
printError_(e);
|
2023-12-12 23:57:36 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public:
|
|
|
|
Printer(std::ostream & output, EvalState & state, PrintOptions options)
|
|
|
|
: output(output), state(state), options(options) { }
|
|
|
|
|
|
|
|
void print(Value & v)
|
|
|
|
{
|
2024-06-29 15:01:10 +03:00
|
|
|
totalAttrsPrinted = 0;
|
2024-06-29 15:02:28 +03:00
|
|
|
totalListItemsPrinted = 0;
|
2024-02-04 10:40:30 +02:00
|
|
|
indent.clear();
|
2023-12-06 22:42:53 +02:00
|
|
|
|
2023-12-12 23:57:36 +02:00
|
|
|
if (options.trackRepeated) {
|
|
|
|
seen.emplace();
|
|
|
|
} else {
|
|
|
|
seen.reset();
|
|
|
|
}
|
|
|
|
|
|
|
|
ValuesSeen seen;
|
|
|
|
print(v, 0);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
void printValue(EvalState & state, std::ostream & output, Value & v, PrintOptions options)
|
|
|
|
{
|
|
|
|
Printer(output, state, options).print(v);
|
|
|
|
}
|
2023-04-16 15:07:35 +03:00
|
|
|
|
2023-12-07 02:03:01 +02:00
|
|
|
std::ostream & operator<<(std::ostream & output, const ValuePrinter & printer)
|
|
|
|
{
|
|
|
|
printValue(printer.state, output, printer.value, printer.options);
|
|
|
|
return output;
|
|
|
|
}
|
|
|
|
|
2024-02-04 05:21:20 +02:00
|
|
|
template<>
|
2024-02-04 06:35:19 +02:00
|
|
|
HintFmt & HintFmt::operator%(const ValuePrinter & value)
|
2024-02-04 05:21:20 +02:00
|
|
|
{
|
|
|
|
fmt % value;
|
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
|
2023-04-09 23:42:20 +03:00
|
|
|
}
|