From 0307a78b041c16b5e9ad60c19f669e856f32f49f Mon Sep 17 00:00:00 2001 From: Steven Knight Date: Tue, 26 Aug 2008 12:55:22 +0000 Subject: Issue 2099: have Execute() print an error message if an action fails. Better document the behavior of returning the exit status, and that exit-on-failure is the SConscript writer's responsibility. --- doc/man/scons.1 | 27 +++++++++++++++++++++++++++ doc/user/factories.in | 26 ++++++++++++++++++++++++-- doc/user/factories.xml | 26 ++++++++++++++++++++++++-- src/CHANGES.txt | 3 +++ src/engine/SCons/Environment.py | 5 +++++ test/Execute.py | 37 ++++++++++++++++++++++++------------- 6 files changed, 107 insertions(+), 17 deletions(-) diff --git a/doc/man/scons.1 b/doc/man/scons.1 index 21e475e..baaa234 100644 --- a/doc/man/scons.1 +++ b/doc/man/scons.1 @@ -3545,6 +3545,33 @@ The exit value of the command or return value of the Python function will be returned. +Note that +.B scons +will print an error message if the executed +.I action +fails--that is, +exits with or returns a non-zero value. +.B scons +will +.I not , +however, +automatically terminate the build +if the specified +.I action +fails. +If you want the build to stop in response to a failed +.BR Execute () +call, +you must explicitly check for a non-zero return value: + +.ES +Execute(Copy('file.out', 'file.in')) + +if Execute("mkdir sub/dir/ectory"): + # The mkdir failed, don't try to build. + Exit(1) +.EE + '\""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""" .TP .RI Exit([ value ]) diff --git a/doc/user/factories.in b/doc/user/factories.in index 94af6a3..34973f1 100644 --- a/doc/user/factories.in +++ b/doc/user/factories.in @@ -440,11 +440,10 @@ You can also execute an &Action; returned by a factory (or actually, any &Action;) at the time the &SConscript; file is read - by wrapping it up in the &Execute; function. + by using the &Execute; function. For example, if we need to make sure that a directory exists before we build any targets, - @@ -482,4 +481,27 @@ + + + The &Execute; function returns the exit status + or return value of the underlying action being executed. + It will also print an error message if the action + fails and returns a non-zero value. + &SCons; will not, however, + actually stop the build if the action fails. + If you want the build to stop + in response to a failure in an action called by &Execute;, + you must do so by explicitly + checking the return value + and calling the &Exit; function + (or a Python equivalent): + + + + + if Execute(Mkdir('__ROOT__/tmp/my_temp_directory')): + # A problem occurred while making the temp directory. + Exit(1) + + diff --git a/doc/user/factories.xml b/doc/user/factories.xml index ae6e9d0..9599930 100644 --- a/doc/user/factories.xml +++ b/doc/user/factories.xml @@ -395,11 +395,10 @@ You can also execute an &Action; returned by a factory (or actually, any &Action;) at the time the &SConscript; file is read - by wrapping it up in the &Execute; function. + by using the &Execute; function. For example, if we need to make sure that a directory exists before we build any targets, - @@ -441,4 +440,27 @@ + + + The &Execute; function returns the exit status + or return value of the underlying action being executed. + It will also print an error message if the action + fails and returns a non-zero value. + &SCons; will not, however, + actually stop the build if the action fails. + If you want the build to stop + in response to a failure in an action called by &Execute;, + you must do so by explicitly + checking the return value + and calling the &Exit; function + (or a Python equivalent): + + + + + if Execute(Mkdir('/tmp/my_temp_directory')): + # A problem occurred while making the temp directory. + Exit(1) + + diff --git a/src/CHANGES.txt b/src/CHANGES.txt index 292c741..05c7f96 100644 --- a/src/CHANGES.txt +++ b/src/CHANGES.txt @@ -24,6 +24,9 @@ RELEASE 1.0.0 - XXX - Document the GetLaunchDir() function in the User's Guide. + - Have the env.Execute() method print an error message if the + executed command fails. + From Greg Noel: - Handle yacc/bison on newer Mac OS X versions creating file.hpp, diff --git a/src/engine/SCons/Environment.py b/src/engine/SCons/Environment.py index f972550..db69d62 100644 --- a/src/engine/SCons/Environment.py +++ b/src/engine/SCons/Environment.py @@ -1813,6 +1813,11 @@ class Base(SubstitutionEnvironment): action = apply(self.Action, (action,) + args, kw) result = action([], [], self) if isinstance(result, SCons.Errors.BuildError): + errstr = result.errstr + if result.filename: + errstr = result.filename + ': ' + errstr + import sys + sys.stderr.write("scons: *** %s\n" % errstr) return result.status else: return result diff --git a/test/Execute.py b/test/Execute.py index 44abc73..35ee949 100644 --- a/test/Execute.py +++ b/test/Execute.py @@ -63,6 +63,9 @@ Execute(lambda target, source, env: shutil.copy('i.in', 'i.out')) Execute(Action(lambda target, source, env: shutil.copy('j.in', 'j.out'))) env.Execute(lambda target, source, env: shutil.copy('k.in', 'k.out')) env.Execute(Action(lambda target, source, env: shutil.copy('l.in', 'l.out'))) + +Execute(Copy('m.out', 'm.in')) +Execute(Copy('nonexistent.out', 'nonexistent.in')) """ % locals()) test.write('a.in', "a.in\n") @@ -77,20 +80,28 @@ test.write('i.in', "i.in\n") test.write('j.in', "j.in\n") test.write('k.in', "k.in\n") test.write('l.in', "l.in\n") +test.write('m.in', "m.in\n") + +expect = """\ +scons: *** Error 1 +scons: *** Error 2 +scons: *** nonexistent.in: No such file or directory +""" -test.run(arguments = '.') +test.run(arguments = '.', stderr=expect) -test.fail_test(test.read('a.out') != "a.in\n") -test.fail_test(test.read('b.out') != "b.in\n") -test.fail_test(test.read('c.out') != "c.in\n") -test.fail_test(test.read('d.out') != "d.in\n") -test.fail_test(test.read('e.out') != "e.in\n") -test.fail_test(test.read('f.out') != "f.in\n") -test.fail_test(test.read('g.out') != "g.in\n") -test.fail_test(test.read('h.out') != "h.in\n") -test.fail_test(test.read('i.out') != "i.in\n") -test.fail_test(test.read('j.out') != "j.in\n") -test.fail_test(test.read('k.out') != "k.in\n") -test.fail_test(test.read('l.out') != "l.in\n") +test.must_match('a.out', "a.in\n") +test.must_match('b.out', "b.in\n") +test.must_match('c.out', "c.in\n") +test.must_match('d.out', "d.in\n") +test.must_match('e.out', "e.in\n") +test.must_match('f.out', "f.in\n") +test.must_match('g.out', "g.in\n") +test.must_match('h.out', "h.in\n") +test.must_match('i.out', "i.in\n") +test.must_match('j.out', "j.in\n") +test.must_match('k.out', "k.in\n") +test.must_match('l.out', "l.in\n") +test.must_match('m.out', "m.in\n") test.pass_test() -- cgit v0.12