Module ethers_solc::project
source · Expand description
Manages compiling of a Project
The compilation of a project is performed in several steps.
First the project’s dependency graph crate::Graph
is constructed and all imported
dependencies are resolved. The graph holds all the relationships between the files and their
versions. From there the appropriate version set is derived
crate::Graph
which need to be compiled with different
crate::Solc
versions.
At this point we check if we need to compile a source file or whether we can reuse an existing
Artifact
. We don’t to compile if:
- caching is enabled
- the file is not dirty
- the artifact for that file exists
This concludes the preprocessing, and we now have either
- only
Source
files that need to be compiled - only cached
Artifacts
, compilation can be skipped. This is considered an unchanged, cached project - Mix of both
Source
andArtifacts
, only theSource
files need to be compiled, theArtifacts
can be reused.
The final step is invoking Solc
via the standard JSON format.
Notes on Import Path Resolution
In order to be able to support reproducible builds on all platforms, the Solidity compiler has to abstract away the details of the filesystem where source files are stored. Paths used in imports must work the same way everywhere while the command-line interface must be able to work with platform-specific paths to provide good user experience. This section aims to explain in detail how Solidity reconciles these requirements.
The compiler maintains an internal database (virtual filesystem or VFS for short) where each source unit is assigned a unique source unit name which is an opaque and unstructured identifier. When you use the import statement, you specify an import path that references a source unit name. If the compiler does not find any source unit name matching the import path in the VFS, it invokes the callback, which is responsible for obtaining the source code to be placed under that name.
This becomes relevant when dealing with resolved imports
Relative Imports
import "./math/math.sol";
import "contracts/tokens/token.sol";
In the above ./math/math.sol
and contracts/tokens/token.sol
are import paths while the
source unit names they translate to are contracts/math/math.sol
and
contracts/tokens/token.sol
respectively.
Direct Imports
An import that does not start with ./
or ../
is a direct import.
import "/project/lib/util.sol"; // source unit name: /project/lib/util.sol
import "lib/util.sol"; // source unit name: lib/util.sol
import "@openzeppelin/address.sol"; // source unit name: @openzeppelin/address.sol
import "https://example.com/token.sol"; // source unit name: <https://example.com/token.sol>
After applying any import remappings the import path simply becomes the source unit name.
Import Remapping
import "github.com/ethereum/dapp-bin/library/math.sol"; // source unit name: dapp-bin/library/math.sol
If compiled with solc github.com/ethereum/dapp-bin/=dapp-bin/
the compiler will look for the
file in the VFS under dapp-bin/library/math.sol
. If the file is not available there, the
source unit name will be passed to the Host Filesystem Loader, which will then look in
/project/dapp-bin/library/iterable_mapping.sol
Caching and Change detection
If caching is enabled in the Project a cache file will be created upon a successful solc build. The cache file stores metadata for all the files that were provided to solc. For every file the cache file contains a dedicated cache entry, which represents the state of the file. A solidity file can contain several contracts, for every contract a separate artifact is emitted. Therefor the entry also tracks all artifacts emitted by a file. A solidity file can also be compiled with several solc versions.
For example in A(<=0.8.10) imports C(>0.4.0)
and
B(0.8.11) imports C(>0.4.0)
, both A
and B
import C
but there’s no solc version that’s
compatible with A
and B
, in which case two sets are compiled: [A
, C
] and [B
, C
].
This is reflected in the cache entry which tracks the file’s artifacts by version.
The cache makes it possible to detect changes during recompilation, so that only the changed, dirty, files need to be passed to solc. A file will be considered as dirty if:
- the file is new, not included in the existing cache
- the file was modified since the last compiler run, detected by comparing content hashes
- any of the imported files is dirty
- the file’s artifacts don’t exist, were deleted.
Recompiling a project with cache enabled detects all files that meet these criteria and provides solc with only these dirty files instead of the entire source set.