Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Separate input modes for --help, --license and --version #12118

Merged
merged 3 commits into from
Nov 3, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
79 changes: 54 additions & 25 deletions solc/CommandLineInterface.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
*/
#include <solc/CommandLineInterface.h>

#include "license.h"
#include "solidity/BuildInfo.h"

#include <libsolidity/interface/Version.h>
Expand Down Expand Up @@ -405,6 +406,13 @@ bool CommandLineInterface::readInputFiles()
{
solAssert(!m_standardJsonInput.has_value(), "");

if (
m_options.input.mode == InputMode::Help ||
m_options.input.mode == InputMode::License ||
m_options.input.mode == InputMode::Version
)
return true;

m_fileReader.setBasePath(m_options.input.basePath);

if (m_fileReader.basePath() != "")
Expand Down Expand Up @@ -573,8 +581,18 @@ void CommandLineInterface::createJson(string const& _fileName, string const& _js

bool CommandLineInterface::parseArguments(int _argc, char const* const* _argv)
{
CommandLineParser parser(sout(/* _markAsUsed */ false), serr(/* _markAsUsed */ false));
bool success = parser.parse(_argc, _argv, isatty(fileno(stdin)));
CommandLineParser parser(serr(/* _markAsUsed */ false));

if (isatty(fileno(stdin)) && _argc == 1)
{
// If the terminal is taking input from the user, provide more user-friendly output.
CommandLineParser::printHelp(sout());

// In this case we want to exit with an error but not display any error message.
return false;
}

bool success = parser.parse(_argc, _argv);
if (!success)
return false;
m_hasOutput = m_hasOutput || parser.hasOutput();
Expand All @@ -587,28 +605,54 @@ bool CommandLineInterface::processInput()
{
switch (m_options.input.mode)
{
case InputMode::Help:
CommandLineParser::printHelp(sout());
break;
case InputMode::License:
printLicense();
break;
case InputMode::Version:
printVersion();
break;
case InputMode::StandardJson:
{
solAssert(m_standardJsonInput.has_value(), "");

StandardCompiler compiler(m_fileReader.reader(), m_options.formatting.json);
sout() << compiler.compile(move(m_standardJsonInput.value())) << endl;
m_standardJsonInput.reset();
return true;
break;
}
case InputMode::Assembler:
{
return assemble(m_options.assembly.inputLanguage, m_options.assembly.targetMachine);
}
if (!assemble(m_options.assembly.inputLanguage, m_options.assembly.targetMachine))
return false;
break;
case InputMode::Linker:
return link();
if (!link())
return false;
writeLinkedFiles();
break;
case InputMode::Compiler:
case InputMode::CompilerWithASTImport:
return compile();
if (!compile())
return false;
outputCompilationResults();
}

solAssert(false, "");
return false;
return !m_outputFailed;
}

void CommandLineInterface::printVersion()
{
sout() << "solc, the solidity compiler commandline interface" << endl;
sout() << "Version: " << solidity::frontend::VersionString << endl;
}

void CommandLineInterface::printLicense()
{
sout() << otherLicenses << endl;
// This is a static variable generated by cmake from LICENSE.txt
sout() << licenseText << endl;
}

bool CommandLineInterface::compile()
Expand Down Expand Up @@ -843,21 +887,6 @@ void CommandLineInterface::handleAst()
}
}

bool CommandLineInterface::actOnInput()
{
if (m_options.input.mode == InputMode::StandardJson || m_options.input.mode == InputMode::Assembler)
// Already done in "processInput" phase.
return true;
else if (m_options.input.mode == InputMode::Linker)
writeLinkedFiles();
else
{
solAssert(m_options.input.mode == InputMode::Compiler || m_options.input.mode == InputMode::CompilerWithASTImport, "");
outputCompilationResults();
}
return !m_outputFailed;
}

bool CommandLineInterface::link()
{
solAssert(m_options.input.mode == InputMode::Linker, "");
Expand Down
7 changes: 3 additions & 4 deletions solc/CommandLineInterface.h
Original file line number Diff line number Diff line change
Expand Up @@ -55,17 +55,16 @@ class CommandLineInterface
bool parseArguments(int _argc, char const* const* _argv);
/// Read the content of all input files and initialize the file reader.
bool readInputFiles();
/// Parse the files and create source code objects
/// Parse the files, create source code objects, print the output.
bool processInput();
/// Perform actions on the input depending on provided compiler arguments
/// @returns true on success.
bool actOnInput();

CommandLineOptions const& options() const { return m_options; }
FileReader const& fileReader() const { return m_fileReader; }
std::optional<std::string> const& standardJsonInput() const { return m_standardJsonInput; }

private:
void printVersion();
void printLicense();
bool compile();
bool link();
void writeLinkedFiles();
Expand Down
69 changes: 24 additions & 45 deletions solc/CommandLineParser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,6 @@
*/
// SPDX-License-Identifier: GPL-3.0

#include "license.h"

#include <solc/CommandLineParser.h>
#include <libyul/optimiser/Suite.h>
#include <liblangutil/EVMVersion.h>
Expand All @@ -36,19 +34,12 @@ namespace po = boost::program_options;
namespace solidity::frontend
{

ostream& CommandLineParser::sout()
{
m_hasOutput = true;
return m_sout;
}

ostream& CommandLineParser::serr()
{
m_hasOutput = true;
return m_serr;
}

#define cout
#define cerr

static string const g_strAllowPaths = "allow-paths";
Expand Down Expand Up @@ -147,26 +138,6 @@ static map<InputMode, string> const g_inputModeName = {
{InputMode::Linker, "linker"},
};

void CommandLineParser::printVersionAndExit()
{
sout() <<
"solc, the solidity compiler commandline interface" <<
endl <<
"Version: " <<
solidity::frontend::VersionString <<
endl;
exit(EXIT_SUCCESS);
}

void CommandLineParser::printLicenseAndExit()
{
sout() << otherLicenses << endl;
// This is a static variable generated by cmake from LICENSE.txt
sout() << licenseText << endl;
exit(EXIT_SUCCESS);
}


bool CommandLineParser::checkMutuallyExclusive(vector<string> const& _optionNames)
{
if (countEnabledOptions(_optionNames) > 1)
Expand Down Expand Up @@ -297,11 +268,11 @@ OptimiserSettings CommandLineOptions::optimiserSettings() const
return settings;
}

bool CommandLineParser::parse(int _argc, char const* const* _argv, bool _interactiveTerminal)
bool CommandLineParser::parse(int _argc, char const* const* _argv)
{
m_hasOutput = false;

if (!parseArgs(_argc, _argv, _interactiveTerminal))
if (!parseArgs(_argc, _argv))
return false;

return processArgs();
Expand Down Expand Up @@ -482,6 +453,10 @@ bool CommandLineParser::parseOutputSelection()

switch (_mode)
{
case InputMode::Help:
case InputMode::License:
case InputMode::Version:
solAssert(false);
case InputMode::Compiler:
case InputMode::CompilerWithASTImport:
return contains(compilerModeOutputs, _outputName);
Expand Down Expand Up @@ -847,7 +822,7 @@ po::positional_options_description CommandLineParser::positionalOptionsDescripti
return filesPositions;
}

bool CommandLineParser::parseArgs(int _argc, char const* const* _argv, bool _interactiveTerminal)
bool CommandLineParser::parseArgs(int _argc, char const* const* _argv)
{
po::options_description allOptions = optionsDescription();
po::positional_options_description filesPositions = positionalOptionsDescription();
Expand All @@ -866,18 +841,6 @@ bool CommandLineParser::parseArgs(int _argc, char const* const* _argv, bool _int
return false;
}

if (m_args.count(g_strHelp) || (_interactiveTerminal && _argc == 1))
{
sout() << allOptions;
return false;
}

if (m_args.count(g_strVersion))
printVersionAndExit();

if (m_args.count(g_strLicense))
printLicenseAndExit();

po::notify(m_args);

return true;
Expand All @@ -886,6 +849,9 @@ bool CommandLineParser::parseArgs(int _argc, char const* const* _argv, bool _int
bool CommandLineParser::processArgs()
{
if (!checkMutuallyExclusive({
g_strHelp,
g_strLicense,
g_strVersion,
g_strStandardJSON,
g_strLink,
g_strAssemble,
Expand All @@ -895,7 +861,13 @@ bool CommandLineParser::processArgs()
}))
return false;

if (m_args.count(g_strStandardJSON) > 0)
if (m_args.count(g_strHelp) > 0)
m_options.input.mode = InputMode::Help;
else if (m_args.count(g_strLicense) > 0)
m_options.input.mode = InputMode::License;
else if (m_args.count(g_strVersion) > 0)
m_options.input.mode = InputMode::Version;
else if (m_args.count(g_strStandardJSON) > 0)
m_options.input.mode = InputMode::StandardJson;
else if (m_args.count(g_strAssemble) > 0 || m_args.count(g_strStrictAssembly) > 0 || m_args.count(g_strYul) > 0)
m_options.input.mode = InputMode::Assembler;
Expand All @@ -906,6 +878,13 @@ bool CommandLineParser::processArgs()
else
m_options.input.mode = InputMode::Compiler;

if (
m_options.input.mode == InputMode::Help ||
m_options.input.mode == InputMode::License ||
m_options.input.mode == InputMode::Version
)
return true;

map<string, set<InputMode>> validOptionInputModeCombinations = {
// TODO: This should eventually contain all options.
{g_strErrorRecovery, {InputMode::Compiler, InputMode::CompilerWithASTImport}},
Expand Down
28 changes: 9 additions & 19 deletions solc/CommandLineParser.h
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,9 @@ namespace solidity::frontend

enum class InputMode
{
Help,
License,
Version,
Compiler,
CompilerWithASTImport,
StandardJson,
Expand Down Expand Up @@ -230,34 +233,28 @@ struct CommandLineOptions

/// Parses the command-line arguments and produces a filled-out CommandLineOptions structure.
/// Validates provided values and prints error messages in case of errors.
///
/// The class is also responsible for handling options that only result in printing informational
/// text, without the need to invoke the compiler - printing usage banner, version or license.
class CommandLineParser
{
public:
explicit CommandLineParser(std::ostream& _sout, std::ostream& _serr):
m_sout(_sout),
explicit CommandLineParser(std::ostream& _serr):
m_serr(_serr)
{}

/// Parses the command-line arguments and fills out the internal CommandLineOptions structure.
/// Performs validation and prints error messages. If requested, prints usage banner, version
/// or license.
/// @param interactiveTerminal specifies whether the terminal is taking input from the user.
/// This is used to determine whether to provide more user-friendly output in some situations.
/// E.g. whether to print help text when no arguments are provided.
/// Performs validation and prints error messages.
/// @return true if there were no validation errors when parsing options and the
/// CommandLineOptions structure has been fully initialized. false if there were errors - in
/// this case CommandLineOptions may be only partially filled out. May also return false if
/// there is not further processing necessary and the program should just exit.
bool parse(int _argc, char const* const* _argv, bool _interactiveTerminal);
bool parse(int _argc, char const* const* _argv);

CommandLineOptions const& options() const { return m_options; }

/// Returns true if the parser has written anything to any of its output streams.
bool hasOutput() const { return m_hasOutput; }

static void printHelp(std::ostream& _out) { _out << optionsDescription(); }

private:
/// @returns a specification of all named command-line options accepted by the compiler.
/// The object can be used to parse command-line arguments or to generate the help screen.
Expand All @@ -270,7 +267,7 @@ class CommandLineParser
/// Uses boost::program_options to parse the command-line arguments and leaves the result in @a m_args.
/// Also handles the arguments that result in information being printed followed by immediate exit.
/// @returns false if parsing fails due to syntactical errors or the arguments not matching the description.
bool parseArgs(int _argc, char const* const* _argv, bool _interactiveTerminal);
bool parseArgs(int _argc, char const* const* _argv);

/// Validates parsed arguments stored in @a m_args and fills out the internal CommandLineOptions
/// structure.
Expand All @@ -294,20 +291,13 @@ class CommandLineParser
bool parseOutputSelection();

bool checkMutuallyExclusive(std::vector<std::string> const& _optionNames);
[[noreturn]] void printVersionAndExit();
[[noreturn]] void printLicenseAndExit();
size_t countEnabledOptions(std::vector<std::string> const& _optionNames) const;
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;

Expand Down
3 changes: 1 addition & 2 deletions solc/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -65,8 +65,7 @@ int main(int argc, char** argv)
bool success =
cli.parseArguments(argc, argv) &&
cli.readInputFiles() &&
cli.processInput() &&
cli.actOnInput();
cli.processInput();

return success ? 0 : 1;
}
Expand Down
Loading