Compare commits

...

12 commits

Author SHA1 Message Date
eldritch horrors 5a54b0a20c meson: install systemd files
Change-Id: Idacf602fd379c82a051f00df2293cb02c8b286d4
2024-03-29 20:10:33 +00:00
eldritch horrors e28dc26084 meson: install shell files
Change-Id: I7c30690e5763d095cf7444333f7b687509051c5f
2024-03-29 20:10:33 +00:00
eldritch horrors 1da1f501fc meson: fix state-dir default value
the autoconf build system defaults to /nix/var, not /nix/var/nix. the
latter is only used in libstore, so we'll move the extra segment there.

Change-Id: Idfbc988ee302355982abdcd51d6d7b5d5d661c0d
2024-03-29 19:14:23 +00:00
Winter Cute 6646b80396 meson: add missing explicit dependency on nlohmann_json
Without this, the Meson setup won't bail out if nlohmann_json is
missing, leading to subpar DX (and maybe worse, but I'm not entirely
sure).

Change-Id: I5913111060226b540dcf003257c99a08e84da0de
2024-03-29 14:16:58 -04:00
Rebecca Turner 9d97d1cb68 Merge "Set MAKEFLAGS=-j and GTEST_BRIEF in .envrc" into main 2024-03-29 17:09:13 +00:00
Rebecca Turner 877750b7c5 Merge "Move DebugChar into its own file" into main 2024-03-29 16:20:14 +00:00
eldritch horrors 6e5db5e4a2 meson: install missing/generated headers
one headers (args/root.hh) was simply missing, and the generated headers
were not installed. not all of them *should* be installed either, only a
select few (and sadly this needs a custom target for each one, it seems)

Change-Id: I37b25517895d0e5e521abc1202fa65624de57ed1
2024-03-29 02:45:48 +00:00
eldritch horrors 69bfd21e20 meson: install pkg-config files for libraries
Change-Id: I14b9d81d09f188eacfb9c68bcfb84751c18e3779
2024-03-29 02:45:48 +00:00
eldritch horrors 86b954a7af meson: increase functional test timeout
sometimes these fail with timeouts on loaded machines. let's up the
timeouts until we can pull the tests apart to more reasonable sizes

Change-Id: I2dfff2183cc1f3ff5e6107f43748ac046fe00d05
2024-03-29 02:19:36 +00:00
Rebecca Turner 236bc046ba Merge "Remove HintFmt::operator%" into main 2024-03-29 01:13:45 +00:00
Rebecca Turner 5ec2efb686 Move DebugChar into its own file
Change-Id: Ia40549e5d0b78ece8dd0722c3a5a032b9915f24b
2024-03-28 15:54:12 -07:00
Rebecca Turner 8e63eca912 Remove HintFmt::operator%
Change-Id: Ibcf1a7848b4b18ec9b0807628ff229079ae7a0fe
2024-03-26 15:40:05 -07:00
30 changed files with 377 additions and 176 deletions

View file

