[Scons-users] Advice sought

Dominic Binks dominic.binks at thebinkles.net
Sat Feb 7 16:16:05 EST 2015

Hi all,

I'm looking for some expert advice on how to go about implementing (well 
technically modifying) a build system to achieve a separated host and 
target build output.

I'm aware of the 'stock' answer of calling SConscript() twice, once for 
the host and once for the target.  That doesn't meet my needs because 
the rest of the SConscripts will be a nightmare to maintain for the 
following reasons.

We have a number of different binaries we build, some of which are only 
targeted to the host side, some only for the target side and some get 
compiled for both.

In a typical deployment, we're often working with both linux platforms 
for host and target with the host being x86 and target being ARM.  Some 
of the ARM targets we work with use bare-metal toolchains and are not 
capable of building executables.

This means that, should I adopt the call SConscript twice I'll end up 
with every single target I need to build having to do something like:

if target_env:
   obj1 = env.Object()
   obj2 = env.Object()
   if target is not bare_metal:
     prog1 = env.Program()

if host_env:
   obj3 = env.Object()
   obj4 = env.Object()
   prog2 = env.Program()

which I really don't like.  When this combined with other conditional 
parts of the build system will get very messy, very quickly.

The Android build system which is based on make has quite a clean way of 
doing this.  I imagined that VariantDir() would help me out here but 
reading more about it, it doesn't really do what I need.

What I'd like to be able to do is to have two environments that I can 
pass around that represent the host and the target builds (say host_env 
for the host environment and target_env for the target environment) and 
do things like this

obj1 = target_env.Object()
obj2 = target_env.Object()
if target is not bare_metal:
   prog1 = target_env.Program()
obj3 = host_env.Object()
obj4 = host_env.Object()
prog2 = host_env.Program()

I've managed to achieve something like this with the following approach 
- I'm not that happy with it but it works.  I wondered if anyone had any 
better suggestions on how I could handle this.

Essentially I wrap the calls to Object to manipulate the target paths 
based on values from the environment (target or host).  While I didn't 
do this above, I could use the same effect to avoid trying to build 
programs for targets that use bare-metal toolchains as well.

env = Environment()

henv = env.Clone()
henv.VariantDir("build/host", "src")

tenv = env.Clone()
tenv.VariantDir("build/target", "src")
tenv["CCFLAGS"] = ["-Os"]

henv.SConscript("src/SConscript", exports=['env','henv','tenv'], 


import os

Import('henv', 'tenv', 'env')

def hostobj(target, sources):
   return henv.Object(
                  str(henv.File(sources[0]).dir),target), sources)

def targobj(target, sources):
   return tenv.Object(
                  str(tenv.File(sources[0]).dir),target), sources)

host_foo = hostobj('foo', ['foo.c'])
targ_foo = targobj('foo', ['foo.c'])
targ_bar = targobj('bar', ['othersrc/bar.c'])

all = env.Alias([host_foo,targ_foo,targ_bar])


For what it's worth all the referenced C files are just empty files.

This gives me output that looks like this:


I can then assemble a package using a combination of host and target 
binaries to send to customers from the output of the build.

Is this the best way to do this?  Can the output path (i.e. "build") be 
a callable that can reference the environment?

I don't really want to have to wrap all the builders in the environment 
to facilitate this.

Many thanks

More information about the Scons-users mailing list