In SCons, my command generators create ridiculously long command lines. I'd like to be able to split these commands across multiple lines for readability in the build log.
e.g. I have a SConscipt like:
import os
# create dependency
def my_cmd_generator(source, target, env, for_signature):
return r'''echo its a small world after all \
its a small world after all'''
my_cmd_builder = Builder(generator=my_cmd_generator, suffix = '.foo')
env = Environment()
env.Append( BUILDERS = {'MyCmd' : my_cmd_builder } )
my_cmd = env.MyCmd('foo.foo',os.popen('which bash').read().strip())
AlwaysBuild(my_cmd)
When it executes, I get:
scons: Reading SConscript files ...
scons: done reading SConscript files.
scons: Building targets ...
echo its a small world after all \
its a small world after all
its a small world after all
sh: line 1: its: command not found
scons: *** [foo.foo] Error 127
scons: building terminated because of errors.
Doing this in the python shell with os.system and os.popen works -- I get a readable command string and the sub-shell process interprets all the lines as one command.
>>> import os
>>> cmd = r'''echo its a small world after all \
... its a small world after all'''
>>> print cmd
echo its a small world after all \
its a small world after all
>>> os.system( cmd)
its a small world after all its a small world after all
0
When I do this in SCons, it executes each line one at a time, which is not what I want.
I also want to avoid building up my commands into a shell-script and then executing the shell script, because that will create string escaping madness.
Is this possible?
UPDATE:
cournape,
Thanks for the clue about the $CCCOMSTR. Unfortunately, I'm not using any of the languages that SCons supports out of the box, so I'm creating my own command generator. Using a generator, how can I get SCons to do:
echo its a small world after all its a small world after all'
but print
echo its a small world after all \
its a small world after all
?
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())
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With