views:

704

answers:

3

I've recently picked up scons to implement a multi-platform build framework for a medium sized C++ project. The build generates a bunch of unit-tests which should be invoked at the end of it all. How does one achieve that sort of thing?

For example in my top level sconstruct, I have

subdirs=['list', 'of', 'my', 'subprojects']
for subdir in subdirs:
    SConscript(dirs=subdir, exports='env', name='sconscript',
       variant_dir=subdir+os.sep+'build'+os.sep+mode, duplicate=0)

Each of the subdir has its unit-tests, however, since there are dependencies between the dlls and executables built inside them - i want to hold the running of tests until all the subdirs have been built and installed (I mean, using env.Install).

Where should I write the loop to iterate through the built tests and execute them? I tried putting it just after this loop - but since scons doesn't let you control the order of execution - it gets executed well before I want it to.

Please help a scons newbie. :)

thanks,

A: 

In terms of dependencies, what you want is for all the test actions to depend on all the program-built actions. A way of doing this is to create and export a dummy-target to all the subdirectories' sconscript files, and in the sconscript files, make the dummy-target Depends on the main targets, and have the test targets Depends on the dummy-target.

I'm having a bit of trouble figuring out how to set up the dummy target, but this basically works:

(in top-level SConstruct)

dummy = env.Command('.all_built', 'SConstruct', 'echo Targets built. > $TARGET')
Export('dummy')

(in each sub-directory's SConscript)

Import('dummy')
for target in target_list:
  Depends(dummy, targe)
for test in test_list:
  Depends(test, dummy)

I'm sure further refinements are possible, but maybe this'll get you started.

EDIT: also worth pointing out this page on the subject.

Aidan Cully
+2  A: 

SCons, like Make, uses a declarative method to solving the build problem. You don't want to tell SCons how to do its job. You want to document all the dependencies and then let SCons solve how it builds everything.

If something is being executed before something else, you need to create and hook up the dependencies.

If you want to create dmy touch files, you can create a custom builder like:

import time

def action(target, source, env):
    os.system('echo here I am running other build')
    dmy_fh = open('dmy_file','w')
    dmy_fh.write( 'Dummy dependency file created at %4d.%02d.%02d %02dh%02dm%02ds\n'%time.localtime()[0:6])
    dmy_fh.close()

bldr = Builder(action=action)
env.Append( BUILDERS = {'SubBuild' : bldr } )

env.SubBuild(srcs,tgts)

It is very important to put the timestamp into the dummy file, because scons uses md5 hashes. If you have an empty file, the md5 will always be the same and it may decide to not do subsequent build steps. If you need to generate different tweaks on a basic command, you can use function factories to modify a template. e.g.

def gen_a_echo_cmd_func(echo_str):
    def cmd_func(target,source,env):
        cmd = 'echo %s'%echo_str
        print cmd
        os.system(cmd)
    return cmd_fun

bldr = Builder(action = gen_a_echo_cmd_func('hi'))
env.Append(BUILDERS = {'Hi': bldr})
env.Hi(srcs,tgts)

bldr = Builder(action = gen_a_echo_cmd_func('bye'))
env.Append(BUILDERS = {'Bye': bldr})
env.Bye(srcs,tgts)

If you have something that you want to automatically inject into the scons build flow ( e.g. something that compresses all your build log files after everything else has run ), see my question here.

Ross Rogers
A: 

The solution should be as simple as this.

Make the result of the Test builders depend on the result of the Install builder

In pseudo:

test = Test(dlls)
result = Install(dlls)
Depends(test,result)

The best way would be if the Test builder actually worked out the dll dependencies for you, but there may be all kinds of reasons it doesn't do that.

daramarak