@ -214,6 +214,9 @@ deps += gtest
toml11 = dependency('toml11', version : '>=3.7.0', required : true)
deps += toml11
nlohmann_json = dependency('nlohmann_json', required : true)
deps += nlohmann_json
#
# Build-time tools
#
@ -244,13 +247,12 @@ bison = find_program('bison')
flex = find_program('flex')
# This is how Nix does generated headers...
# other instances of header generation use a very similar command.
# FIXME(Qyriad): do we really need to use the shell for this?
gen_header_sh = 'echo \'R"__NIX_STR(\' | cat - @INPUT@ && echo \')__NIX_STR"\''
gen_header = generator(
bash,
arguments : [
'-c',
'echo \'R"__NIX_STR(\' | cat - @INPUT@ && echo \')__NIX_STR"\'',
],
arguments : [ '-c', gen_header_sh ],
capture : true,
output : '@PLAINNAME@.gen.hh',
)
@ -352,13 +354,10 @@ if get_option('profile-build').require(meson.get_compiler('cpp').get_id() == 'cl
endif
subdir('src')
subdir('scripts')
subdir('misc')
if enable_tests
# Just configures `scripts/nix-profile.sh.in` (and copies the original to the build directory).
# Done as a subdirectory to convince Meson to put the configured files
# in `build/scripts` instead of just `build`.
subdir('scripts')
subdir('tests/unit')
subdir('tests/functional')
endif

View file

@ -39,7 +39,7 @@ option('store-dir', type : 'string', value : '/nix/store',
description : 'path of the Nix store',
)
option('state-dir', type : 'string', value : '/nix/var/nix',
option('state-dir', type : 'string', value : '/nix/var',
description : 'path to store state in for Nix',
)

8
misc/bash/meson.build Normal file
View file

@ -0,0 +1,8 @@
configure_file(
input : 'completion.sh',
output : 'nix',
install : true,
install_dir : datadir / 'bash-completion/completions',
install_mode : 'rw-r--r--',
copy : true,
)

8
misc/fish/meson.build Normal file
View file

@ -0,0 +1,8 @@
configure_file(
input : 'completion.fish',
output : 'nix.fish',
install : true,
install_dir : datadir / 'fish/vendor_completions.d',
install_mode : 'rw-r--r--',
copy : true,
)

5
misc/meson.build Normal file
View file

@ -0,0 +1,5 @@
subdir('bash')
subdir('fish')
subdir('zsh')
subdir('systemd')

25
misc/systemd/meson.build Normal file
View file

@ -0,0 +1,25 @@
foreach config : [ 'nix-daemon.socket', 'nix-daemon.service' ]
configure_file(
input : config + '.in',
output : config,
install : true,
install_dir : prefix / 'lib/systemd/system',
install_mode : 'rw-r--r--',
configuration : {
'storedir' : store_dir,
'localstatedir' : state_dir,
'bindir' : bindir,
},
)
endforeach
configure_file(
input : 'nix-daemon.conf.in',
output : 'nix-daemon.conf',
install : true,
install_dir : prefix / 'lib/tmpfiles.d',
install_mode : 'rw-r--r--',
configuration : {
'localstatedir' : state_dir,
},
)

10
misc/zsh/meson.build Normal file
View file

@ -0,0 +1,10 @@
foreach script : [ [ 'completion.zsh', '_nix' ], [ 'run-help-nix' ] ]
configure_file(
input : script[0],
output : script.get(1, script[0]),
install : true,
install_dir : datadir / 'zsh/site-functions',
install_mode : 'rw-r--r--',
copy : true,
)
endforeach

View file

@ -1,3 +1,5 @@
# configures `scripts/nix-profile.sh.in` (and copies the original to the build directory).
# this is only needed for tests, but running it unconditionally does not hurt enough to care.
configure_file(
input : 'nix-profile.sh.in',
output : 'nix-profile.sh',
@ -12,3 +14,16 @@ configure_file(
output : 'nix-profile.sh.in',
copy : true,
)
foreach rc : [ '.sh', '.fish', '-daemon.sh', '-daemon.fish' ]
configure_file(
input : 'nix-profile' + rc + '.in',
output : 'nix' + rc,
install : true,
install_dir : sysconfdir / 'profile.d',
install_mode : 'rw-r--r--',
configuration : {
'localstatedir': state_dir,
},
)
endforeach

View file

@ -185,16 +185,6 @@ static int main_build_remote(int argc, char * * argv)
std::cerr << "# postpone\n";
else
{
// build the hint template.
std::string errorText =
"Failed to find a machine for remote build!\n"
"derivation: %s\nrequired (system, features): (%s, [%s])";
errorText += "\n%s available machines:";
errorText += "\n(systems, maxjobs, supportedFeatures, mandatoryFeatures)";
for (unsigned int i = 0; i < machines.size(); ++i)
errorText += "\n([%s], %s, [%s], [%s])";
// add the template values.
std::string drvstr;
if (drvPath.has_value())
@ -202,19 +192,30 @@ static int main_build_remote(int argc, char * * argv)
else
drvstr = "<unknown>";
auto error = HintFmt::fromFormatString(errorText);
error
% drvstr
% neededSystem
% concatStringsSep<StringSet>(", ", requiredFeatures)
% machines.size();
std::string machinesFormatted;
for (auto & m : machines)
error
% concatStringsSep<StringSet>(", ", m.systemTypes)
% m.maxJobs
% concatStringsSep<StringSet>(", ", m.supportedFeatures)
% concatStringsSep<StringSet>(", ", m.mandatoryFeatures);
for (auto & m : machines) {
machinesFormatted += HintFmt(
"\n([%s], %s, [%s], [%s])",
concatStringsSep<StringSet>(", ", m.systemTypes),
m.maxJobs,
concatStringsSep<StringSet>(", ", m.supportedFeatures),
concatStringsSep<StringSet>(", ", m.mandatoryFeatures)
).str();
}
auto error = HintFmt(
"Failed to find a machine for remote build!\n"
"derivation: %s\n"
"required (system, features): (%s, [%s])\n"
"%s available machines:\n"
"(systems, maxjobs, supportedFeatures, mandatoryFeatures)%s",
drvstr,
neededSystem,
concatStringsSep<StringSet>(", ", requiredFeatures),
machines.size(),
Uncolored(machinesFormatted)
);
printMsg(couldBuildLocally ? lvlChatty : lvlWarn, error.str());

View file

@ -44,6 +44,7 @@ libcmd = library(
boehm,
editline,
lowdown,
nlohmann_json,
],
install : true,
# FIXME(Qyriad): is this right?
@ -56,3 +57,17 @@ liblixcmd = declare_dependency(
include_directories : '.',
link_with : libcmd,
)
# FIXME: not using the pkg-config module because it creates way too many deps
# while meson migration is in progress, and we want to not include boost here
configure_file(
input : 'nix-cmd.pc.in',
output : 'nix-cmd.pc',
install_dir : libdir / 'pkgconfig',
configuration : {
'prefix' : prefix,
'libdir' : libdir,
'includedir' : includedir,
'PACKAGE_VERSION' : meson.project_version(),
},
)

View file

@ -0,0 +1,8 @@
libexpr_generated_headers += custom_target(
command : [ 'bash', '-c', 'echo \'R"__NIX_STR(\' | cat - @INPUT@ && echo \')__NIX_STR"\'' ],
input : 'call-flake.nix',
output : '@PLAINNAME@.gen.hh',
capture : true,
install : true,
install_dir : includedir / 'nix/flake',
)

View file

@ -49,10 +49,20 @@ meson.add_install_script(
'@0@'.format(includedir),
)
imported_drv_to_derivation_gen = gen_header.process('imported-drv-to-derivation.nix')
fetchurl_gen = gen_header.process('fetchurl.nix')
derivation_gen = gen_header.process('primops/derivation.nix', preserve_path_from : meson.current_source_dir())
call_flake_gen = gen_header.process('flake/call-flake.nix')
libexpr_generated_headers = [
gen_header.process('primops/derivation.nix', preserve_path_from : meson.current_source_dir()),
]
foreach header : [ 'imported-drv-to-derivation.nix', 'fetchurl.nix' ]
libexpr_generated_headers += custom_target(
command : [ 'bash', '-c', 'echo \'R"__NIX_STR(\' | cat - @INPUT@ && echo \')__NIX_STR"\'' ],
input : header,
output : '@PLAINNAME@.gen.hh',
capture : true,
install : true,
install_dir : includedir / 'nix',
)
endforeach
subdir('flake')
libexpr_sources = files(
'attr-path.cc',
@ -121,10 +131,7 @@ libexpr = library(
libexpr_sources,
parser_tab,
lexer_tab,
imported_drv_to_derivation_gen,
fetchurl_gen,
derivation_gen,
call_flake_gen,
libexpr_generated_headers,
dependencies : [
liblixutil,
liblixstore,
@ -132,6 +139,7 @@ libexpr = library(
boehm,
boost,
toml11,
nlohmann_json,
],
# for shared.hh
include_directories : [
@ -152,3 +160,17 @@ liblixexpr = declare_dependency(
include_directories : include_directories('.'),
link_with : libexpr,
)
# FIXME: not using the pkg-config module because it creates way too many deps
# while meson migration is in progress, and we want to not include boost here
configure_file(
input : 'nix-expr.pc.in',
output : 'nix-expr.pc',
install_dir : libdir / 'pkgconfig',
configuration : {
'prefix' : prefix,
'libdir' : libdir,
'includedir' : includedir,
'PACKAGE_VERSION' : meson.project_version(),
},
)

View file

@ -608,7 +608,7 @@ std::ostream & operator<<(std::ostream & output, const ValuePrinter & printer)
}
template<>
HintFmt & HintFmt::operator%(const ValuePrinter & value)
fmt_internal::HintFmt & fmt_internal::HintFmt::operator%(const ValuePrinter & value)
{
fmt % value;
return *this;

View file

@ -86,6 +86,6 @@ std::ostream & operator<<(std::ostream & output, const ValuePrinter & printer);
* magenta.
*/
template<>
HintFmt & HintFmt::operator%(const ValuePrinter & value);
fmt_internal::HintFmt & fmt_internal::HintFmt::operator%(const ValuePrinter & value);
}

View file

@ -28,6 +28,7 @@ libfetchers = library(
dependencies : [
liblixstore,
liblixutil,
nlohmann_json,
],
install : true,
# FIXME(Qyriad): is this right?

View file

@ -31,3 +31,17 @@ liblixmain = declare_dependency(
include_directories : include_directories('.'),
link_with : libmain,
)
# FIXME: not using the pkg-config module because it creates way too many deps
# while meson migration is in progress, and we want to not include boost here
configure_file(
input : 'nix-main.pc.in',
output : 'nix-main.pc',
install_dir : libdir / 'pkgconfig',
configuration : {
'prefix' : prefix,
'libdir' : libdir,
'includedir' : includedir,
'PACKAGE_VERSION' : meson.project_version(),
},
)

View file

@ -1,5 +1,14 @@
schema_sql_gen = gen_header.process('schema.sql')
ca_specific_schema_gen = gen_header.process('ca-specific-schema.sql')
libstore_generated_headers = []
foreach header : [ 'schema.sql', 'ca-specific-schema.sql' ]
libstore_generated_headers += custom_target(
command : [ 'bash', '-c', 'echo \'R"__NIX_STR(\' | cat - @INPUT@ && echo \')__NIX_STR"\'' ],
input : header,
output : '@PLAINNAME@.gen.hh',
capture : true,
install : true,
install_dir : includedir / 'nix',
)
endforeach
libstore_sources = files(
'binary-cache-store.cc',
@ -140,7 +149,7 @@ cpp_str_defines = {
'NIX_PREFIX': prefix,
'NIX_STORE_DIR': store_dir,
'NIX_DATA_DIR': datadir,
'NIX_STATE_DIR': state_dir,
'NIX_STATE_DIR': state_dir / 'nix',
'NIX_LOG_DIR': log_dir,
'NIX_CONF_DIR': sysconfdir,
'NIX_BIN_DIR': bindir,
@ -157,8 +166,7 @@ endforeach
libstore = library(
'nixstore',
schema_sql_gen,
ca_specific_schema_gen,
libstore_generated_headers,
libstore_sources,
dependencies : [
libarchive,
@ -172,6 +180,7 @@ libstore = library(
aws_sdk,
aws_s3,
aws_sdk_transfer,
nlohmann_json,
],
cpp_args : cpp_args,
install : true,
@ -186,3 +195,17 @@ liblixstore = declare_dependency(
include_directories : include_directories('.'),
link_with : libstore,
)
# FIXME: not using the pkg-config module because it creates way too many deps
# while meson migration is in progress, and we want to not include boost here
configure_file(
input : 'nix-store.pc.in',
output : 'nix-store.pc',
install_dir : libdir / 'pkgconfig',
configuration : {
'prefix' : prefix,
'libdir' : libdir,
'includedir' : includedir,
'PACKAGE_VERSION' : meson.project_version(),
},
)

View file

@ -0,0 +1,22 @@
#include <boost/io/ios_state.hpp>
#include <iomanip>
#include <iostream>
#include "escape-char.hh"
namespace nix {
std::ostream & operator<<(std::ostream & s, MaybeHexEscapedChar c)
{
boost::io::ios_flags_saver _ifs(s);
if (isprint(c.c)) {
s << static_cast<char>(c.c);
} else {
s << "\\x" << std::hex << std::setfill('0') << std::setw(2)
<< (static_cast<unsigned int>(c.c) & 0xff);
}
return s;
}
} // namespace nix

View file

@ -0,0 +1,22 @@
#pragma once
#include <ostream>
namespace nix {
/**
* A struct that prints a debug representation of a character, like `\x1f` for
* non-printable characters, or the character itself for other characters.
*
* Note that these are suitable for human readable output, but further care is
* necessary to include them in C++ strings to avoid running into adjacent
* hex-like characters. (`"puppy\x1bdoggy"` parses as `"puppy" "\x1bd" "oggy"`
* and errors because 0x1bd is too big for a `char`.)
*/
struct MaybeHexEscapedChar
{
char c;
};
std::ostream & operator<<(std::ostream & s, MaybeHexEscapedChar c);
} // namespace nix

View file

@ -5,43 +5,94 @@
#include <string>
#include "ansicolor.hh"
namespace nix {
namespace {
/**
* A helper for writing `boost::format` expressions.
* Values wrapped in this struct are printed in magenta.
*
* These are equivalent:
*
* ```
* formatHelper(formatter, a_0, ..., a_n)
* formatter % a_0 % ... % a_n
* ```
*
* With a single argument, `formatHelper(s)` is a no-op.
* By default, arguments to `HintFmt` are printed in magenta. To avoid this,
* either wrap the argument in `Uncolored` or add a specialization of
* `HintFmt::operator%`.
*/
template<class F>
inline void formatHelper(F & f)
{ }
template<class F, typename T, typename... Args>
inline void formatHelper(F & f, const T & x, const Args & ... args)
template<class T>
struct Magenta
{
// Interpolate one argument and then recurse.
formatHelper(f % x, args...);
Magenta(const T & s) : value(s) {}
const T & value;
};
template<class T>
std::ostream & operator<<(std::ostream & out, const Magenta<T> & y)
{
return out << ANSI_MAGENTA << y.value << ANSI_NORMAL;
}
/**
* Values wrapped in this class are printed without coloring.
*
* By default, arguments to `HintFmt` are printed in magenta (see `Magenta`).
*/
template<class T>
struct Uncolored
{
Uncolored(const T & s) : value(s) {}
const T & value;
};
template<class T>
std::ostream & operator<<(std::ostream & out, const Uncolored<T> & y)
{
return out << ANSI_NORMAL << y.value;
}
namespace fmt_internal {
/**
* Set the correct exceptions for `fmt`.
*/
void setExceptions(boost::format & fmt)
inline void setExceptions(boost::format & fmt)
{
fmt.exceptions(
boost::io::all_error_bits ^
boost::io::too_many_args_bit ^
boost::io::too_few_args_bit);
boost::io::all_error_bits ^ boost::io::too_many_args_bit ^ boost::io::too_few_args_bit
);
}
/**
* Helper class for `HintFmt` that supports the evil `operator%`.
*
* See: https://git.lix.systems/lix-project/lix/issues/178
*/
struct HintFmt
{
boost::format fmt;
template<typename... Args>
HintFmt(boost::format && fmt, const Args &... args) : fmt(std::move(fmt))
{
setExceptions(fmt);
(*this % ... % args);
}
template<class T>
HintFmt & operator%(const T & value)
{
fmt % Magenta(value);
return *this;
}
template<class T>
HintFmt & operator%(const Uncolored<T> & value)
{
fmt % value.value;
return *this;
}
boost::format into_format()
{
return std::move(fmt);
}
};
}
/**
@ -77,52 +128,14 @@ inline std::string fmt(const char * s)
}
template<typename... Args>
inline std::string fmt(const std::string & fs, const Args & ... args)
inline std::string fmt(const std::string & fs, const Args &... args)
{
boost::format f(fs);
setExceptions(f);
formatHelper(f, args...);
fmt_internal::setExceptions(f);
(f % ... % args);
return f.str();
}
/**
* Values wrapped in this struct are printed in magenta.
*
* By default, arguments to `HintFmt` are printed in magenta. To avoid this,
* either wrap the argument in `Uncolored` or add a specialization of
* `HintFmt::operator%`.
*/
template <class T>
struct Magenta
{
Magenta(const T &s) : value(s) {}
const T & value;
};
template <class T>
std::ostream & operator<<(std::ostream & out, const Magenta<T> & y)
{
return out << ANSI_WARNING << y.value << ANSI_NORMAL;
}
/**
* Values wrapped in this class are printed without coloring.
*
* By default, arguments to `HintFmt` are printed in magenta (see `Magenta`).
*/
template <class T>
struct Uncolored
{
Uncolored(const T & s) : value(s) {}
const T & value;
};
template <class T>
std::ostream & operator<<(std::ostream & out, const Uncolored<T> & y)
{
return out << ANSI_NORMAL << y.value;
}
/**
* A wrapper around `boost::format` which colors interpolated arguments in
* magenta by default.
@ -137,46 +150,28 @@ public:
* Format the given string literally, without interpolating format
* placeholders.
*/
HintFmt(const std::string & literal)
: HintFmt("%s", Uncolored(literal))
{ }
static HintFmt fromFormatString(const std::string & format) {
return HintFmt(boost::format(format));
}
HintFmt(const std::string & literal) : HintFmt("%s", Uncolored(literal)) {}
/**
* Interpolate the given arguments into the format string.
*/
template<typename... Args>
HintFmt(const std::string & format, const Args & ... args)
HintFmt(const std::string & format, const Args &... args)
: HintFmt(boost::format(format), args...)
{ }
{
}
HintFmt(const HintFmt & hf)
: fmt(hf.fmt)
{ }
HintFmt(const HintFmt & hf) : fmt(hf.fmt) {}
template<typename... Args>
HintFmt(boost::format && fmt, const Args & ... args)
: fmt(std::move(fmt))
HintFmt(boost::format && fmt, const Args &... args)
: fmt(fmt_internal::HintFmt(std::move(fmt), args...).into_format())
{
setExceptions(fmt);
formatHelper(*this, args...);
if (this->fmt.remaining_args() != 0) {
throw boost::io::too_few_args(
this->fmt.bound_args() + this->fmt.fed_args(), this->fmt.expected_args()
);
}
template<class T>
HintFmt & operator%(const T & value)
{
fmt % Magenta(value);
return *this;
}
template<class T>
HintFmt & operator%(const Uncolored<T> & value)
{
fmt % value.value;
return *this;
}
std::string str() const

View file

@ -230,9 +230,7 @@ extern Verbosity verbosity;
template<typename... Args>
inline void warn(const std::string & fs, const Args & ... args)
{
boost::format f(fs);
formatHelper(f, args...);
logger->warn(f.str());
logger->warn(HintFmt(fs, args...).str());
}
#define warnOnce(haveWarned, args...) \

View file

@ -8,6 +8,7 @@ libutil_sources = files(
'config.cc',
'english.cc',
'error.cc',
'escape-char.cc',
'exit.cc',
'experimental-features.cc',
'filesystem.cc',
@ -35,6 +36,7 @@ libutil_headers = files(
'abstract-setting-to-json.hh',
'ansicolor.hh',
'archive.hh',
'args/root.hh',
'args.hh',
'box_ptr.hh',
'callback.hh',
@ -49,6 +51,7 @@ libutil_headers = files(
'config.hh',
'english.hh',
'error.hh',
'escape-char.hh',
'exit.hh',
'experimental-features.hh',
'experimental-features-json.hh',
@ -102,6 +105,7 @@ libutil = library(
libarchive,
brotli,
openssl,
nlohmann_json,
],
implicit_include_directories : true,
install : true,

View file

@ -87,6 +87,7 @@ nix = executable(
liblixfetchers,
liblixmain,
boehm,
nlohmann_json,
],
install : true,
# FIXME(Qyriad): is this right?

View file

@ -176,5 +176,8 @@ foreach script : functional_tests_scripts
env : {
'MESON_BUILD_ROOT': meson.project_build_root(),
},
# some tests take 15+ seconds even on an otherwise idle machine, on a loaded machine
# this can easily drive them to failure. give them more time, 5min rather than 30sec
timeout : 300,
)
endforeach

View file

@ -3,7 +3,7 @@
#include "test-session.hh"
#include "util.hh"
#include "tests/debug-char.hh"
#include "escape-char.hh"
namespace nix {
@ -60,7 +60,7 @@ std::ostream & operator<<(std::ostream & os, ReplOutputParser::State s)
void ReplOutputParser::transition(State new_state, char responsible_char, bool wasPrompt)
{
if constexpr (DEBUG_REPL_PARSER) {
std::cerr << "transition " << new_state << " for " << DebugChar{responsible_char}
std::cerr << "transition " << new_state << " for " << MaybeHexEscapedChar{responsible_char}
<< (wasPrompt ? " [prompt]" : "") << "\n";
}
state = new_state;
@ -118,7 +118,7 @@ bool TestSession::waitForPrompt()
});
if constexpr (DEBUG_REPL_PARSER) {
std::cerr << "raw " << DebugChar{buf[i]} << (wasEaten ? " [eaten]" : "") << "\n";
std::cerr << "raw " << MaybeHexEscapedChar{buf[i]} << (wasEaten ? " [eaten]" : "") << "\n";
}
}

View file

@ -1,6 +1,6 @@
#include "cli-literate-parser.hh"
#include "libexpr/print.hh"
#include "debug-char.hh"
#include "escape-char.hh"
#include "types.hh"
#include "util.hh"
#include <ranges>
@ -77,7 +77,7 @@ CLILiterateParser::CLILiterateParser(std::string prompt, size_t indent)
void CLILiterateParser::feed(char c)
{
if constexpr (DEBUG_PARSER) {
std::cout << stateDebug(state_) << " " << DebugChar{c} << "\n";
std::cout << stateDebug(state_) << " " << MaybeHexEscapedChar{c} << "\n";
}
if (c == '\n') {

View file

@ -1,24 +0,0 @@
///@file
#include <ostream>
#include <boost/io/ios_state.hpp>
namespace nix {
struct DebugChar
{
char c;
};
inline std::ostream & operator<<(std::ostream & s, DebugChar c)
{
boost::io::ios_flags_saver _ifs(s);
if (isprint(c.c)) {
s << static_cast<char>(c.c);
} else {
s << std::hex << "0x" << (static_cast<unsigned int>(c.c) & 0xff);
}
return s;
}
}

View file

@ -1,5 +1,5 @@
#include "terminal-code-eater.hh"
#include "debug-char.hh"
#include "escape-char.hh"
#include <assert.h>
#include <cstdint>
#include <iostream>
@ -14,7 +14,7 @@ void TerminalCodeEater::feed(char c, std::function<void(char)> on_char)
auto isIntermediateChar = [](char v) -> bool { return v >= 0x20 && v <= 0x2f; };
auto isFinalChar = [](char v) -> bool { return v >= 0x40 && v <= 0x7e; };
if constexpr (DEBUG_EATER) {
std::cerr << "eater" << DebugChar{c} << "\n";
std::cerr << "eater" << MaybeHexEscapedChar{c} << "\n";
}
switch (state) {
@ -28,7 +28,7 @@ void TerminalCodeEater::feed(char c, std::function<void(char)> on_char)
return;
}
if constexpr (DEBUG_EATER) {
std::cerr << "eater uneat" << DebugChar{c} << "\n";
std::cerr << "eater uneat" << MaybeHexEscapedChar{c} << "\n";
}
on_char(c);
break;

23
tests/unit/libutil/fmt.cc Normal file
View file

@ -0,0 +1,23 @@
#include "fmt.hh"
#include "ansicolor.hh"
#include <gtest/gtest.h>
namespace nix {
TEST(HintFmt, arg_count)
{
// Single arg is treated as a literal string.
ASSERT_EQ(HintFmt("%s").str(), "%s");
// Other strings format as expected:
ASSERT_EQ(HintFmt("%s", 1).str(), ANSI_MAGENTA "1" ANSI_NORMAL);
ASSERT_EQ(HintFmt("%1%", "hello").str(), ANSI_MAGENTA "hello" ANSI_NORMAL);
// Argument counts are detected at construction.
ASSERT_THROW(HintFmt("%s %s", 1), boost::io::too_few_args);
ASSERT_THROW(HintFmt("%s", 1, 2), boost::io::too_many_args);
}
}

View file

@ -63,6 +63,7 @@ libutil_tester = executable(
liblixutil,
liblixexpr,
liblixutil_test_support,
nlohmann_json,
],
)
@ -127,6 +128,7 @@ libstore_tester = executable(
liblixutil,
rapidcheck,
gtest,
nlohmann_json,
],
)
@ -191,6 +193,7 @@ libexpr_tester = executable(
rapidcheck,
boehm,
gtest,
nlohmann_json,
],
)