-
-
Notifications
You must be signed in to change notification settings - Fork 341
Description
Describe the bug
When a command fails after writing its output file, SCons doesn't delete the target file.
Add feature like make's DELETE_ON_ERROR to allow optional deletion of targets of actions which error out.
See also StackOverFlow question
Required information
- Link to SCons Users thread discussing your issue.
https://pairlist4.pair.net/pipermail/scons-users/2025-July/009515.html
-
Version of SCons
4.8.1 -
Version of Python
3.13.1 -
Which python distribution if applicable (python.org, cygwin, anaconda, macports, brew,etc)
python.org (I think)
- How you installed SCons
pip install scons
- What Platform are you on? (Linux/Windows and which version)
macOS 15.5 Apple Silicon
- How to reproduce your issue?
Extract the three files below SConstruct, command.sh and run.sh, make command.sh and run.sh executable and run run.sh.
The command command.sh copies file1 to file2. If file1 starts with 'bad' it exits with an error code to simulate a crash after the output file was written. SCons is executed three times with file1 having the content "good", "bad", and "good" respectively. At the end of the third scons runs we expect file2 to have "good" but it contains "bad".
- File
SConstruct:
# SCons environment
env = Environment()
# Copy file1 → file2 using command.sh
# Inject an error if file1 starts with 'bad"
file2 = env.Command(
target='file2',
source='file1',
action='./command.sh $SOURCE $TARGET'
)
# Make 'file2' the default target
Default(file2)- File
command.sh:
#!/bin/bash
# Usage: ./command input > output
# Copy source to destination
cp "$1" "$2"
# If the input file stats with 'bad' inject an error AFTER creating the output file.
first_line=$(head -n 1 "$1")
if [[ "$first_line" == bad* ]]; then
exit 1
fi
exit 0- File
run.sh:
#!/bin/bash
# Clean up.
rm -f .sconsign.dblite
rm -f file[12]
values=('good' 'bad' 'good')
i=0
for val in "${values[@]}"; do
((i++))
echo
echo "----- Iteration $i: file1 = '$val' -----"
echo
echo "$val" > file1
scons
echo
echo "File1"
md5 file1
cat -n file1
echo
echo "File2"
md5 file2
cat -n file2
echo
echo "DBlite:"
sconsign .sconsign.dblite
done
exit - Output log
$ ./run.sh
----- Iteration 1: file1 = 'good' -----
scons: Reading SConscript files ...
scons: done reading SConscript files.
scons: Building targets ...
./command.sh file1 file2
scons: done building targets.
File1
MD5 (file1) = d7f986677d9f563bd1794b09d82206a3
1 good
File2
MD5 (file2) = d7f986677d9f563bd1794b09d82206a3
1 good
DBlite:
=== .:
file1: d7f986677d9f563bd1794b09d82206a3 1752702994 5
file2: d7f986677d9f563bd1794b09d82206a3 1752702995 5
file1: d7f986677d9f563bd1794b09d82206a3 1752702994 5
2dbc2dce125a753309a27b7d5157aaaa [./command.sh $SOURCE $TARGET]
----- Iteration 2: file1 = 'bad' -----
scons: Reading SConscript files ...
scons: done reading SConscript files.
scons: Building targets ...
./command.sh file1 file2
scons: *** [file2] Error 1
scons: building terminated because of errors.
File1
MD5 (file1) = df207dc9143c6fabf60b69b9c3035103
1 bad
File2
MD5 (file2) = df207dc9143c6fabf60b69b9c3035103
1 bad
DBlite:
=== .:
file1: df207dc9143c6fabf60b69b9c3035103 1752702995 4
file2: d7f986677d9f563bd1794b09d82206a3 1752702995 5
file1: d7f986677d9f563bd1794b09d82206a3 1752702994 5
2dbc2dce125a753309a27b7d5157aaaa [./command.sh $SOURCE $TARGET]
----- Iteration 3: file1 = 'good' -----
scons: Reading SConscript files ...
scons: done reading SConscript files.
scons: Building targets ...
scons: `file2' is up to date.
scons: done building targets.
File1
MD5 (file1) = d7f986677d9f563bd1794b09d82206a3
1 good
File2
MD5 (file2) = df207dc9143c6fabf60b69b9c3035103
1 bad
DBlite:
=== .:
file1: d7f986677d9f563bd1794b09d82206a3 1752702995 5
file2: d7f986677d9f563bd1794b09d82206a3 1752702995 4
file1: d7f986677d9f563bd1794b09d82206a3 1752702995 5
2dbc2dce125a753309a27b7d5157aaaa [./command.sh $SOURCE $TARGET]
/projects/apio-dev/repo/experimental/scons_bug$ - How you invoke scons (The command line you're using "scons --flags some_arguments")
Running scons three times via run.sh.