-
-
Notifications
You must be signed in to change notification settings - Fork 316
ParseDepends
ParseDepends allows to parse compiler-generated dependency files and let SCons establish the listed dependencies.
As a rule of thumb, never use it unless you have to. SCons has scanners to extract implicit dependencies. However, sometimes the built-in scanners choke on pre-processor statements. Consider the following example: hello.c:
#define FOO_HEADER "foo.h"
#include FOO_HEADER
int main() {
return FOO;
}
foo.h:
#define FOO 42
SConstruct:
Program('hello.c')
scons --tree=prune
As the dependency tree reveals, SCons does not know about foo.h
and does not rebuild hello.o
when foo.h
changes. If the compiler is able to extract implicit dependencies and output those as Make rules, SCons can parse these files and properly set up the dependencies.
Here is an example of how to let gcc generate a dependency file while compiling the object file:
Program("hello.c", CCFLAGS="-MD -MF hello.d")
ParseDepends("hello.d")
SideEffect("hello.d", "hello.o")
GCC generates a dependency file that looks like the following:
hello.o: hello.c foo.h
There is one problem with this approach: Read the full story here DEAD LINK. TL;DR ParseDepends
does not read the file in the first pass, leading to unnecessary rebuilds in the second pass. The reason is, that the signature changes as new dependencies are added (foo.h
in the example above).
If you want to extract the dependencies (and call ParseDepends
) before building the object files, the only viable solution is to use a multi-stage builder with a source scanner:
def parsedep(node, env, path):
print "ParseDepends(%s)" % str(node)
ParseDepends(str(node))
return []
def parsecheck(node, env):
return node.exists()
depscan = Scanner(function=parsedep, skeys=[".d"], scan_check=parsecheck)
depbuild = Builder(
action="$CC -M -MF $TARGET $CCFLAGS -c $SOURCE", suffix=".d", src_suffix=".c"
)
depparse = Builder(
action=Copy("$TARGET", "$SOURCE"),
suffix=".dep",
src_builder=depbuild,
source_scanner=depscan,
)
env = Environment(BUILDERS={"ExtractDependencies": depparse})
dep = env.ExtractDependencies("hello.c")
obj = env.Object("hello.c")
env.Requires(obj, dep)
env.Program(obj)
Some notes:
- The scanner needs to be called when the file exists
- scan_check ensures the node exists before calling the scanner
- A target_scanner in depbuild does not work (it's called before the
.d
file exists)
- The Copy action is useless, the depparse builder exists only to execute a source_scanner
- The dependency file must be generated before the object, therefore the order-only prerequisite
- Mailing List: compiler-generated
.d
files and MD5 signatures - Mailing List:
ParseDepends
questions DEAD LINK - Documentation Issue 1984