brownie
brownie copied to clipboard
Brownie compile can't find relative imports
Environment information
-
brownie
Version: v1.17.1 - Python development framework for Ethereum -
ganache-cli
Version: v6.12.2 (ganache-core: 2.13.2) -
solc
Version: 0.8.10+commit.fc410830.Linux.g++ - Python Version: 3.8.10
- OS:
Linux calypso 5.11.0-40-generic #44~20.04.2-Ubuntu SMP Tue Oct 26 18:07:44 UTC 2021 x86_64 x86_64 x86_64 GNU/Linux
Brownie stopped being able to compile solidity files that use relative imports to refer to files in the local directory tree. I can reproduce it with this simple test case. This was working for me earlier, and I don't know why it's not working any more.
I run brownie init
in a clean folder, create ContractA.sol and ContractB.sol in ./contracts, and run brownie compile
.
// SPDX-License-Identifier: MIT
pragma solidity 0.8.10;
contract ContractA {
}
// SPDX-License-Identifier: MIT
pragma solidity 0.8.10;
import "./ContractA.sol";
contract ContractB is ContractA {
}
$ brownie compile
Brownie v1.17.1 - Python development framework for Ethereum
Compiling contracts...
Solc version: 0.8.10
Optimizer: Enabled Runs: 200
EVM Version: Istanbul
CompilerError: solc returned the following errors:
ParserError: Source "/home/my_username/.brownie/packages/contracts/ContractA.sol" not found: File not found.
--> contracts/ContractB.sol:4:1:
|
4 | import "./ContractA.sol";
| ^^^^^^^^^^^^^^^^^^^^^^^^^
Why is it looking for the file in /home/my_username/.brownie/packages/contracts/ContractA.sol
instead of looking for it right there in the same directory?
I can get a stack trace if I call it from python:
from brownie import network, project
network.connect("development")
test_project = project.load(".")
Compiling contracts...
Solc version: 0.8.10
Optimizer: Enabled Runs: 200
EVM Version: Istanbul
---------------------------------------------------------------------------
SolcError Traceback (most recent call last)
~/pyenv/eth/lib/python3.8/site-packages/brownie/project/compiler/solidity.py in compile_from_input_json(input_json, silent, allow_paths)
77 try:
---> 78 return solcx.compile_standard(input_json, allow_paths=allow_paths)
79 except solcx.exceptions.SolcError as e:
~/pyenv/eth/lib/python3.8/site-packages/solcx/main.py in compile_standard(input_data, base_path, allow_paths, output_dir, overwrite, solc_binary, solc_version, allow_empty)
393 )
--> 394 raise SolcError(
395 error_message,
SolcError: ParserError: Source "/home/my_username/.brownie/packages/contracts/ContractA.sol" not found: File not found.
--> contracts/ContractB.sol:4:1:
|
4 | import "./ContractA.sol";
| ^^^^^^^^^^^^^^^^^^^^^^^^^
> command: `/home/my_username/.solcx/solc-v0.8.10 --standard-json --allow-paths /home/my_username/test_case,/home/my_username/.brownie/packages`
> return code: `0`
> stdout:
{"errors":[{"component":"general","errorCode":"6275","formattedMessage":"ParserError: Source \"/home/my_username/.brownie/packages/contracts/ContractA.sol\" not found: File not found.\n --> contracts/ContractB.sol:4:1:\n |\n4 | import \"./ContractA.sol\";\n | ^^^^^^^^^^^^^^^^^^^^^^^^^\n\n","message":"Source \"/home/my_username/.brownie/packages/contracts/ContractA.sol\" not found: File not found.","severity":"error","sourceLocation":{"end":82,"file":"contracts/ContractB.sol","start":57},"type":"ParserError"}],"sources":{}}
> stderr:
During handling of the above exception, another exception occurred:
CompilerError Traceback (most recent call last)
<ipython-input-4-086384e98c33> in <module>
----> 1 test_project = project.load(".")
~/pyenv/eth/lib/python3.8/site-packages/brownie/project/main.py in load(project_path, name)
749
750 # load sources and build
--> 751 return Project(name, project_path)
752
753
~/pyenv/eth/lib/python3.8/site-packages/brownie/project/main.py in __init__(self, name, project_path)
181 self._name = name
182 self._active = False
--> 183 self.load()
184
185 def load(self) -> None:
~/pyenv/eth/lib/python3.8/site-packages/brownie/project/main.py in load(self)
236 # compile updated sources, update build
237 changed = self._get_changed_contracts(interface_hashes)
--> 238 self._compile(changed, self._compiler_config, False)
239 self._compile_interfaces(interface_hashes)
240 self._load_dependency_artifacts()
~/pyenv/eth/lib/python3.8/site-packages/brownie/project/main.py in _compile(self, contract_sources, compiler_config, silent)
93
94 try:
---> 95 build_json = compiler.compile_and_format(
96 contract_sources,
97 solc_version=compiler_config["solc"].get("version", None),
~/pyenv/eth/lib/python3.8/site-packages/brownie/project/compiler/__init__.py in compile_and_format(contract_sources, solc_version, vyper_version, optimize, runs, evm_version, silent, allow_paths, interface_sources, remappings, optimizer)
139 )
140
--> 141 output_json = compile_from_input_json(input_json, silent, allow_paths)
142 build_json.update(generate_build_json(input_json, output_json, compiler_data, silent))
143
~/pyenv/eth/lib/python3.8/site-packages/brownie/project/compiler/__init__.py in compile_from_input_json(input_json, silent, allow_paths)
254 if input_json["language"] == "Solidity":
255 allow_paths = _get_allow_paths(allow_paths, input_json["settings"]["remappings"])
--> 256 return solidity.compile_from_input_json(input_json, silent, allow_paths)
257
258 raise UnsupportedLanguage(f"{input_json['language']}")
~/pyenv/eth/lib/python3.8/site-packages/brownie/project/compiler/solidity.py in compile_from_input_json(input_json, silent, allow_paths)
78 return solcx.compile_standard(input_json, allow_paths=allow_paths)
79 except solcx.exceptions.SolcError as e:
---> 80 raise CompilerError(e, "solc")
81
82
CompilerError: solc returned the following errors:
ParserError: Source "/home/my_username/.brownie/packages/contracts/ContractA.sol" not found: File not found.
--> contracts/ContractB.sol:4:1:
|
4 | import "./ContractA.sol";
| ^^^^^^^^^^^^^^^^^^^^^^^^^
So this is weird. I just ran brownie console
and it compiled just fine. Now that I exited out of that, running brownie compile
and brownie compile --all
work.
I don't understand it, but that seems like it might be a workaround if anyone else ever finds themselves in this state.
I'm in this state again, and entering brownie console doesn't help. Every brownie project I have is affected, starting a new command shell doesn't help, and reinstalling brownie doesn't help.
Edit: I even tried rebooting my computer and it's still broken.
Does anyone have any ideas?
I'm having the same problem. Everything works fine and at some point brownie starts to look in /home/username/.brownie/packages/ and relative imports stop working
What Brownie version was working for relative imports? And what solc version? Or what has changed on your end since relative imports were working?
There are many packages being used by Brownie, in addition to the solc compiler that could affect this.
Basically, there has been a change somewhere that has cause this, but it is hard to pinpoint without the above information. It is relatively easy to rule out the solc compiler if you try multiple versions, like 0.8.9 or earlier.
Temporary solution is to use absolute paths. Annoying for sure but at least it should work!
It worked with Brownie version v1.17.1 and solc version 0.8.10+commit.fc410830.Linux.g++, then stopped working with those same versions, then started working again with those same versions, then stopped working again with those same versions, all within about 24 hours. I didn't install, uninstall, or upgrade anything during this time.
My workaround was to put a symlink to my project's contracts folder in /home/username/.brownie/packages.
I think this may have to do with how Brownie is generating the Standard JSON input for solc
but not 100% sure. Check under build/contracts
for the json output from compilation (hopefully something was output). You should then be able to find the field called allSourcePaths
. See if the source path for ContractA
is being set properly. The field named nodes
should also give you this information.
If nothing is output from the failed compilation attempts, then we'll need to find a way to get some of those variables created during compilation.
This will hopefully help nail down the root of the issue.
It doesn't generate the JSON files. Looks like it crashes before it gets to that point.
I tried changing the pragma line from 0.8.10 to 0.8.11 in both contracts. It downloaded the solc-linux-amd64-v0.8.11+commit.d7f03943 compiler, but I still get the same error.
Interestingly enough, I'm using solc v0.8.9 and got relative paths to compile. Can you try that version and see if it works?
Same error for me with 0.8.9, and this has worked for me in the past with 0.8.10:
Brownie v1.17.1 - Python development framework for Ethereum
Downloading from https://solc-bin.ethereum.org/linux-amd64/solc-linux-amd64-v0.8.9+commit.e5eed63a
100%|██████| 12.4M/12.4M [00:27<00:00, 442kiB/s]
solc 0.8.9 successfully installed at: /home/jdavis/.solcx/solc-v0.8.9
Compiling contracts...
Solc version: 0.8.9
Optimizer: Enabled Runs: 200
EVM Version: Istanbul
CompilerError: solc returned the following errors:
ParserError: Source "/home/jdavis/.brownie/packages/contracts/ContractA.sol" not found: File not found.
--> contracts/ContractB.sol:4:1:
|
4 | import "./ContractA.sol";
| ^^^^^^^^^^^^^^^^^^^^^^^^^
Just following up to report that this error still exists in Brownie v1.18.1
One thing I realized I never asked and can’t tell from your original issue… are you running the cli command from the brownie project folder or from within a sub folder of the project? solc can only perform sub folder relative path compilation.
Directly from the brownie project folder.
test_case$ brownie compile
Brownie v1.18.1 - Python development framework for Ethereum
Compiling contracts...
Solc version: 0.8.11
Optimizer: Enabled Runs: 200
EVM Version: Istanbul
CompilerError: solc returned the following errors:
ParserError: Source "/home/jdavis/.brownie/packages/contracts/ContractA.sol" not found: File not found.
--> contracts/ContractB.sol:4:1:
|
4 | import "./ContractA.sol";
| ^^^^^^^^^^^^^^^^^^^^^^^^^
test_case$ ls -R
.:
build contracts interfaces reports scripts tests
./build:
contracts deployments interfaces
./build/contracts:
./build/deployments:
./build/interfaces:
./contracts:
ContractA.sol ContractB.sol
./interfaces:
./reports:
./scripts:
./tests:
I found another workaround. If I set the fully-qualified path to my contracts folder as a remapping, then it doesn't look for the contracts in DATA_FOLDER.
compiler:
solc:
remappings:
- "contracts=/fully/qualified/path/to/contracts"
This seems to be required even though I'm compiling directly from the project folder.
Just a heads up: paths and remappings become a part of contract metadata. If you use an absolute path and publish your metadata (and it's recommended to publish it, see https://sourcify.dev), you might end up exposing your local info - e.g. your username via home dir path. This info can be tied directly to the contract via the metadata hash embedded in the bytecode.
So I figured out the root cause.
I don't remember when or how, but somehow a folder named contracts was created under $HOME/.brownie/packages. Deleting this folder fixed the problem.
I'm leaving this open because I don't believe the presence of a folder named contracts under $HOME/.brownie/packages should break importing files from relative paths.