I liked RPGilespie's answer a lot and played around with it for our software package that has ~4500 nodes. I did a little tweaking though and thought I'd post it in case it helps anyone. The main differences are:
1.) I added a command to run at the end of the job to write the total node count to a file based on this post. This saves having to continually hit the file system in the progress function.
2.) Due to 1.) it updates the file every time scons is run (not just when it senses the number of nodes has exceeded the previous maximum). This is useful if you are working on a single part of the tree (i.e. fewer nodes) and are constantly rebuilding just that part.
3.) I have it put the carriage return before the screen update so it can write it straight from the progress_function as opposed to setting a variable for later. This has the added benefit of showing the counter updating while it scans through a mostly-built tree at the beginning.
As a side note, I set the update interval to just 1 here. The reason is because I noticed sometimes the number of nodes associated with a single build command was less than the interval causing it not to print the counter for that line. With the above change to how the counter file is written, I did not see any noticeable slowdown due to this. YMMV.
I put the following at the end of my SConstruct file:
screen = open('/dev/tty', 'w')
node_count = 0
node_count_max = 0
node_count_interval = 1
node_count_fname = str(env.Dir('#')) + '/.scons_node_count'
def progress_function(node):
global node_count, node_count_max, node_count_interval, node_count_fname
node_count += node_count_interval
if node_count > node_count_max: node_count_max = 0
if node_count_max>0 :
screen.write('\r[%3d%%] ' % (node_count*100/node_count_max))
screen.flush()
def progress_finish(target, source, env):
global node_count
with open(node_count_fname, 'w') as f: f.write('%d\n' % node_count)
try:
with open(node_count_fname) as f: node_count_max = int(f.readline())
except: pass
Progress(progress_function, interval=node_count_interval)
progress_finish_command = Command('progress_finish', [], progress_finish)
Depends(progress_finish_command, BUILD_TARGETS)
if 'progress_finish' not in BUILD_TARGETS:
BUILD_TARGETS.append('progress_finish')
Example output is:
[ 0%] Installing [/Users/davidl/HallD/builds/sim-recon/Darwin_macosx10.11-x86_64-llvm8.0.0/bin/MakeEventWriterROOT.pl]
[ 0%] Installing [/Users/davidl/HallD/builds/sim-recon/Darwin_macosx10.11-x86_64-llvm8.0.0/bin/MakeReactionPlugin.pl]
[ 0%] Compiling [programs/Simulation/genr8/genkin.c]
[ 0%] Compiling [programs/Simulation/genr8/genr8.c]
[ 3%] Compiling [programs/Utilities/hddm/hddm-cpp.cpp]
[ 3%] Compiling [programs/Utilities/hddm/XString.cpp]
[ 3%] Compiling [programs/Utilities/hddm/XParsers.cpp]
[ 3%] Compiling [programs/Utilities/hddm/md5.c]
[ 4%] Compiling [external/xstream/src/base64.cpp]
[ 4%] Compiling [external/xstream/src/bz.cpp]
[ 4%] Compiling [external/xstream/src/common.cpp]
[ 4%] Compiling [external/xstream/src/dater.cpp]
[ 4%] Linking [.Darwin_macosx10.11-x86_64-llvm8.0.0/programs/Simulation/genr8/genr8]
[ 4%] Installing [/Users/davidl/HallD/builds/sim-recon/Darwin_macosx10.11-x86_64-llvm8.0.0/bin/genr8]
[ 4%] Compiling [external/xstream/src/debug.cpp]
[ 4%] Compiling [external/xstream/src/digest.cpp]
...
For completeness, here are my COMSTR definitions:
env.Replace( CCCOMSTR = "Compiling [$SOURCE]",
CXXCOMSTR = "Compiling [$SOURCE]",
FORTRANPPCOMSTR = "Compiling [$SOURCE]",
FORTRANCOMSTR = "Compiling [$SOURCE]",
SHCCCOMSTR = "Compiling [$SOURCE]",
SHCXXCOMSTR = "Compiling [$SOURCE]",
LINKCOMSTR = "Linking [$TARGET]",
SHLINKCOMSTR = "Linking [$TARGET]",
INSTALLSTR = "Installing [$TARGET]",
ARCOMSTR = "Archiving [$TARGET]",
RANLIBCOMSTR = "Ranlib [$TARGET]")