CommandLineParser: Replace global sout/serr streams with class members

- This removes the global variable and prevents stderr/stdout from being printed in tests
This commit is contained in:
Kamil Śliwak 2021-06-10 17:45:09 +02:00
parent ce11ebb687
commit 6c33fbcb6a
4 changed files with 58 additions and 40 deletions

View File

@ -497,11 +497,11 @@ void CommandLineInterface::createJson(string const& _fileName, string const& _js
bool CommandLineInterface::parseArguments(int _argc, char const* const* _argv) bool CommandLineInterface::parseArguments(int _argc, char const* const* _argv)
{ {
CommandLineParser parser; CommandLineParser parser(sout(false), serr(false));
bool success = parser.parse(_argc, _argv, isatty(fileno(stdin))); bool success = parser.parse(_argc, _argv, isatty(fileno(stdin)));
if (!success) if (!success)
return false; return false;
g_hasOutput = g_hasOutput || CommandLineParser::hasOutput(); g_hasOutput = g_hasOutput || parser.hasOutput();
m_options = parser.options(); m_options = parser.options();
return true; return true;

View File

@ -34,24 +34,16 @@ namespace po = boost::program_options;
namespace solidity::frontend namespace solidity::frontend
{ {
static bool g_hasOutput = false; ostream& CommandLineParser::sout()
namespace
{ {
m_hasOutput = true;
std::ostream& sout() return m_sout;
{
g_hasOutput = true;
return cout;
} }
std::ostream& serr(bool _used = true) ostream& CommandLineParser::serr()
{ {
if (_used) m_hasOutput = true;
g_hasOutput = true; return m_serr;
return cerr;
}
} }
#define cout #define cout
@ -233,8 +225,7 @@ static set<string> const g_metadataHashArgs
g_strNone g_strNone
}; };
[[noreturn]] void CommandLineParser::printVersionAndExit()
static void printVersionAndExit()
{ {
sout() << sout() <<
"solc, the solidity compiler commandline interface" << "solc, the solidity compiler commandline interface" <<
@ -245,8 +236,7 @@ static void printVersionAndExit()
exit(EXIT_SUCCESS); exit(EXIT_SUCCESS);
} }
[[noreturn]] void CommandLineParser::printLicenseAndExit()
static void printLicenseAndExit()
{ {
sout() << otherLicenses << endl; sout() << otherLicenses << endl;
// This is a static variable generated by cmake from LICENSE.txt // This is a static variable generated by cmake from LICENSE.txt
@ -254,10 +244,8 @@ static void printLicenseAndExit()
exit(EXIT_SUCCESS); exit(EXIT_SUCCESS);
} }
namespace
{
bool checkMutuallyExclusive(boost::program_options::variables_map const& args, std::string const& _optionA, std::string const& _optionB) bool CommandLineParser::checkMutuallyExclusive(boost::program_options::variables_map const& args, string const& _optionA, string const& _optionB)
{ {
if (args.count(_optionA) && args.count(_optionB)) if (args.count(_optionA) && args.count(_optionB))
{ {
@ -268,8 +256,6 @@ bool checkMutuallyExclusive(boost::program_options::variables_map const& args, s
return true; return true;
} }
}
bool CompilerOutputs::operator==(CompilerOutputs const& _other) const noexcept bool CompilerOutputs::operator==(CompilerOutputs const& _other) const noexcept
{ {
static_assert( static_assert(
@ -489,7 +475,7 @@ bool CommandLineParser::parseLibraryOption(string const& _input)
bool CommandLineParser::parse(int _argc, char const* const* _argv, bool interactiveTerminal) bool CommandLineParser::parse(int _argc, char const* const* _argv, bool interactiveTerminal)
{ {
g_hasOutput = false; m_hasOutput = false;
// Declare the supported options. // Declare the supported options.
po::options_description desc((R"(solc, the Solidity commandline compiler. po::options_description desc((R"(solc, the Solidity commandline compiler.
@ -1186,11 +1172,6 @@ General Information)").c_str(),
return true; return true;
} }
bool CommandLineParser::hasOutput()
{
return g_hasOutput;
}
bool CommandLineParser::parseCombinedJsonOption() bool CommandLineParser::parseCombinedJsonOption()
{ {
if (!m_args.count(g_argCombinedJson)) if (!m_args.count(g_argCombinedJson))
@ -1242,4 +1223,5 @@ string CommandLineParser::joinOptionNames(vector<string> const& _optionNames, st
_separator _separator
); );
} }
} // namespace solidity::frontend } // namespace solidity::frontend

View File

@ -33,6 +33,7 @@
#include <map> #include <map>
#include <memory> #include <memory>
#include <optional> #include <optional>
#include <ostream>
#include <set> #include <set>
#include <string> #include <string>
#include <vector> #include <vector>
@ -177,6 +178,11 @@ struct CommandLineOptions
class CommandLineParser class CommandLineParser
{ {
public: public:
explicit CommandLineParser(std::ostream& _sout, std::ostream& _serr):
m_sout(_sout),
m_serr(_serr)
{}
/// Parses the command-line arguments and fills out the internal CommandLineOptions structure. /// Parses the command-line arguments and fills out the internal CommandLineOptions structure.
/// Performs validation and prints error messages. If requested, prints usage banner, version /// Performs validation and prints error messages. If requested, prints usage banner, version
/// or license. /// or license.
@ -191,9 +197,8 @@ public:
CommandLineOptions const& options() const { return m_options; } CommandLineOptions const& options() const { return m_options; }
/// Returns true if any parser instance has written anything to cout or cerr since the last /// Returns true if the parser has written anything to any of its output streams.
/// call to parse(). bool hasOutput() const { return m_hasOutput; }
static bool hasOutput();
private: private:
/// Parses the value supplied to --combined-json. /// Parses the value supplied to --combined-json.
@ -210,9 +215,28 @@ private:
/// @return false if there are any validation errors, true otherwise. /// @return false if there are any validation errors, true otherwise.
bool parseLibraryOption(std::string const& _input); bool parseLibraryOption(std::string const& _input);
bool checkMutuallyExclusive(
boost::program_options::variables_map const& args,
std::string const& _optionA,
std::string const& _optionB
);
[[noreturn]] void printVersionAndExit();
[[noreturn]] void printLicenseAndExit();
size_t countEnabledOptions(std::vector<std::string> const& _optionNames) const; size_t countEnabledOptions(std::vector<std::string> const& _optionNames) const;
static std::string joinOptionNames(std::vector<std::string> const& _optionNames, std::string _separator = ", "); static std::string joinOptionNames(std::vector<std::string> const& _optionNames, std::string _separator = ", ");
/// Returns the stream that should receive normal output. Sets m_hasOutput to true if the
/// stream has ever been used.
std::ostream& sout();
/// Returns the stream that should receive error output. Sets m_hasOutput to true if the
/// stream has ever been used.
std::ostream& serr();
std::ostream& m_sout;
std::ostream& m_serr;
bool m_hasOutput = false;
CommandLineOptions m_options; CommandLineOptions m_options;
/// Map of command-line arguments produced by boost::program_options. /// Map of command-line arguments produced by boost::program_options.

View File

@ -46,7 +46,7 @@ using namespace solidity::yul;
namespace namespace
{ {
optional<CommandLineOptions> parseCommandLine(vector<string> const& commandLine) optional<CommandLineOptions> parseCommandLine(vector<string> const& commandLine, ostream& _stdout, ostream& _stderr)
{ {
size_t argc = commandLine.size(); size_t argc = commandLine.size();
vector<char const*> argv(argc + 1); vector<char const*> argv(argc + 1);
@ -57,7 +57,7 @@ optional<CommandLineOptions> parseCommandLine(vector<string> const& commandLine)
for (size_t i = 0; i < argc; ++i) for (size_t i = 0; i < argc; ++i)
argv[i] = commandLine[i].c_str(); argv[i] = commandLine[i].c_str();
CommandLineParser cliParser; CommandLineParser cliParser(_stdout, _stderr);
bool success = cliParser.parse( bool success = cliParser.parse(
static_cast<int>(argc), static_cast<int>(argc),
argv.data(), argv.data(),
@ -93,8 +93,11 @@ BOOST_AUTO_TEST_CASE(no_options)
nullopt, nullopt,
}; };
optional<CommandLineOptions> parsedOptions = parseCommandLine(commandLine); stringstream sout, serr;
optional<CommandLineOptions> parsedOptions = parseCommandLine(commandLine, sout, serr);
BOOST_TEST(sout.str() == "");
BOOST_TEST(serr.str() == "");
BOOST_REQUIRE(parsedOptions.has_value()); BOOST_REQUIRE(parsedOptions.has_value());
BOOST_TEST((parsedOptions.value() == expectedOptions)); BOOST_TEST((parsedOptions.value() == expectedOptions));
} }
@ -202,8 +205,11 @@ BOOST_AUTO_TEST_CASE(cli_mode_options)
5, 5,
}; };
optional<CommandLineOptions> parsedOptions = parseCommandLine(commandLine); stringstream sout, serr;
optional<CommandLineOptions> parsedOptions = parseCommandLine(commandLine, sout, serr);
BOOST_TEST(sout.str() == "");
BOOST_TEST(serr.str() == "");
BOOST_REQUIRE(parsedOptions.has_value()); BOOST_REQUIRE(parsedOptions.has_value());
BOOST_TEST((parsedOptions.value() == expectedOptions)); BOOST_TEST((parsedOptions.value() == expectedOptions));
} }
@ -316,8 +322,11 @@ BOOST_AUTO_TEST_CASE(assembly_mode_options)
expectedOptions.optimizer.yulSteps = "agf"; expectedOptions.optimizer.yulSteps = "agf";
} }
optional<CommandLineOptions> parsedOptions = parseCommandLine(commandLine); stringstream sout, serr;
optional<CommandLineOptions> parsedOptions = parseCommandLine(commandLine, sout, serr);
BOOST_TEST(sout.str() == "");
BOOST_TEST(serr.str() == "Warning: Yul is still experimental. Please use the output with care.\n");
BOOST_REQUIRE(parsedOptions.has_value()); BOOST_REQUIRE(parsedOptions.has_value());
BOOST_TEST((parsedOptions.value() == expectedOptions)); BOOST_TEST((parsedOptions.value() == expectedOptions));
@ -388,8 +397,11 @@ BOOST_AUTO_TEST_CASE(standard_json_mode_options)
expectedOptions.compiler.combinedJsonRequests->abi = true; expectedOptions.compiler.combinedJsonRequests->abi = true;
expectedOptions.compiler.combinedJsonRequests->binary = true; expectedOptions.compiler.combinedJsonRequests->binary = true;
optional<CommandLineOptions> parsedOptions = parseCommandLine(commandLine); stringstream sout, serr;
optional<CommandLineOptions> parsedOptions = parseCommandLine(commandLine, sout, serr);
BOOST_TEST(sout.str() == "");
BOOST_TEST(serr.str() == "");
BOOST_REQUIRE(parsedOptions.has_value()); BOOST_REQUIRE(parsedOptions.has_value());
BOOST_TEST((parsedOptions.value() == expectedOptions)); BOOST_TEST((parsedOptions.value() == expectedOptions));
} }