mirror of https://github.com/djcb/mu.git
utils: rework running system commands
Use g_spawn and pass arguments, so we don't involve a shell that needs escaping etc. Improve error handling.
This commit is contained in:
parent
5efd0a61aa
commit
cf6c5a36d7
|
@ -115,24 +115,6 @@ Mu::allow_warnings()
|
||||||
{});
|
{});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Result<int>
|
|
||||||
Mu::run_mu_command(const std::string& args)
|
|
||||||
{
|
|
||||||
auto cmdline{mu_format("/bin/sh -c '{} {}'", MU_PROGRAM, args)};
|
|
||||||
if (g_test_verbose())
|
|
||||||
mu_println("command-line: {}", cmdline);
|
|
||||||
|
|
||||||
GError *err{};
|
|
||||||
int wait_status{};
|
|
||||||
if (!g_spawn_command_line_sync(cmdline.c_str(), {}, {}, &wait_status, &err))
|
|
||||||
return Err(Error::Code::File, &err, "failed to execute command '{}", cmdline);
|
|
||||||
else
|
|
||||||
return Ok(WEXITSTATUS(wait_status));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
Mu::TempDir::TempDir(bool autodelete): autodelete_{autodelete} {
|
Mu::TempDir::TempDir(bool autodelete): autodelete_{autodelete} {
|
||||||
|
|
||||||
if (auto res{make_temp_dir()}; !res)
|
if (auto res{make_temp_dir()}; !res)
|
||||||
|
|
|
@ -20,6 +20,7 @@
|
||||||
#ifndef MU_TEST_UTILS_HH__
|
#ifndef MU_TEST_UTILS_HH__
|
||||||
#define MU_TEST_UTILS_HH__
|
#define MU_TEST_UTILS_HH__
|
||||||
|
|
||||||
|
#include <initializer_list>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <utils/mu-utils.hh>
|
#include <utils/mu-utils.hh>
|
||||||
#include <utils/mu-result.hh>
|
#include <utils/mu-result.hh>
|
||||||
|
@ -91,6 +92,14 @@ bool set_en_us_utf8_locale();
|
||||||
} \
|
} \
|
||||||
} while(0)
|
} while(0)
|
||||||
|
|
||||||
|
#define assert_valid_command(RCO) do { \
|
||||||
|
assert_valid_result(RCO); \
|
||||||
|
if ((RCO)->exit_code != 0 && !(RCO)->standard_err.empty()) \
|
||||||
|
mu_printerrln("{}:{}: {}", \
|
||||||
|
__FILE__, __LINE__, (RCO)->standard_err); \
|
||||||
|
g_assert_cmpuint((RCO)->exit_code, ==, 0); \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* For unit-tests, allow warnings in the current function.
|
* For unit-tests, allow warnings in the current function.
|
||||||
*
|
*
|
||||||
|
@ -98,18 +107,6 @@ bool set_en_us_utf8_locale();
|
||||||
void allow_warnings();
|
void allow_warnings();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Execute the in-tree mu executable with the arguments
|
|
||||||
* Asserts if fails.
|
|
||||||
*
|
|
||||||
* @param args arguments;;
|
|
||||||
*
|
|
||||||
* @return either the exit code or an error.
|
|
||||||
*/
|
|
||||||
Result<int> run_mu_command(const std::string& args);
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* For unit-tests, a RAII tempdir.
|
* For unit-tests, a RAII tempdir.
|
||||||
*
|
*
|
||||||
|
|
|
@ -277,6 +277,37 @@ Mu::read_from_stdin()
|
||||||
g_memory_output_stream_get_size(G_MEMORY_OUTPUT_STREAM(outmem))});
|
g_memory_output_stream_get_size(G_MEMORY_OUTPUT_STREAM(outmem))});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Result<Mu::CommandOutput>
|
||||||
|
Mu::run_command(std::initializer_list<std::string> args)
|
||||||
|
{
|
||||||
|
std::vector<char*> argvec{};
|
||||||
|
for (auto&& arg: args)
|
||||||
|
argvec.push_back(g_strdup(arg.c_str()));
|
||||||
|
argvec.push_back({});
|
||||||
|
|
||||||
|
GError *err{};
|
||||||
|
int wait_status{};
|
||||||
|
gchar *std_out{}, *std_err{};
|
||||||
|
auto res = g_spawn_sync({},
|
||||||
|
static_cast<char**>(argvec.data()),
|
||||||
|
{},
|
||||||
|
(GSpawnFlags)(G_SPAWN_SEARCH_PATH),
|
||||||
|
{}, {},
|
||||||
|
&std_out, &std_err,
|
||||||
|
&wait_status, &err);
|
||||||
|
|
||||||
|
for (auto& a: argvec)
|
||||||
|
g_free(a);
|
||||||
|
|
||||||
|
if (!res)
|
||||||
|
return Err(Error::Code::File, &err, "failed to execute command");
|
||||||
|
else
|
||||||
|
return Ok(Mu::CommandOutput{
|
||||||
|
WEXITSTATUS(wait_status),
|
||||||
|
to_string_gchar(std::move(std_out/*consumed*/)),
|
||||||
|
to_string_gchar(std::move(std_err/*consumed*/))});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
#ifdef BUILD_TESTS
|
#ifdef BUILD_TESTS
|
||||||
|
|
||||||
|
|
|
@ -196,6 +196,22 @@ Result<std::string> make_temp_dir();
|
||||||
Result<void> remove_directory(const std::string& path);
|
Result<void> remove_directory(const std::string& path);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Run some system command
|
||||||
|
*
|
||||||
|
* @param cmd the command
|
||||||
|
*
|
||||||
|
* @return Ok(exit code) or an error. Note that exit-code != 0 is _not_
|
||||||
|
* considered an error from the perspective of this function.
|
||||||
|
*/
|
||||||
|
struct CommandOutput {
|
||||||
|
int exit_code;
|
||||||
|
std::string standard_out;
|
||||||
|
std::string standard_err;
|
||||||
|
};
|
||||||
|
Result<CommandOutput> run_command(std::initializer_list<std::string> args);
|
||||||
|
|
||||||
|
|
||||||
} // namespace Mu
|
} // namespace Mu
|
||||||
|
|
||||||
#endif /* MU_UTILS_FILE_HH__ */
|
#endif /* MU_UTILS_FILE_HH__ */
|
||||||
|
|
Loading…
Reference in New Issue