mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
86 lines
3.8 KiB
Markdown
86 lines
3.8 KiB
Markdown
Dependency Analysis
|
|
===================
|
|
|
|
Swift's intra-module dependency analysis is based on a "provides" / "depends"
|
|
system, which is ultimately trying to prove which files do not need to be
|
|
rebuilt. In its simplest form, every file has a list of what it "provides" and
|
|
what it "depends on", and when a file is touched, every file that "depends on"
|
|
what the first file "provides" needs to be rebuilt.
|
|
|
|
The golden rule of dependency analysis is to be conservative. Rebuilding a file
|
|
when you don't have to is annoying. *Not* rebuilding a file when you *do* have
|
|
to is tantamount to a debug-time miscompile!
|
|
|
|
|
|
Kinds of Dependency
|
|
===================
|
|
|
|
There are four major kinds of dependency between files:
|
|
|
|
- `top-level`: use of an unqualified name that is looked up at module scope,
|
|
and definition of a name at module scope. This includes free functions,
|
|
top-level type definitions, and global constants and variables.
|
|
|
|
- `nominal`: use of a particular type, in any way, and declarations that
|
|
change the "shape" of the type (the original definition, and extensions that
|
|
add conformances). The type is identified by its mangled name.
|
|
|
|
- `member`: a two-part entry that constitutes either *providing* a member or
|
|
*accessing* a specific member of a type. This has some complications; see
|
|
below.
|
|
|
|
- `dynamic-lookup`: use of a specific member accessed through `AnyObject`,
|
|
which has special `id`-like rules for member accesses, and definitions of
|
|
`@objc` members that can be accessed this way.
|
|
|
|
The `member` dependency kind has a special entry where the member name is
|
|
empty. This is in the "provides" set of *every file that adds non-private
|
|
members* to a type, making it a superset of provided `nominal` entries. When
|
|
listed as a dependency, it means that something in the file needs to be
|
|
recompiled whenever *any* members are added to the type in question. This is
|
|
currently used for cases of inheritance: superclasses and protocol conformances.
|
|
|
|
The "provides" sets for each file are computed after type-checking has
|
|
completed. The "depends" sets are tracked by instrumenting lookups in the
|
|
compiler. The most common kinds of lookups (qualified name lookup, unqualified
|
|
name lookup, and protocol conformance checks) will already properly record
|
|
dependencies, but direct lookups into a type or module will not, nor will
|
|
dependencies not modeled by a query of some kind. These latter dependencies
|
|
must be handled on a case-by-case basis.
|
|
|
|
Note:
|
|
|
|
> The compiler currently does not track `nominal` dependencies separate from
|
|
> `member` dependencies. Instead, it considers every `member` dependency
|
|
> to implicitly be a `nominal` dependency, since adding a protocol to a type
|
|
> may change its members drastically.
|
|
|
|
|
|
External Dependencies
|
|
=====================
|
|
|
|
External dependencies, including imported Swift module files and Clang headers,
|
|
are tracked using a special `depends-external` set. These dependencies refer
|
|
to files that are external to the module. The Swift driver
|
|
interprets this set specially and decides whether or not the cross-module
|
|
dependencies have changed.
|
|
|
|
Because every Swift file in the module at least has its imports resolved,
|
|
currently every file in the module has the same (complete) list of external
|
|
dependencies. This means if an external dependency changes, everything in the
|
|
module is rebuilt.
|
|
|
|
|
|
Complications
|
|
=============
|
|
|
|
- A file's "provides" set may be different before and after it is compiled --
|
|
declarations can be both added and removed, and other files may depend on the
|
|
declarations that were added and the declarations that were removed. This
|
|
means the dependency graph has to be updated after each file during
|
|
compilation. (This is also why reusing build products is hard.)
|
|
|
|
- If a build stops in the middle, we need to make sure the next build takes
|
|
care of anything that was scheduled to be rebuilt but didn't get rebuilt this
|
|
time.
|