Sometimes the best way to try to figure out what
SCons is doing is simply to take a look at the
dependency graph that it constructs
based on your SConscript
files.
The --tree
option
will display all or part of the
SCons dependency graph in an
"ASCII art" graphical format
that shows the dependency hierarchy.
For example, given the following input SConstruct
file:
env = Environment(CPPPATH = ['.']) env.Program('prog', ['f1.c', 'f2.c', 'f3.c'])
Running SCons with the --tree=all
option yields:
% scons -Q --tree=all
cc -o f1.o -c -I. f1.c
cc -o f2.o -c -I. f2.c
cc -o f3.o -c -I. f3.c
cc -o prog f1.o f2.o f3.o
+-.
+-SConstruct
+-f1.c
+-f1.o
| +-f1.c
| +-inc.h
+-f2.c
+-f2.o
| +-f2.c
| +-inc.h
+-f3.c
+-f3.o
| +-f3.c
| +-inc.h
+-inc.h
+-prog
+-f1.o
| +-f1.c
| +-inc.h
+-f2.o
| +-f2.c
| +-inc.h
+-f3.o
+-f3.c
+-inc.h
The tree will also be printed when the
-n
(no execute) option is used,
which allows you to examine the dependency graph
for a configuration without actually
rebuilding anything in the tree.
By default SCons uses "ASCII art" to draw the tree. It is
possible to use line-drawing characters (Unicode calls these
Box Drawing) to make a nicer display. To do this, add the
linedraw
qualifier:
% scons -Q --tree=all,linedraw
cc -o f1.o -c -I. f1.c
cc -o f2.o -c -I. f2.c
cc -o f3.o -c -I. f3.c
cc -o prog f1.o f2.o f3.o
└─┬.
├─SConstruct
├─f1.c
├─┬f1.o
│ ├─f1.c
│ └─inc.h
├─f2.c
├─┬f2.o
│ ├─f2.c
│ └─inc.h
├─f3.c
├─┬f3.o
│ ├─f3.c
│ └─inc.h
├─inc.h
└─┬prog
├─┬f1.o
│ ├─f1.c
│ └─inc.h
├─┬f2.o
│ ├─f2.c
│ └─inc.h
└─┬f3.o
├─f3.c
└─inc.h
The --tree
option only prints
the dependency graph for the specified targets
(or the default target(s) if none are specified on the command line).
So if you specify a target like f2.o
on the command line,
the --tree
option will only
print the dependency graph for that file:
% scons -Q --tree=all f2.o
cc -o f2.o -c -I. f2.c
+-f2.o
+-f2.c
+-inc.h
This is, of course, useful for restricting the output from a very large build configuration to just a portion in which you're interested. Multiple targets are fine, in which case a tree will be printed for each specified target:
% scons -Q --tree=all f1.o f3.o
cc -o f1.o -c -I. f1.c
+-f1.o
+-f1.c
+-inc.h
cc -o f3.o -c -I. f3.c
+-f3.o
+-f3.c
+-inc.h
The status
argument may be used
to tell SCons to print status information about
each file in the dependency graph:
% scons -Q --tree=status
cc -o f1.o -c -I. f1.c
cc -o f2.o -c -I. f2.c
cc -o f3.o -c -I. f3.c
cc -o prog f1.o f2.o f3.o
E = exists
R = exists in repository only
b = implicit builder
B = explicit builder
S = side effect
P = precious
A = always build
C = current
N = no clean
H = no cache
[E b ]+-.
[E C ] +-SConstruct
[E C ] +-f1.c
[E B C ] +-f1.o
[E C ] | +-f1.c
[E C ] | +-inc.h
[E C ] +-f2.c
[E B C ] +-f2.o
[E C ] | +-f2.c
[E C ] | +-inc.h
[E C ] +-f3.c
[E B C ] +-f3.o
[E C ] | +-f3.c
[E C ] | +-inc.h
[E C ] +-inc.h
[E B C ] +-prog
[E B C ] +-f1.o
[E C ] | +-f1.c
[E C ] | +-inc.h
[E B C ] +-f2.o
[E C ] | +-f2.c
[E C ] | +-inc.h
[E B C ] +-f3.o
[E C ] +-f3.c
[E C ] +-inc.h
Note that --tree=all,status
is equivalent;
the all
is assumed if only status
is present.
As an alternative to all
,
you can specify --tree=derived
to have SCons only print derived targets
in the tree output,
skipping source files
(like .c
and .h
files):
% scons -Q --tree=derived
cc -o f1.o -c -I. f1.c
cc -o f2.o -c -I. f2.c
cc -o f3.o -c -I. f3.c
cc -o prog f1.o f2.o f3.o
+-.
+-f1.o
+-f2.o
+-f3.o
+-prog
+-f1.o
+-f2.o
+-f3.o
You can use the status
modifier with derived
as well:
% scons -Q --tree=derived,status
cc -o f1.o -c -I. f1.c
cc -o f2.o -c -I. f2.c
cc -o f3.o -c -I. f3.c
cc -o prog f1.o f2.o f3.o
E = exists
R = exists in repository only
b = implicit builder
B = explicit builder
S = side effect
P = precious
A = always build
C = current
N = no clean
H = no cache
[E b ]+-.
[E B C ] +-f1.o
[E B C ] +-f2.o
[E B C ] +-f3.o
[E B C ] +-prog
[E B C ] +-f1.o
[E B C ] +-f2.o
[E B C ] +-f3.o
Note that the order of the --tree=
arguments doesn't matter;
--tree=status,derived
is
completely equivalent.
The default behavior of the --tree
option
is to repeat all of the dependencies each time the library dependency
(or any other dependency file) is encountered in the tree.
If certain target files share other target files,
such as two programs that use the same library:
env = Environment(CPPPATH = ['.'], LIBS = ['foo'], LIBPATH = ['.']) env.Library('foo', ['f1.c', 'f2.c', 'f3.c']) env.Program('prog1.c') env.Program('prog2.c')
Then there can be a lot of repetition in the
--tree=
output:
% scons -Q --tree=all
cc -o f1.o -c -I. f1.c
cc -o f2.o -c -I. f2.c
cc -o f3.o -c -I. f3.c
ar rc libfoo.a f1.o f2.o f3.o
ranlib libfoo.a
cc -o prog1.o -c -I. prog1.c
cc -o prog1 prog1.o -L. -lfoo
cc -o prog2.o -c -I. prog2.c
cc -o prog2 prog2.o -L. -lfoo
+-.
+-SConstruct
+-f1.c
+-f1.o
| +-f1.c
| +-inc.h
+-f2.c
+-f2.o
| +-f2.c
| +-inc.h
+-f3.c
+-f3.o
| +-f3.c
| +-inc.h
+-inc.h
+-libfoo.a
| +-f1.o
| | +-f1.c
| | +-inc.h
| +-f2.o
| | +-f2.c
| | +-inc.h
| +-f3.o
| +-f3.c
| +-inc.h
+-prog1
| +-prog1.o
| | +-prog1.c
| | +-inc.h
| +-libfoo.a
| +-f1.o
| | +-f1.c
| | +-inc.h
| +-f2.o
| | +-f2.c
| | +-inc.h
| +-f3.o
| +-f3.c
| +-inc.h
+-prog1.c
+-prog1.o
| +-prog1.c
| +-inc.h
+-prog2
| +-prog2.o
| | +-prog2.c
| | +-inc.h
| +-libfoo.a
| +-f1.o
| | +-f1.c
| | +-inc.h
| +-f2.o
| | +-f2.c
| | +-inc.h
| +-f3.o
| +-f3.c
| +-inc.h
+-prog2.c
+-prog2.o
+-prog2.c
+-inc.h
In a large configuration with many internal libraries
and include files,
this can very quickly lead to huge output trees.
To help make this more manageable,
a prune
modifier may
be added to the option list,
in which case SCons
will print the name of a target that has
already been visited during the tree-printing
in square brackets ([]
)
as an indication that the dependencies
of the target file may be found
by looking farther up the tree:
% scons -Q --tree=prune
cc -o f1.o -c -I. f1.c
cc -o f2.o -c -I. f2.c
cc -o f3.o -c -I. f3.c
ar rc libfoo.a f1.o f2.o f3.o
ranlib libfoo.a
cc -o prog1.o -c -I. prog1.c
cc -o prog1 prog1.o -L. -lfoo
cc -o prog2.o -c -I. prog2.c
cc -o prog2 prog2.o -L. -lfoo
+-.
+-SConstruct
+-f1.c
+-f1.o
| +-f1.c
| +-inc.h
+-f2.c
+-f2.o
| +-f2.c
| +-inc.h
+-f3.c
+-f3.o
| +-f3.c
| +-inc.h
+-inc.h
+-libfoo.a
| +-[f1.o]
| +-[f2.o]
| +-[f3.o]
+-prog1
| +-prog1.o
| | +-prog1.c
| | +-inc.h
| +-[libfoo.a]
+-prog1.c
+-[prog1.o]
+-prog2
| +-prog2.o
| | +-prog2.c
| | +-inc.h
| +-[libfoo.a]
+-prog2.c
+-[prog2.o]
Like the status
keyword,
the prune
argument by itself
is equivalent to --tree=all,prune
.