Thanks to cournape's tip about Actions versus Generators ( and eclipse pydev debugger), I've finally figured out what I need to do. You want to pass in your function to the 'Builder' class as an 'action' not a 'generator'. This will allow you to actually execute the os.system or os.popen call directly. Here's the updated code:
import os
def my_action(source, target, env):
cmd = r'''echo its a small world after all \
its a small world after all'''
print cmd
return os.system(cmd)
my_cmd_builder = Builder(
action=my_action, # <-- CRUCIAL PIECE OF SOLUTION
suffix = '.foo')
env = Environment()
env.Append( BUILDERS = {'MyCmd' : my_cmd_builder } )
my_cmd = env.MyCmd('foo.foo',os.popen('which bash').read().strip())
This SConstruct file will produce the following output:
scons: Reading SConscript files ...
scons: done reading SConscript files.
scons: Building targets ...
my_action(["foo.foo"], ["/bin/bash"])
echo its a small world after all \
its a small world after all
its a small world after all its a small world after all
scons: done building targets.
The other crucial piece is to remember that switching from a 'generator' to an 'action' means the target you're building no longer has an implicit dependency on the actual string that you are passing to the sub-process shell. You can re-create this dependency by adding the string into your environment.
e.g., the solution that I personally want looks like:
import os
cmd = r'''echo its a small world after all \
its a small world after all'''
def my_action(source, target, env):
print cmd
return os.system(cmd)
my_cmd_builder = Builder(
action=my_action,
suffix = '.foo')
env = Environment()
env['_MY_CMD'] = cmd # <-- CREATE IMPLICIT DEPENDENCY ON CMD STRING
env.Append( BUILDERS = {'MyCmd' : my_cmd_builder } )
my_cmd = env.MyCmd('foo.foo',os.popen('which bash').read().strip())