Merge pull request #370 from chriseth/redirects

Path remappings for solc.
This commit is contained in:
chriseth 2016-01-28 17:05:12 +01:00
commit bdbb7d8a40
3 changed files with 87 additions and 33 deletions

View File

@ -103,7 +103,16 @@ One of the build targets of the Solidity repository is `solc`, the solidity comm
Using `solc --help` provides you with an explanation of all options. The compiler can produce various outputs, ranging from simple binaries and assembly over an abstract syntax tree (parse tree) to estimations of gas usage. Using `solc --help` provides you with an explanation of all options. The compiler can produce various outputs, ranging from simple binaries and assembly over an abstract syntax tree (parse tree) to estimations of gas usage.
If you only want to compile a single file, you run it as `solc --bin sourceFile.sol` and it will print the binary. Before you deploy your contract, activate the optimizer while compiling using `solc --optimize --bin sourceFile.sol`. If you want to get some of the more advanced output variants of `solc`, it is probably better to tell it to output everything to separate files using `solc -o outputDirectory --bin --ast --asm sourceFile.sol`. If you only want to compile a single file, you run it as `solc --bin sourceFile.sol` and it will print the binary. Before you deploy your contract, activate the optimizer while compiling using `solc --optimize --bin sourceFile.sol`. If you want to get some of the more advanced output variants of `solc`, it is probably better to tell it to output everything to separate files using `solc -o outputDirectory --bin --ast --asm sourceFile.sol`.
Of course, you can also specify several source files and actually that is also required if you use the `import` statement in Solidity: The compiler will (for now) not automatically discover source files for you, so you have to provide it with all source files your project consists of. The commandline compiler will automatically read imported files from the filesystem, but
it is also possible to provide path redirects using `prefix=path` in the following way:
solc github.com/ethereum/dapp-bin/=/usr/local/lib/dapp-bin/ =/usr/local/lib/fallback file.sol
This essentially instructs the compiler to search for anything starting with
`github.com/ethereum/dapp-bin/` under `/usr/local/lib/dapp-bin` and if it does not
find the file there, it will look at `/usr/local/lib/fallback` (the empty prefix
always matches) and if also that fails, it will make a full path lookup
on the filesystem.
If your contracts use [libraries](#libraries), you will notice that the bytecode contains substrings of the form `__LibraryName______`. You can use `solc` as a linker meaning that it will insert the library addresses for you at those points: If your contracts use [libraries](#libraries), you will notice that the bytecode contains substrings of the form `__LibraryName______`. You can use `solc` as a linker meaning that it will insert the library addresses for you at those points:

View File

@ -297,6 +297,48 @@ void CommandLineInterface::handleFormal()
cout << "Formal version:" << endl << m_compiler->formalTranslation() << endl; cout << "Formal version:" << endl << m_compiler->formalTranslation() << endl;
} }
void CommandLineInterface::readInputFilesAndConfigureRemappings()
{
if (!m_args.count("input-file"))
{
string s;
while (!cin.eof())
{
getline(cin, s);
m_sourceCodes[g_stdinFileName].append(s + '\n');
}
}
else
for (string const& infile: m_args["input-file"].as<vector<string>>())
{
auto eq = find(infile.begin(), infile.end(), '=');
if (eq != infile.end())
m_remappings.push_back(make_pair(
string(infile.begin(), eq),
string(eq + 1, infile.end())
));
else
{
auto path = boost::filesystem::path(infile);
if (!boost::filesystem::exists(path))
{
cerr << "Skipping non existant input file \"" << infile << "\"" << endl;
continue;
}
if (!boost::filesystem::is_regular_file(path))
{
cerr << "\"" << infile << "\" is not a valid file. Skipping" << endl;
continue;
}
m_sourceCodes[infile] = dev::contentsString(infile);
}
}
// Add empty remapping to try the path itself.
m_remappings.push_back(make_pair(string(), string()));
}
bool CommandLineInterface::parseLibraryOption(string const& _input) bool CommandLineInterface::parseLibraryOption(string const& _input)
{ {
namespace fs = boost::filesystem; namespace fs = boost::filesystem;
@ -457,33 +499,7 @@ Allowed options)",
bool CommandLineInterface::processInput() bool CommandLineInterface::processInput()
{ {
if (!m_args.count("input-file")) readInputFilesAndConfigureRemappings();
{
string s;
while (!cin.eof())
{
getline(cin, s);
m_sourceCodes[g_stdinFileName].append(s + '\n');
}
}
else
for (string const& infile: m_args["input-file"].as<vector<string>>())
{
auto path = boost::filesystem::path(infile);
if (!boost::filesystem::exists(path))
{
cerr << "Skipping non existant input file \"" << infile << "\"" << endl;
continue;
}
if (!boost::filesystem::is_regular_file(path))
{
cerr << "\"" << infile << "\" is not a valid file. Skipping" << endl;
continue;
}
m_sourceCodes[infile] = dev::contentsString(infile);
}
if (m_args.count("libraries")) if (m_args.count("libraries"))
for (string const& library: m_args["libraries"].as<vector<string>>()) for (string const& library: m_args["libraries"].as<vector<string>>())
@ -499,13 +515,38 @@ bool CommandLineInterface::processInput()
function<pair<string,string>(string const&)> fileReader = [this](string const& _path) function<pair<string,string>(string const&)> fileReader = [this](string const& _path)
{ {
auto path = boost::filesystem::path(_path); // Try to find the longest prefix match in all remappings. At the end, there will be an
if (!boost::filesystem::exists(path)) // empty remapping so that we also try the path itself.
int errorLevel = 0;
size_t longestPrefix = 0;
string bestMatchPath;
for (auto const& redir: m_remappings)
{
auto const& virt = redir.first;
if (longestPrefix > 0 && virt.length() <= longestPrefix)
continue;
if (virt.length() > _path.length() || !std::equal(virt.begin(), virt.end(), _path.begin()))
continue;
string path = redir.second;
path.append(_path.begin() + virt.length(), _path.end());
auto boostPath = boost::filesystem::path(path);
if (!boost::filesystem::exists(boostPath))
errorLevel = max(errorLevel, 0);
else if (!boost::filesystem::is_regular_file(boostPath))
errorLevel = max(errorLevel, 1);
else
{
longestPrefix = virt.length();
bestMatchPath = path;
}
}
if (!bestMatchPath.empty())
return make_pair(m_sourceCodes[bestMatchPath] = dev::contentsString(bestMatchPath), string());
if (errorLevel == 0)
return make_pair(string(), string("File not found.")); return make_pair(string(), string("File not found."));
else if (!boost::filesystem::is_regular_file(path))
return make_pair(string(), string("Not a valid file."));
else else
return make_pair(m_sourceCodes[_path] = dev::contentsString(_path), string()); return make_pair(string(), string("Not a valid file."));
}; };
m_compiler.reset(new CompilerStack(m_args.count(g_argAddStandard) > 0, fileReader)); m_compiler.reset(new CompilerStack(m_args.count(g_argAddStandard) > 0, fileReader));

View File

@ -61,6 +61,8 @@ private:
void handleGasEstimation(std::string const& _contract); void handleGasEstimation(std::string const& _contract);
void handleFormal(); void handleFormal();
/// Fills @a m_sourceCodes initially and @a m_redirects.
void readInputFilesAndConfigureRemappings();
/// Tries to read from the file @a _input or interprets _input literally if that fails. /// Tries to read from the file @a _input or interprets _input literally if that fails.
/// It then tries to parse the contents and appends to m_libraries. /// It then tries to parse the contents and appends to m_libraries.
bool parseLibraryOption(std::string const& _input); bool parseLibraryOption(std::string const& _input);
@ -76,6 +78,8 @@ private:
boost::program_options::variables_map m_args; boost::program_options::variables_map m_args;
/// map of input files to source code strings /// map of input files to source code strings
std::map<std::string, std::string> m_sourceCodes; std::map<std::string, std::string> m_sourceCodes;
/// list of path prefix remappings, e.g. github.com/ethereum -> /usr/local/ethereum
std::vector<std::pair<std::string, std::string>> m_remappings;
/// map of library names to addresses /// map of library names to addresses
std::map<std::string, h160> m_libraries; std::map<std::string, h160> m_libraries;
/// Solidity compiler stack /// Solidity compiler stack