mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Process input files in Standard JSON mode just like in other modes
- This makes `-` for stdin work. - `--ignore-missing` now works with `--standard-json`, though it's not very useful because there can be at most one input file. - Separate errors for situations where there are no input files on the command line (this can be detected in the parser) and where they are not present on disk.
This commit is contained in:
parent
c938f35b99
commit
9a7c364c71
@ -15,6 +15,8 @@ Bugfixes:
|
||||
* Code Generator: Fix crash when passing an empty string literal to ``bytes.concat()``.
|
||||
* Code Generator: Fix internal compiler error when calling functions bound to calldata structs and arrays.
|
||||
* Code Generator: Fix internal compiler error when passing a 32-byte hex literal or a zero literal to ``bytes.concat()`` by disallowing such literals.
|
||||
* Commandline Interface: Fix crash when a directory path is passed to ``--standard-json``.
|
||||
* Commandline Interface: Read JSON from standard input when ``--standard-json`` gets ``-`` as a file name.
|
||||
* Standard JSON: Include source location for errors in files with empty name.
|
||||
* Type Checker: Fix internal error and prevent static calls to unimplemented modifiers.
|
||||
* Yul Code Generator: Fix internal compiler error when using a long literal with bitwise negation.
|
||||
|
@ -377,8 +377,18 @@ void CommandLineInterface::handleGasEstimation(string const& _contract)
|
||||
}
|
||||
}
|
||||
|
||||
bool CommandLineInterface::readInputFilesAndConfigureFileReader()
|
||||
bool CommandLineInterface::readInputFiles()
|
||||
{
|
||||
solAssert(!m_standardJsonInput.has_value(), "");
|
||||
|
||||
m_fileReader.setBasePath(m_options.input.basePath);
|
||||
|
||||
if (m_fileReader.basePath() != "" && !boost::filesystem::is_directory(m_fileReader.basePath()))
|
||||
{
|
||||
serr() << "Base path must be a directory: " << m_fileReader.basePath() << endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
for (boost::filesystem::path const& allowedDirectory: m_options.input.allowedDirectories)
|
||||
m_fileReader.allowDirectory(allowedDirectory);
|
||||
|
||||
@ -411,16 +421,33 @@ bool CommandLineInterface::readInputFilesAndConfigureFileReader()
|
||||
}
|
||||
|
||||
// NOTE: we ignore the FileNotFound exception as we manually check above
|
||||
m_fileReader.setSource(infile, readFileAsString(infile.string()));
|
||||
m_fileReader.allowDirectory(boost::filesystem::path(boost::filesystem::canonical(infile).string()).remove_filename());
|
||||
string fileContent = readFileAsString(infile.string());
|
||||
if (m_options.input.mode == InputMode::StandardJson)
|
||||
{
|
||||
solAssert(!m_standardJsonInput.has_value(), "");
|
||||
m_standardJsonInput = move(fileContent);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_fileReader.setSource(infile, move(fileContent));
|
||||
m_fileReader.allowDirectory(boost::filesystem::canonical(infile).remove_filename());
|
||||
}
|
||||
}
|
||||
|
||||
if (m_options.input.addStdin)
|
||||
m_fileReader.setSource(g_stdinFileName, readUntilEnd(m_sin));
|
||||
|
||||
if (m_fileReader.sourceCodes().size() == 0)
|
||||
{
|
||||
serr() << "No input files given. If you wish to use the standard input please specify \"-\" explicitly." << endl;
|
||||
if (m_options.input.mode == InputMode::StandardJson)
|
||||
{
|
||||
solAssert(!m_standardJsonInput.has_value(), "");
|
||||
m_standardJsonInput = readUntilEnd(m_sin);
|
||||
}
|
||||
else
|
||||
m_fileReader.setSource(g_stdinFileName, readUntilEnd(m_sin));
|
||||
}
|
||||
|
||||
if (m_fileReader.sourceCodes().empty() && !m_standardJsonInput.has_value())
|
||||
{
|
||||
serr() << "All specified input files either do not exist or are not regular files." << endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -502,57 +529,35 @@ bool CommandLineInterface::parseArguments(int _argc, char const* const* _argv)
|
||||
|
||||
bool CommandLineInterface::processInput()
|
||||
{
|
||||
m_fileReader.setBasePath(m_options.input.basePath);
|
||||
|
||||
if (m_options.input.basePath != "" && !boost::filesystem::is_directory(m_options.input.basePath))
|
||||
switch (m_options.input.mode)
|
||||
{
|
||||
serr() << "Base path must be a directory: \"" << m_options.input.basePath << "\"\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (m_options.input.mode == InputMode::StandardJson)
|
||||
case InputMode::StandardJson:
|
||||
{
|
||||
string input;
|
||||
if (m_options.input.standardJsonFile.empty())
|
||||
input = readUntilEnd(m_sin);
|
||||
else
|
||||
{
|
||||
try
|
||||
{
|
||||
input = readFileAsString(m_options.input.standardJsonFile);
|
||||
}
|
||||
catch (FileNotFound const&)
|
||||
{
|
||||
serr() << "File not found: " << m_options.input.standardJsonFile << endl;
|
||||
return false;
|
||||
}
|
||||
catch (NotAFile const&)
|
||||
{
|
||||
serr() << "Not a regular file: " << m_options.input.standardJsonFile << endl;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
solAssert(m_standardJsonInput.has_value(), "");
|
||||
|
||||
StandardCompiler compiler(m_fileReader.reader(), m_options.formatting.json);
|
||||
sout() << compiler.compile(std::move(input)) << endl;
|
||||
sout() << compiler.compile(move(m_standardJsonInput.value())) << endl;
|
||||
m_standardJsonInput.reset();
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!readInputFilesAndConfigureFileReader())
|
||||
return false;
|
||||
|
||||
if (m_options.input.mode == InputMode::Assembler)
|
||||
case InputMode::Assembler:
|
||||
{
|
||||
return assemble(
|
||||
m_options.assembly.inputLanguage,
|
||||
m_options.assembly.targetMachine,
|
||||
m_options.optimizer.enabled,
|
||||
m_options.optimizer.yulSteps
|
||||
);
|
||||
|
||||
if (m_options.input.mode == InputMode::Linker)
|
||||
}
|
||||
case InputMode::Linker:
|
||||
return link();
|
||||
case InputMode::Compiler:
|
||||
case InputMode::CompilerWithASTImport:
|
||||
return compile();
|
||||
}
|
||||
|
||||
solAssert(m_options.input.mode == InputMode::Compiler || m_options.input.mode == InputMode::CompilerWithASTImport, "");
|
||||
return compile();
|
||||
solAssert(false, "");
|
||||
return false;
|
||||
}
|
||||
|
||||
bool CommandLineInterface::compile()
|
||||
|
@ -53,6 +53,8 @@ public:
|
||||
|
||||
/// Parse command line arguments and return false if we should not continue
|
||||
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
|
||||
bool processInput();
|
||||
/// Perform actions on the input depending on provided compiler arguments
|
||||
@ -61,6 +63,7 @@ public:
|
||||
|
||||
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:
|
||||
bool compile();
|
||||
@ -95,10 +98,6 @@ private:
|
||||
void handleGasEstimation(std::string const& _contract);
|
||||
void handleStorageLayout(std::string const& _contract);
|
||||
|
||||
/// Reads the content of input files specified on the command line and passes them to FileReader.
|
||||
/// @return false if there are no input files or input files cannot be read.
|
||||
bool readInputFilesAndConfigureFileReader();
|
||||
|
||||
/// Tries to read @ m_sourceCodes as a JSONs holding ASTs
|
||||
/// such that they can be imported into the compiler (importASTs())
|
||||
/// (produced by --combined-json ast,compact-format <file.sol>
|
||||
@ -129,6 +128,7 @@ private:
|
||||
bool m_hasOutput = false;
|
||||
bool m_error = false; ///< If true, some error occurred.
|
||||
FileReader m_fileReader;
|
||||
std::optional<std::string> m_standardJsonInput;
|
||||
std::unique_ptr<frontend::CompilerStack> m_compiler;
|
||||
CommandLineOptions m_options;
|
||||
};
|
||||
|
@ -265,7 +265,6 @@ bool CommandLineOptions::operator==(CommandLineOptions const& _other) const noex
|
||||
{
|
||||
return
|
||||
input.paths == _other.input.paths &&
|
||||
input.standardJsonFile == _other.input.standardJsonFile &&
|
||||
input.remappings == _other.input.remappings &&
|
||||
input.addStdin == _other.input.addStdin &&
|
||||
input.basePath == _other.input.basePath &&
|
||||
@ -301,12 +300,20 @@ bool CommandLineOptions::operator==(CommandLineOptions const& _other) const noex
|
||||
bool CommandLineParser::parseInputPathsAndRemappings()
|
||||
{
|
||||
m_options.input.ignoreMissingFiles = (m_args.count(g_strIgnoreMissingFiles) > 0);
|
||||
|
||||
if (m_args.count(g_strInputFile))
|
||||
for (string path: m_args[g_strInputFile].as<vector<string>>())
|
||||
{
|
||||
auto eq = find(path.begin(), path.end(), '=');
|
||||
if (eq != path.end())
|
||||
{
|
||||
if (m_options.input.mode == InputMode::StandardJson)
|
||||
{
|
||||
serr() << "Import remappings are not accepted on the command line in Standard JSON mode." << endl;
|
||||
serr() << "Please put them under 'settings.remappings' in the JSON input." << endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (auto r = ImportRemapper::parseRemapping(path))
|
||||
m_options.input.remappings.emplace_back(std::move(*r));
|
||||
else
|
||||
@ -324,6 +331,25 @@ bool CommandLineParser::parseInputPathsAndRemappings()
|
||||
m_options.input.paths.insert(path);
|
||||
}
|
||||
|
||||
if (m_options.input.mode == InputMode::StandardJson)
|
||||
{
|
||||
if (m_options.input.paths.size() > 1 || (m_options.input.paths.size() == 1 && m_options.input.addStdin))
|
||||
{
|
||||
serr() << "Too many input files for --" << g_strStandardJSON << "." << endl;
|
||||
serr() << "Please either specify a single file name or provide its content on standard input." << endl;
|
||||
return false;
|
||||
}
|
||||
else if (m_options.input.paths.size() == 0)
|
||||
// Standard JSON mode input used to be handled separately and zero files meant "read from stdin".
|
||||
// Keep it working that way for backwards-compatibility.
|
||||
m_options.input.addStdin = true;
|
||||
}
|
||||
else if (m_options.input.paths.size() == 0 && !m_options.input.addStdin)
|
||||
{
|
||||
serr() << "No input files given. If you wish to use the standard input please specify \"-\" explicitly." << endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -882,25 +908,12 @@ General Information)").c_str(),
|
||||
else
|
||||
m_options.input.mode = InputMode::Compiler;
|
||||
|
||||
if (m_options.input.mode == InputMode::StandardJson)
|
||||
{
|
||||
vector<string> inputFiles;
|
||||
if (m_args.count(g_strInputFile))
|
||||
inputFiles = m_args[g_strInputFile].as<vector<string>>();
|
||||
if (inputFiles.size() == 1)
|
||||
m_options.input.standardJsonFile = inputFiles[0];
|
||||
else if (inputFiles.size() > 1)
|
||||
{
|
||||
serr() << "If --" << g_strStandardJSON << " is used, only zero or one input files are supported." << endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!parseInputPathsAndRemappings())
|
||||
return false;
|
||||
|
||||
if (m_options.input.mode == InputMode::StandardJson)
|
||||
return true;
|
||||
|
||||
if (m_args.count(g_strLibraries))
|
||||
for (string const& library: m_args[g_strLibraries].as<vector<string>>())
|
||||
if (!parseLibraryOption(library))
|
||||
|
@ -107,7 +107,6 @@ struct CommandLineOptions
|
||||
{
|
||||
InputMode mode = InputMode::Compiler;
|
||||
std::set<boost::filesystem::path> paths;
|
||||
std::string standardJsonFile;
|
||||
std::vector<ImportRemapper::Remapping> remappings;
|
||||
bool addStdin = false;
|
||||
boost::filesystem::path basePath = "";
|
||||
|
@ -57,6 +57,8 @@ int main(int argc, char** argv)
|
||||
solidity::frontend::CommandLineInterface cli(cin, cout, cerr);
|
||||
if (!cli.parseArguments(argc, argv))
|
||||
return 1;
|
||||
if (!cli.readInputFiles())
|
||||
return 1;
|
||||
if (!cli.processInput())
|
||||
return 1;
|
||||
bool success = false;
|
||||
|
@ -152,6 +152,9 @@ set(libyul_sources
|
||||
detect_stray_source_files("${libyul_sources}" "libyul/")
|
||||
|
||||
set(solcli_sources
|
||||
solc/Common.cpp
|
||||
solc/Common.h
|
||||
solc/CommandLineInterface.cpp
|
||||
solc/CommandLineParser.cpp
|
||||
)
|
||||
detect_stray_source_files("${solcli_sources}" "solc/")
|
||||
|
@ -24,6 +24,21 @@ using namespace std;
|
||||
using namespace solidity;
|
||||
using namespace solidity::test;
|
||||
|
||||
void solidity::test::createFilesWithParentDirs(set<boost::filesystem::path> const& _paths, string const& _content)
|
||||
{
|
||||
for (boost::filesystem::path const& path: _paths)
|
||||
{
|
||||
if (!path.parent_path().empty())
|
||||
boost::filesystem::create_directories(path.parent_path());
|
||||
|
||||
ofstream newFile(path.string());
|
||||
newFile << _content;
|
||||
|
||||
if (newFile.fail() || !boost::filesystem::exists(path))
|
||||
BOOST_THROW_EXCEPTION(runtime_error("Failed to create an empty file: \"" + path.string() + "\"."));
|
||||
}
|
||||
}
|
||||
|
||||
void solidity::test::createFileWithContent(boost::filesystem::path const& _path, string const& content)
|
||||
{
|
||||
if (boost::filesystem::is_regular_file(_path))
|
||||
|
@ -23,11 +23,16 @@
|
||||
|
||||
#include <boost/filesystem.hpp>
|
||||
|
||||
#include <set>
|
||||
#include <string>
|
||||
|
||||
namespace solidity::test
|
||||
{
|
||||
|
||||
/// Creates all the specified files and fills them with the specifiedcontent. Creates their parent
|
||||
/// directories if they do not exist. Throws an exception if any part of the operation does not succeed.
|
||||
void createFilesWithParentDirs(std::set<boost::filesystem::path> const& _paths, std::string const& _content = "");
|
||||
|
||||
/// Creates a file with the exact content specified in the second argument.
|
||||
/// Throws an exception if the file already exists or if the parent directory of the file does not.
|
||||
void createFileWithContent(boost::filesystem::path const& _path, std::string const& content);
|
||||
|
@ -1 +1 @@
|
||||
File not found: input.sol
|
||||
"input.sol" is not found.
|
||||
|
@ -215,7 +215,7 @@ ostream& operator<<(ostream& _out, map<string, set<string>> const& _map)
|
||||
namespace boost::test_tools::tt_detail
|
||||
{
|
||||
|
||||
// Boost won't find find the << operator unless we put it in the std namespace which is illegal.
|
||||
// Boost won't find the << operator unless we put it in the std namespace which is illegal.
|
||||
// The recommended solution is to overload print_log_value<> struct and make it use our operator.
|
||||
|
||||
template<>
|
||||
|
346
test/solc/CommandLineInterface.cpp
Normal file
346
test/solc/CommandLineInterface.cpp
Normal file
@ -0,0 +1,346 @@
|
||||
/*
|
||||
This file is part of solidity.
|
||||
|
||||
solidity is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
solidity is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with solidity. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
|
||||
/// Unit tests for solc/CommandLineInterface.h
|
||||
|
||||
#include <solc/CommandLineInterface.h>
|
||||
|
||||
#include <test/solc/Common.h>
|
||||
|
||||
#include <test/Common.h>
|
||||
#include <test/libsolidity/util/SoltestErrors.h>
|
||||
#include <test/FilesystemUtils.h>
|
||||
#include <test/TemporaryDirectory.h>
|
||||
|
||||
#include <range/v3/view/transform.hpp>
|
||||
|
||||
#include <map>
|
||||
#include <ostream>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
using namespace std;
|
||||
using namespace solidity::frontend;
|
||||
using namespace solidity::test;
|
||||
|
||||
using PathSet = set<boost::filesystem::path>;
|
||||
|
||||
#define TEST_CASE_NAME (boost::unit_test::framework::current_test_case().p_name)
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
ostream& operator<<(ostream& _out, vector<ImportRemapper::Remapping> const& _remappings)
|
||||
{
|
||||
static auto remappingToString = [](auto const& _remapping)
|
||||
{
|
||||
return _remapping.context + ":" + _remapping.prefix + "=" + _remapping.target;
|
||||
};
|
||||
|
||||
_out << "[" << joinHumanReadable(_remappings | ranges::views::transform(remappingToString)) << "]";
|
||||
return _out;
|
||||
}
|
||||
|
||||
ostream& operator<<(ostream& _out, map<string, string> const& _map)
|
||||
{
|
||||
_out << "{" << endl;
|
||||
for (auto const& [key, value]: _map)
|
||||
_out << "" << key << ": " << value << "," << endl;
|
||||
_out << "}";
|
||||
|
||||
return _out;
|
||||
}
|
||||
|
||||
ostream& operator<<(ostream& _out, PathSet const& _paths)
|
||||
{
|
||||
static auto pathString = [](auto const& _path) { return _path.string(); };
|
||||
|
||||
_out << "{" << joinHumanReadable(_paths | ranges::views::transform(pathString)) << "}";
|
||||
return _out;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace boost::test_tools::tt_detail
|
||||
{
|
||||
|
||||
// Boost won't find the << operator unless we put it in the std namespace which is illegal.
|
||||
// The recommended solution is to overload print_log_value<> struct and make it use our operator.
|
||||
|
||||
template<>
|
||||
struct print_log_value<vector<ImportRemapper::Remapping>>
|
||||
{
|
||||
void operator()(std::ostream& _out, vector<ImportRemapper::Remapping> const& _value) { ::operator<<(_out, _value); }
|
||||
};
|
||||
|
||||
template<>
|
||||
struct print_log_value<map<string, string>>
|
||||
{
|
||||
void operator()(std::ostream& _out, map<string, string> const& _value) { ::operator<<(_out, _value); }
|
||||
};
|
||||
|
||||
template<>
|
||||
struct print_log_value<PathSet>
|
||||
{
|
||||
void operator()(std::ostream& _out, PathSet const& _value) { ::operator<<(_out, _value); }
|
||||
};
|
||||
|
||||
} // namespace boost::test_tools::tt_detail
|
||||
|
||||
namespace solidity::frontend::test
|
||||
{
|
||||
|
||||
BOOST_AUTO_TEST_SUITE(CommandLineInterfaceTest)
|
||||
|
||||
BOOST_AUTO_TEST_CASE(multiple_input_modes)
|
||||
{
|
||||
array<string, 6> inputModeOptions = {
|
||||
"--standard-json",
|
||||
"--link",
|
||||
"--assemble",
|
||||
"--strict-assembly",
|
||||
"--yul",
|
||||
"--import-ast",
|
||||
};
|
||||
string expectedMessage =
|
||||
"The following options are mutually exclusive: "
|
||||
"--standard-json, --link, --assemble, --strict-assembly, --yul, --import-ast. "
|
||||
"Select at most one.\n";
|
||||
|
||||
for (string const& mode1: inputModeOptions)
|
||||
for (string const& mode2: inputModeOptions)
|
||||
if (mode1 != mode2)
|
||||
{
|
||||
vector<string> commandLine = {"solc", mode1, mode2};
|
||||
OptionsReaderAndMessages result = parseCommandLineAndReadInputFiles(commandLine);
|
||||
BOOST_TEST(!result.success);
|
||||
BOOST_TEST(result.stderrContent == expectedMessage);
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(cli_input)
|
||||
{
|
||||
TemporaryDirectory tempDir1(TEST_CASE_NAME);
|
||||
TemporaryDirectory tempDir2(TEST_CASE_NAME);
|
||||
createFilesWithParentDirs({tempDir1.path() / "input1.sol"});
|
||||
createFilesWithParentDirs({tempDir2.path() / "input2.sol"});
|
||||
|
||||
vector<ImportRemapper::Remapping> expectedRemappings = {
|
||||
{"", "a", "b/c/d"},
|
||||
{"a", "b", "c/d/e/"},
|
||||
};
|
||||
map<string, string> expectedSources = {
|
||||
{"<stdin>", "\n"},
|
||||
{(tempDir1.path() / "input1.sol").generic_string(), ""},
|
||||
{(tempDir2.path() / "input2.sol").generic_string(), ""},
|
||||
};
|
||||
PathSet expectedAllowedPaths = {
|
||||
boost::filesystem::canonical(tempDir1.path()),
|
||||
boost::filesystem::canonical(tempDir2.path()),
|
||||
"b/c",
|
||||
"c/d/e",
|
||||
};
|
||||
|
||||
OptionsReaderAndMessages result = parseCommandLineAndReadInputFiles({
|
||||
"solc",
|
||||
"a=b/c/d",
|
||||
(tempDir1.path() / "input1.sol").string(),
|
||||
(tempDir2.path() / "input2.sol").string(),
|
||||
"a:b=c/d/e/",
|
||||
"-",
|
||||
});
|
||||
|
||||
BOOST_TEST(result.success);
|
||||
BOOST_TEST(result.stderrContent == "");
|
||||
BOOST_TEST(result.options.input.mode == InputMode::Compiler);
|
||||
BOOST_TEST(result.options.input.addStdin);
|
||||
BOOST_CHECK_EQUAL(result.options.input.remappings, expectedRemappings);
|
||||
BOOST_CHECK_EQUAL(result.reader.sourceCodes(), expectedSources);
|
||||
BOOST_CHECK_EQUAL(result.reader.allowedDirectories(), expectedAllowedPaths);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(cli_ignore_missing_some_files_exist)
|
||||
{
|
||||
TemporaryDirectory tempDir1(TEST_CASE_NAME);
|
||||
TemporaryDirectory tempDir2(TEST_CASE_NAME);
|
||||
createFilesWithParentDirs({tempDir1.path() / "input1.sol"});
|
||||
|
||||
// NOTE: Allowed paths should not be added for skipped files.
|
||||
map<string, string> expectedSources = {{(tempDir1.path() / "input1.sol").generic_string(), ""}};
|
||||
PathSet expectedAllowedPaths = {boost::filesystem::canonical(tempDir1.path())};
|
||||
|
||||
OptionsReaderAndMessages result = parseCommandLineAndReadInputFiles({
|
||||
"solc",
|
||||
(tempDir1.path() / "input1.sol").string(),
|
||||
(tempDir2.path() / "input2.sol").string(),
|
||||
"--ignore-missing",
|
||||
});
|
||||
BOOST_TEST(result.success);
|
||||
BOOST_TEST(result.stderrContent == "\"" + (tempDir2.path() / "input2.sol").string() + "\" is not found. Skipping.\n");
|
||||
BOOST_TEST(result.options.input.mode == InputMode::Compiler);
|
||||
BOOST_TEST(!result.options.input.addStdin);
|
||||
BOOST_CHECK_EQUAL(result.reader.sourceCodes(), expectedSources);
|
||||
BOOST_CHECK_EQUAL(result.reader.allowedDirectories(), expectedAllowedPaths);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(cli_ignore_missing_no_files_exist)
|
||||
{
|
||||
TemporaryDirectory tempDir(TEST_CASE_NAME);
|
||||
|
||||
string expectedMessage =
|
||||
"\"" + (tempDir.path() / "input1.sol").string() + "\" is not found. Skipping.\n"
|
||||
"\"" + (tempDir.path() / "input2.sol").string() + "\" is not found. Skipping.\n"
|
||||
"All specified input files either do not exist or are not regular files.\n";
|
||||
|
||||
OptionsReaderAndMessages result = parseCommandLineAndReadInputFiles({
|
||||
"solc",
|
||||
(tempDir.path() / "input1.sol").string(),
|
||||
(tempDir.path() / "input2.sol").string(),
|
||||
"--ignore-missing",
|
||||
});
|
||||
BOOST_TEST(!result.success);
|
||||
BOOST_TEST(result.stderrContent == expectedMessage);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(cli_not_a_file)
|
||||
{
|
||||
TemporaryDirectory tempDir(TEST_CASE_NAME);
|
||||
|
||||
string expectedMessage = "\"" + tempDir.path().string() + "\" is not a valid file.\n";
|
||||
|
||||
OptionsReaderAndMessages result = parseCommandLineAndReadInputFiles({"solc", tempDir.path().string()});
|
||||
BOOST_TEST(!result.success);
|
||||
BOOST_TEST(result.stderrContent == expectedMessage);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(standard_json_base_path)
|
||||
{
|
||||
TemporaryDirectory tempDir(TEST_CASE_NAME);
|
||||
|
||||
OptionsReaderAndMessages result = parseCommandLineAndReadInputFiles({
|
||||
"solc",
|
||||
"--standard-json",
|
||||
"--base-path=" + tempDir.path().string(),
|
||||
});
|
||||
BOOST_TEST(result.success);
|
||||
BOOST_TEST(result.stderrContent == "");
|
||||
BOOST_TEST(result.options.input.mode == InputMode::StandardJson);
|
||||
BOOST_TEST(result.options.input.addStdin);
|
||||
BOOST_TEST(result.options.input.paths.empty());
|
||||
BOOST_TEST(result.reader.sourceCodes().empty());
|
||||
BOOST_TEST(result.reader.allowedDirectories().empty());
|
||||
BOOST_TEST(result.reader.basePath() == tempDir.path());
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(standard_json_no_input_file)
|
||||
{
|
||||
OptionsReaderAndMessages result = parseCommandLineAndReadInputFiles({"solc", "--standard-json"});
|
||||
BOOST_TEST(result.success);
|
||||
BOOST_TEST(result.stderrContent == "");
|
||||
BOOST_TEST(result.options.input.mode == InputMode::StandardJson);
|
||||
BOOST_TEST(result.options.input.addStdin);
|
||||
BOOST_TEST(result.options.input.paths.empty());
|
||||
BOOST_TEST(result.reader.sourceCodes().empty());
|
||||
BOOST_TEST(result.reader.allowedDirectories().empty());
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(standard_json_dash)
|
||||
{
|
||||
OptionsReaderAndMessages result = parseCommandLineAndReadInputFiles({"solc", "--standard-json", "-"});
|
||||
BOOST_TEST(result.success);
|
||||
BOOST_TEST(result.stderrContent == "");
|
||||
BOOST_TEST(result.options.input.mode == InputMode::StandardJson);
|
||||
BOOST_TEST(result.options.input.addStdin);
|
||||
BOOST_TEST(result.reader.sourceCodes().empty());
|
||||
BOOST_TEST(result.reader.allowedDirectories().empty());
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(standard_json_one_input_file)
|
||||
{
|
||||
TemporaryDirectory tempDir(TEST_CASE_NAME);
|
||||
createFilesWithParentDirs({tempDir.path() / "input.json"});
|
||||
|
||||
vector<string> commandLine = {"solc", "--standard-json", (tempDir.path() / "input.json").string()};
|
||||
OptionsReaderAndMessages result = parseCommandLineAndReadInputFiles(commandLine);
|
||||
BOOST_TEST(result.success);
|
||||
BOOST_TEST(result.stderrContent == "");
|
||||
BOOST_TEST(result.options.input.mode == InputMode::StandardJson);
|
||||
BOOST_TEST(!result.options.input.addStdin);
|
||||
BOOST_TEST(result.options.input.paths == PathSet{tempDir.path() / "input.json"});
|
||||
BOOST_TEST(result.reader.allowedDirectories().empty());
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(standard_json_two_input_files)
|
||||
{
|
||||
string expectedMessage =
|
||||
"Too many input files for --standard-json.\n"
|
||||
"Please either specify a single file name or provide its content on standard input.\n";
|
||||
|
||||
vector<string> commandLine = {"solc", "--standard-json", "input1.json", "input2.json"};
|
||||
OptionsReaderAndMessages result = parseCommandLineAndReadInputFiles(commandLine);
|
||||
BOOST_TEST(!result.success);
|
||||
BOOST_TEST(result.stderrContent == expectedMessage);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(standard_json_one_input_file_and_stdin)
|
||||
{
|
||||
string expectedMessage =
|
||||
"Too many input files for --standard-json.\n"
|
||||
"Please either specify a single file name or provide its content on standard input.\n";
|
||||
|
||||
vector<string> commandLine = {"solc", "--standard-json", "input1.json", "-"};
|
||||
OptionsReaderAndMessages result = parseCommandLineAndReadInputFiles(commandLine);
|
||||
BOOST_TEST(!result.success);
|
||||
BOOST_TEST(result.stderrContent == expectedMessage);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(standard_json_ignore_missing)
|
||||
{
|
||||
TemporaryDirectory tempDir(TEST_CASE_NAME);
|
||||
|
||||
// This option is pretty much useless Standard JSON mode.
|
||||
string expectedMessage =
|
||||
"\"" + (tempDir.path() / "input.json").string() + "\" is not found. Skipping.\n"
|
||||
"All specified input files either do not exist or are not regular files.\n";
|
||||
|
||||
OptionsReaderAndMessages result = parseCommandLineAndReadInputFiles({
|
||||
"solc",
|
||||
"--standard-json",
|
||||
(tempDir.path() / "input.json").string(),
|
||||
"--ignore-missing",
|
||||
});
|
||||
BOOST_TEST(!result.success);
|
||||
BOOST_TEST(result.stderrContent == expectedMessage);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(standard_json_remapping)
|
||||
{
|
||||
string expectedMessage =
|
||||
"Import remappings are not accepted on the command line in Standard JSON mode.\n"
|
||||
"Please put them under 'settings.remappings' in the JSON input.\n";
|
||||
|
||||
vector<string> commandLine = {"solc", "--standard-json", "a=b"};
|
||||
OptionsReaderAndMessages result = parseCommandLineAndReadInputFiles(commandLine);
|
||||
BOOST_TEST(!result.success);
|
||||
BOOST_TEST(result.stderrContent == expectedMessage);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END()
|
||||
|
||||
} // namespace solidity::frontend::test
|
@ -20,6 +20,8 @@
|
||||
|
||||
#include <solc/CommandLineParser.h>
|
||||
|
||||
#include <test/solc/Common.h>
|
||||
|
||||
#include <test/Common.h>
|
||||
#include <test/libsolidity/util/SoltestErrors.h>
|
||||
|
||||
@ -28,7 +30,6 @@
|
||||
#include <libsolidity/interface/Version.h>
|
||||
|
||||
#include <boost/algorithm/string.hpp>
|
||||
#include <boost/test/unit_test.hpp>
|
||||
|
||||
#include <map>
|
||||
#include <optional>
|
||||
@ -46,20 +47,13 @@ using namespace solidity::yul;
|
||||
namespace
|
||||
{
|
||||
|
||||
optional<CommandLineOptions> parseCommandLine(vector<string> const& commandLine, ostream& _stdout, ostream& _stderr)
|
||||
optional<CommandLineOptions> parseCommandLine(vector<string> const& _commandLine, ostream& _stdout, ostream& _stderr)
|
||||
{
|
||||
size_t argc = commandLine.size();
|
||||
vector<char const*> argv(argc + 1);
|
||||
|
||||
// argv[argc] typically contains NULL
|
||||
argv[argc] = nullptr;
|
||||
|
||||
for (size_t i = 0; i < argc; ++i)
|
||||
argv[i] = commandLine[i].c_str();
|
||||
vector<char const*> argv = test::makeArgv(_commandLine);
|
||||
|
||||
CommandLineParser cliParser(_stdout, _stderr);
|
||||
bool success = cliParser.parse(
|
||||
static_cast<int>(argc),
|
||||
static_cast<int>(_commandLine.size()),
|
||||
argv.data(),
|
||||
false // interactiveTerminal
|
||||
);
|
||||
@ -70,7 +64,6 @@ optional<CommandLineOptions> parseCommandLine(vector<string> const& commandLine,
|
||||
return cliParser.options();
|
||||
}
|
||||
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace solidity::frontend::test
|
||||
@ -99,7 +92,7 @@ BOOST_AUTO_TEST_CASE(no_options)
|
||||
BOOST_TEST(sout.str() == "");
|
||||
BOOST_TEST(serr.str() == "");
|
||||
BOOST_REQUIRE(parsedOptions.has_value());
|
||||
BOOST_TEST((parsedOptions.value() == expectedOptions));
|
||||
BOOST_TEST(parsedOptions.value() == expectedOptions);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(help)
|
||||
@ -224,7 +217,7 @@ BOOST_AUTO_TEST_CASE(cli_mode_options)
|
||||
BOOST_TEST(sout.str() == "");
|
||||
BOOST_TEST(serr.str() == "");
|
||||
BOOST_REQUIRE(parsedOptions.has_value());
|
||||
BOOST_TEST((parsedOptions.value() == expectedOptions));
|
||||
BOOST_TEST(parsedOptions.value() == expectedOptions);
|
||||
}
|
||||
}
|
||||
|
||||
@ -342,8 +335,7 @@ BOOST_AUTO_TEST_CASE(assembly_mode_options)
|
||||
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_TEST((parsedOptions.value() == expectedOptions));
|
||||
BOOST_TEST(parsedOptions.value() == expectedOptions);
|
||||
}
|
||||
}
|
||||
|
||||
@ -355,7 +347,7 @@ BOOST_AUTO_TEST_CASE(standard_json_mode_options)
|
||||
"--standard-json",
|
||||
"--base-path=/home/user/",
|
||||
"--allow-paths=/tmp,/home,project,../contracts",
|
||||
"--ignore-missing", // Ignored in Standard JSON mode
|
||||
"--ignore-missing",
|
||||
"--error-recovery", // Ignored in Standard JSON mode
|
||||
"--output-dir=/tmp/out", // Accepted but has no effect in Standard JSON mode
|
||||
"--overwrite", // Accepted but has no effect in Standard JSON mode
|
||||
@ -393,10 +385,10 @@ BOOST_AUTO_TEST_CASE(standard_json_mode_options)
|
||||
CommandLineOptions expectedOptions;
|
||||
|
||||
expectedOptions.input.mode = InputMode::StandardJson;
|
||||
expectedOptions.input.paths = {};
|
||||
expectedOptions.input.standardJsonFile = "input.json";
|
||||
expectedOptions.input.paths = {"input.json"};
|
||||
expectedOptions.input.basePath = "/home/user/";
|
||||
expectedOptions.input.allowedDirectories = {"/tmp", "/home", "project", "../contracts"};
|
||||
expectedOptions.input.ignoreMissingFiles = true;
|
||||
expectedOptions.output.dir = "/tmp/out";
|
||||
expectedOptions.output.overwriteFiles = true;
|
||||
expectedOptions.output.revertStrings = RevertStrings::Strip;
|
||||
@ -419,7 +411,7 @@ BOOST_AUTO_TEST_CASE(standard_json_mode_options)
|
||||
BOOST_TEST(sout.str() == "");
|
||||
BOOST_TEST(serr.str() == "");
|
||||
BOOST_REQUIRE(parsedOptions.has_value());
|
||||
BOOST_TEST((parsedOptions.value() == expectedOptions));
|
||||
BOOST_TEST(parsedOptions.value() == expectedOptions);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END()
|
||||
|
58
test/solc/Common.cpp
Normal file
58
test/solc/Common.cpp
Normal file
@ -0,0 +1,58 @@
|
||||
/*
|
||||
This file is part of solidity.
|
||||
|
||||
solidity is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
solidity is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with solidity. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
|
||||
#include <test/solc/Common.h>
|
||||
|
||||
#include <solc/CommandLineInterface.h>
|
||||
|
||||
#include <sstream>
|
||||
|
||||
using namespace std;
|
||||
using namespace solidity::frontend;
|
||||
|
||||
vector<char const*> test::makeArgv(vector<string> const& _commandLine)
|
||||
{
|
||||
size_t argc = _commandLine.size();
|
||||
vector<char const*> argv(_commandLine.size() + 1);
|
||||
|
||||
// C++ standard mandates argv[argc] to be NULL
|
||||
argv[argc] = nullptr;
|
||||
|
||||
for (size_t i = 0; i < argc; ++i)
|
||||
argv[i] = _commandLine[i].c_str();
|
||||
|
||||
return argv;
|
||||
}
|
||||
|
||||
test::OptionsReaderAndMessages test::parseCommandLineAndReadInputFiles(
|
||||
vector<string> const& _commandLine,
|
||||
string const& _standardInputContent,
|
||||
bool _processInput
|
||||
)
|
||||
{
|
||||
vector<char const*> argv = makeArgv(_commandLine);
|
||||
stringstream sin(_standardInputContent), sout, serr;
|
||||
CommandLineInterface cli(sin, sout, serr);
|
||||
bool success = cli.parseArguments(static_cast<int>(_commandLine.size()), argv.data());
|
||||
if (success)
|
||||
success = cli.readInputFiles();
|
||||
if (success && _processInput)
|
||||
success = cli.processInput();
|
||||
|
||||
return {success, cli.options(), cli.fileReader(), cli.standardJsonInput(), sout.str(), serr.str()};
|
||||
}
|
53
test/solc/Common.h
Normal file
53
test/solc/Common.h
Normal file
@ -0,0 +1,53 @@
|
||||
/*
|
||||
This file is part of solidity.
|
||||
|
||||
solidity is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
solidity is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with solidity. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
// SPDX-License-Identifier: GPL-3.0
|
||||
|
||||
/// Utilities shared by multiple tests for code in solc/.
|
||||
|
||||
#include <solc/CommandLineParser.h>
|
||||
|
||||
#include <boost/test/unit_test.hpp>
|
||||
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
BOOST_TEST_DONT_PRINT_LOG_VALUE(solidity::frontend::CommandLineOptions)
|
||||
BOOST_TEST_DONT_PRINT_LOG_VALUE(solidity::frontend::InputMode)
|
||||
|
||||
namespace solidity::frontend::test
|
||||
{
|
||||
|
||||
struct OptionsReaderAndMessages
|
||||
{
|
||||
bool success;
|
||||
CommandLineOptions options;
|
||||
FileReader reader;
|
||||
std::optional<std::string> standardJsonInput;
|
||||
std::string stdoutContent;
|
||||
std::string stderrContent;
|
||||
};
|
||||
|
||||
std::vector<char const*> makeArgv(std::vector<std::string> const& _commandLine);
|
||||
|
||||
OptionsReaderAndMessages parseCommandLineAndReadInputFiles(
|
||||
std::vector<std::string> const& _commandLine,
|
||||
std::string const& _standardInputContent = "",
|
||||
bool _processInput = false
|
||||
);
|
||||
|
||||
} // namespace solidity::frontend::test
|
@ -57,7 +57,7 @@ std::ostream& operator<<(std::ostream& _output, std::tuple<T1, T2> const& _tuple
|
||||
namespace boost::test_tools::tt_detail
|
||||
{
|
||||
|
||||
// Boost won't find find the << operator unless we put it in the std namespace which is illegal.
|
||||
// Boost won't find the << operator unless we put it in the std namespace which is illegal.
|
||||
// The recommended solution is to overload print_log_value<> struct and make it use our global operator.
|
||||
template<typename T1,typename T2>
|
||||
struct print_log_value<std::tuple<T1, T2>>
|
||||
|
Loading…
Reference in New Issue
Block a user