Chris Warth
Chris Warth

Reputation: 938

scons fails to notice errors in commands with pipes ('|')

scons experts -

Scons thinks that this failing command succeeds, and running scons another time indicates all targets are up-to-date. The fribbleblat command doesn't exist. This behavior occurs when a 'middle' command in a pipeline fails, but not when the last command in the pipeline fails.

How can I propagate errors in piped commands to scons?

import os
from SCons.Script import Environment

env = Environment()
env.Command(source='foo',
            target='bar',
            action='cat $SOURCE | fribbleblat | cat >$TARGET')

Running it twice demonstrates that scons thinks it has built the target correctly.
[edit: I created a file 'foo' with random content, echo dslkfjds >foo ]

$ scons -f SConstruct.exp 
scons: Reading SConscript files ...
scons: done reading SConscript files.
scons: Building targets ...
cat foo | fribbleblat | cat > bar
sh: 1: fribbleblat: not found
cat: write error: Broken pipe
scons: done building targets.

$ scons -f SConstruct.exp 
scons: Reading SConscript files ...
scons: done reading SConscript files.
scons: Building targets ...
scons: `.' is up to date.
scons: done building targets.

$ scons --version
SCons by Steven Knight et al.:
        script: v2.3.0.rel_2.3.0:2870:c8dbbaa4598e, 2013/07/31 13:02:49, by bdbaddog on Williams-MacBook
        engine: v2.3.0, 2013/03/03 09:48:35, by garyo on reepicheep
        engine path: ['/usr/lib/scons/SCons']
Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 The SCons Foundation

Upvotes: 0

Views: 409

Answers (2)

Chris Warth
Chris Warth

Reputation: 938

dirkbaechle is right - Turns out this isn't so much an scons question as a shell question. A little more searching led me to the following answers:

The short answer is 'set -o pipefail', but that only works in bash, ksh, and zsh, but not in sh, so I had to make sure scons was invoking bash as well.

The following scons file works as expected and catches the failed command.

import os
from SCons.Script import Environment

env = Environment()
env['SHELL'] = 'bash'
env['ENV']['SHELLOPTS'] = 'pipefail'

env.Command(source='foo',
            target='bar',
            action='cat $SOURCE | fribbleblat | cat >$TARGET')

Upvotes: 2

dirkbaechle
dirkbaechle

Reputation: 4052

Your assumption is wrong: it's not SCons that thinks the command was successful. On my Linux system the command

> cat test.txt | fribblecat | cat > out.txt

yields "fribblecat: command not found." and an immediate

> echo $?

gives a "0" (success) as result. So the shell opens the output file, and signals a success to SCons. You have to find a way to let the pipe sequence fail on the shell level, e.g. by wrapping your commands in a shell script that checks whether the executable "fribblecat" actually exists. Another option would be to write a simple "fribblecat" Builder, assuming that the executable supports command-line options for the input and output file:

env = Environment()
env.Command('out.txt','test.txt','fribblecat -o $TARGET $SOURCE')

Check out the UserGuide "18. Writing Your Own Builders" ( http://www.scons.org/doc/production/HTML/scons-user.html ), and our guide for writing Tools at http://www.scons.org/wiki/ToolsForFools .

Upvotes: 2

Related Questions