diff options
author | William Deegan <bill@baddogconsulting.com> | 2020-02-21 02:53:57 (GMT) |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-02-21 02:53:57 (GMT) |
commit | 962736590a7367450460a594ef2cd3f594fe72ec (patch) | |
tree | 22fe52c634a5b1a483a9ad208b4e41b751a7392a | |
parent | 54c36335c575a98fcf6de2ff84c16b66efa7da5e (diff) | |
parent | 0814deb56ca870f631a415a3255ce8dd99688e01 (diff) | |
download | SCons-962736590a7367450460a594ef2cd3f594fe72ec.zip SCons-962736590a7367450460a594ef2cd3f594fe72ec.tar.gz SCons-962736590a7367450460a594ef2cd3f594fe72ec.tar.bz2 |
Merge pull request #3483 from grossag/topic/grossag/compoundactionscan
Add support for scanning multiple entries in an action string
-rwxr-xr-x | src/CHANGES.txt | 4 | ||||
-rw-r--r-- | src/engine/SCons/Action.py | 81 | ||||
-rw-r--r-- | src/engine/SCons/Action.xml | 39 | ||||
-rw-r--r-- | test/Batch/action-changed.py | 16 | ||||
-rw-r--r-- | test/Repository/Program.py | 195 | ||||
-rw-r--r-- | test/Repository/StaticLibrary.py | 165 | ||||
-rw-r--r-- | test/implicit/IMPLICIT_COMMAND_DEPENDENCIES.py | 73 |
7 files changed, 362 insertions, 211 deletions
diff --git a/src/CHANGES.txt b/src/CHANGES.txt index 9d2f51c..025e046 100755 --- a/src/CHANGES.txt +++ b/src/CHANGES.txt @@ -28,6 +28,10 @@ RELEASE VERSION/DATE TO BE FILLED IN LATER - Updated design doc to use the correct syntax for Depends() From Adam Gross: + - Added support for scanning multiple entries in an action string if + IMPLICIT_COMMAND_DEPENDENCIES is set to 2. This opts into more thorough + action scanning where every string in the command is scanned to determine + if it is a non-source and non-target path. - Added support for taking instances of the Value class as implicit dependencies. - Added new module SCons.Scanner.Python to allow scanning .py files. diff --git a/src/engine/SCons/Action.py b/src/engine/SCons/Action.py index 0b7282c..8a8cf27 100644 --- a/src/engine/SCons/Action.py +++ b/src/engine/SCons/Action.py @@ -968,11 +968,33 @@ class CommandAction(_ActionAction): return env.subst_target_source(cmd, SUBST_SIG, target, source) def get_implicit_deps(self, target, source, env, executor=None): + """Return the implicit dependencies of this action's command line.""" icd = env.get('IMPLICIT_COMMAND_DEPENDENCIES', True) if is_String(icd) and icd[:1] == '$': icd = env.subst(icd) + if not icd or icd in ('0', 'None'): return [] + + try: + icd_int = int(icd) + except ValueError: + icd_int = None + + if (icd_int and icd_int > 1) or icd == 'all': + # An integer value greater than 1 specifies the number of entries + # to scan. "all" means to scan all. + return self._get_implicit_deps_heavyweight(target, source, env, executor, icd_int) + else: + # Everything else (usually 1 or True) means that we want + # lightweight dependency scanning. + return self._get_implicit_deps_lightweight(target, source, env, executor) + + def _get_implicit_deps_lightweight(self, target, source, env, executor): + """ + Lightweight dependency scanning involves only scanning the first entry + in an action string, even if it contains &&. + """ from SCons.Subst import SUBST_SIG if executor: cmd_list = env.subst_list(self.cmd_list, SUBST_SIG, executor=executor) @@ -990,6 +1012,65 @@ class CommandAction(_ActionAction): res.append(env.fs.File(d)) return res + def _get_implicit_deps_heavyweight(self, target, source, env, executor, + icd_int): + """ + Heavyweight dependency scanning involves scanning more than just the + first entry in an action string. The exact behavior depends on the + value of icd_int. Only files are taken as implicit dependencies; + directories are ignored. + + If icd_int is an integer value, it specifies the number of entries to + scan for implicit dependencies. Action strings are also scanned after + a &&. So for example, if icd_int=2 and the action string is + "cd <some_dir> && $PYTHON $SCRIPT_PATH <another_path>", the implicit + dependencies would be the path to the python binary and the path to the + script. + + If icd_int is None, all entries are scanned for implicit dependencies. + """ + + # Avoid circular and duplicate dependencies by not providing source, + # target, or executor to subst_list. This causes references to + # $SOURCES, $TARGETS, and all related variables to disappear. + from SCons.Subst import SUBST_SIG + cmd_list = env.subst_list(self.cmd_list, SUBST_SIG, conv=lambda x: x) + res = [] + + for cmd_line in cmd_list: + if cmd_line: + entry_count = 0 + for entry in cmd_line: + d = str(entry) + if ((icd_int is None or entry_count < icd_int) and + not d.startswith(('&', '-', '/') if os.name == 'nt' + else ('&', '-'))): + m = strip_quotes.match(d) + if m: + d = m.group(1) + + if d: + # Resolve the first entry in the command string using + # PATH, which env.WhereIs() looks in. + # For now, only match files, not directories. + p = os.path.abspath(d) if os.path.isfile(d) else None + if not p and entry_count == 0: + p = env.WhereIs(d) + + if p: + res.append(env.fs.File(p)) + + entry_count = entry_count + 1 + else: + entry_count = 0 if d == '&&' else entry_count + 1 + + # Despite not providing source and target to env.subst() above, we + # can still end up with sources in this list. For example, files in + # LIBS will still resolve in env.subst(). This won't result in + # circular dependencies, but it causes problems with cache signatures + # changing between full and incremental builds. + return [r for r in res if r not in target and r not in source] + class CommandGeneratorAction(ActionBase): """Class for command-generator actions.""" diff --git a/src/engine/SCons/Action.xml b/src/engine/SCons/Action.xml index 7a8194e..d85af3b 100644 --- a/src/engine/SCons/Action.xml +++ b/src/engine/SCons/Action.xml @@ -58,6 +58,45 @@ not be added to the targets built with that construction environment. </para> +<para> +If the construction variable +&cv-IMPLICIT_COMMAND_DEPENDENCIES; +is set to <literal>2</literal> or higher, +then that number of entries in the command +string will be scanned for relative or absolute +paths. The count will reset after any +<literal>&&</literal> entries are found. +The first command in the action string and +the first after any <literal>&&</literal> +entries will be found by searching the +<varname>PATH</varname> variable in the +<varname>ENV</varname> environment used to +execute the command. All other commands will +only be found if they are absolute paths or +valid paths relative to the working directory. +</para> + +<para> +If the construction variable +&cv-IMPLICIT_COMMAND_DEPENDENCIES; +is set to <literal>all</literal>, then +all entries in all command strings will be +scanned for relative or absolute paths. If +any are present, they will be added as +implicit dependencies to the targets built +with that construction environment. +not be added to the targets built with that +construction environment. The first command +in the action string and the first after any +<literal>&&</literal> entries will be found +by searching the <varname>PATH</varname> +variable in the <varname>ENV</varname> +environment used to execute the command. +All other commands will only be found if they +are absolute paths or valid paths relative +to the working directory. +</para> + <example_commands> env = Environment(IMPLICIT_COMMAND_DEPENDENCIES = 0) </example_commands> diff --git a/test/Batch/action-changed.py b/test/Batch/action-changed.py index b2b1673..da5115e 100644 --- a/test/Batch/action-changed.py +++ b/test/Batch/action-changed.py @@ -33,7 +33,11 @@ import os import TestSCons -python = TestSCons.python +# swap slashes because on py3 on win32 inside the generated build.py +# the backslashes are getting interpretted as unicode which is +# invalid. +python = TestSCons.python.replace('\\','//') +_python_ = TestSCons._python_ test = TestSCons.TestSCons() @@ -53,17 +57,21 @@ sys.exit(0) test.write('build.py', build_py_contents % (python, 'one')) os.chmod(test.workpath('build.py'), 0o755) +build_py_workpath = test.workpath('build.py') + +# Provide IMPLICIT_COMMAND_DEPENDENCIES=2 so we take a dependency on build.py. +# Without that, we only scan the first entry in the action string. test.write('SConstruct', """ -env = Environment() +env = Environment(IMPLICIT_COMMAND_DEPENDENCIES=2) env.PrependENVPath('PATHEXT', '.PY') -bb = Action(r'"%s" $CHANGED_TARGETS -- $CHANGED_SOURCES', +bb = Action(r'%(_python_)s "%(build_py_workpath)s" $CHANGED_TARGETS -- $CHANGED_SOURCES', batch_key=True, targets='CHANGED_TARGETS') env['BUILDERS']['Batch'] = Builder(action=bb) env.Batch('f1.out', 'f1.in') env.Batch('f2.out', 'f2.in') env.Batch('f3.out', 'f3.in') -""" % test.workpath('build.py')) +""" % locals()) test.write('f1.in', "f1.in\n") test.write('f2.in', "f2.in\n") diff --git a/test/Repository/Program.py b/test/Repository/Program.py index 1eb18d8..9e8af77 100644 --- a/test/Repository/Program.py +++ b/test/Repository/Program.py @@ -32,25 +32,22 @@ if sys.platform == 'win32': else: _exe = '' -test = TestSCons.TestSCons() - - - -# First, test a single repository. -test.subdir('repository', 'work1') - -repository = test.workpath('repository') -repository_foo_c = test.workpath('repository', 'foo.c') -work1_foo = test.workpath('work1', 'foo' + _exe) -work1_foo_c = test.workpath('work1', 'foo.c') - -test.write(['work1', 'SConstruct'], r""" +for implicit_deps in ['0', '1', '2', 'all']: + # First, test a single repository. + test = TestSCons.TestSCons() + test.subdir('repository', 'work1') + repository = test.workpath('repository') + repository_foo_c = test.workpath('repository', 'foo.c') + work1_foo = test.workpath('work1', 'foo' + _exe) + work1_foo_c = test.workpath('work1', 'foo.c') + + test.write(['work1', 'SConstruct'], r""" Repository(r'%s') -env = Environment() +env = Environment(IMPLICIT_COMMAND_DEPENDENCIES=%s) env.Program(target= 'foo', source = Split('aaa.c bbb.c foo.c')) -""" % repository) +""" % (repository, implicit_deps)) -test.write(['repository', 'aaa.c'], r""" + test.write(['repository', 'aaa.c'], r""" #include <stdio.h> void aaa(void) @@ -59,7 +56,7 @@ aaa(void) } """) -test.write(['repository', 'bbb.c'], r""" + test.write(['repository', 'bbb.c'], r""" #include <stdio.h> void bbb(void) @@ -68,7 +65,7 @@ bbb(void) } """) -test.write(['repository', 'foo.c'], r""" + test.write(['repository', 'foo.c'], r""" #include <stdio.h> #include <stdlib.h> @@ -85,21 +82,21 @@ main(int argc, char *argv[]) } """) -# Make the entire repository non-writable, so we'll detect -# if we try to write into it accidentally. -test.writable('repository', 0) + # Make the entire repository non-writable, so we'll detect + # if we try to write into it accidentally. + test.writable('repository', 0) -test.run(chdir = 'work1', arguments = '.') + test.run(chdir = 'work1', arguments = '.') -test.run(program = work1_foo, stdout = """repository/aaa.c + test.run(program = work1_foo, stdout = """repository/aaa.c repository/bbb.c repository/foo.c """) -test.up_to_date(chdir = 'work1', arguments = '.') + test.up_to_date(chdir = 'work1', arguments = '.') -# -test.write(['work1', 'bbb.c'], r""" + # + test.write(['work1', 'bbb.c'], r""" #include <stdio.h> void bbb(void) @@ -108,17 +105,17 @@ bbb(void) } """) -test.run(chdir = 'work1', arguments = '.') + test.run(chdir = 'work1', arguments = '.') -test.run(program = work1_foo, stdout = """repository/aaa.c + test.run(program = work1_foo, stdout = """repository/aaa.c work1/bbb.c repository/foo.c """) -test.up_to_date(chdir = 'work1', arguments = '.') + test.up_to_date(chdir = 'work1', arguments = '.') -# -test.write(['work1', 'aaa.c'], r""" + # + test.write(['work1', 'aaa.c'], r""" #include <stdio.h> void aaa(void) @@ -127,7 +124,7 @@ aaa(void) } """) -test.write(['work1', 'foo.c'], r""" + test.write(['work1', 'foo.c'], r""" #include <stdio.h> #include <stdlib.h> extern void aaa(void); @@ -143,57 +140,57 @@ main(int argc, char *argv[]) } """) -test.run(chdir = 'work1', arguments = '.') + test.run(chdir = 'work1', arguments = '.') -test.run(program = work1_foo, stdout = """work1/aaa.c + test.run(program = work1_foo, stdout = """work1/aaa.c work1/bbb.c work1/foo.c """) -test.up_to_date(chdir = 'work1', arguments = '.') + test.up_to_date(chdir = 'work1', arguments = '.') -# -test.unlink(['work1', 'aaa.c']) + # + test.unlink(['work1', 'aaa.c']) -test.run(chdir = 'work1', arguments = '.') + test.run(chdir = 'work1', arguments = '.') -test.run(program = work1_foo, stdout = """repository/aaa.c + test.run(program = work1_foo, stdout = """repository/aaa.c work1/bbb.c work1/foo.c """) -test.up_to_date(chdir = 'work1', arguments = '.') + test.up_to_date(chdir = 'work1', arguments = '.') -# -test.unlink(['work1', 'bbb.c']) -test.unlink(['work1', 'foo.c']) + # + test.unlink(['work1', 'bbb.c']) + test.unlink(['work1', 'foo.c']) -test.run(chdir = 'work1', arguments = '.') + test.run(chdir = 'work1', arguments = '.') -test.run(program = work1_foo, stdout = """repository/aaa.c + test.run(program = work1_foo, stdout = """repository/aaa.c repository/bbb.c repository/foo.c """) -test.up_to_date(chdir = 'work1', arguments = '.') + test.up_to_date(chdir = 'work1', arguments = '.') -# Now, test multiple repositories. -test.subdir('repository.new', 'repository.old', 'work2') + # Now, test multiple repositories. + test.subdir('repository.new', 'repository.old', 'work2') -repository_new = test.workpath('repository.new') -repository_old = test.workpath('repository.old') -work2_foo = test.workpath('work2', 'foo' + _exe) + repository_new = test.workpath('repository.new') + repository_old = test.workpath('repository.old') + work2_foo = test.workpath('work2', 'foo' + _exe) -test.write(['work2', 'SConstruct'], r""" + test.write(['work2', 'SConstruct'], r""" Repository(r'%s') Repository(r'%s') env = Environment() env.Program(target= 'foo', source = Split('aaa.c bbb.c foo.c')) """ % (repository_new, repository_old)) -test.write(['repository.old', 'aaa.c'], r""" + test.write(['repository.old', 'aaa.c'], r""" #include <stdio.h> void aaa(void) @@ -202,7 +199,7 @@ aaa(void) } """) -test.write(['repository.old', 'bbb.c'], r""" + test.write(['repository.old', 'bbb.c'], r""" #include <stdio.h> void bbb(void) @@ -211,7 +208,7 @@ bbb(void) } """) -test.write(['repository.old', 'foo.c'], r""" + test.write(['repository.old', 'foo.c'], r""" #include <stdio.h> #include <stdlib.h> extern void aaa(void); @@ -227,24 +224,24 @@ main(int argc, char *argv[]) } """) -# Make both repositories non-writable, so we'll detect -# if we try to write into it accidentally. -test.writable('repository.new', 0) -test.writable('repository.old', 0) + # Make both repositories non-writable, so we'll detect + # if we try to write into it accidentally. + test.writable('repository.new', 0) + test.writable('repository.old', 0) -test.run(chdir = 'work2', arguments = '.') + test.run(chdir = 'work2', arguments = '.') -test.run(program = work2_foo, stdout = """repository.old/aaa.c + test.run(program = work2_foo, stdout = """repository.old/aaa.c repository.old/bbb.c repository.old/foo.c """) -test.up_to_date(chdir = 'work2', arguments = '.') + test.up_to_date(chdir = 'work2', arguments = '.') -# -test.writable('repository.new', 1) + # + test.writable('repository.new', 1) -test.write(['repository.new', 'aaa.c'], r""" + test.write(['repository.new', 'aaa.c'], r""" #include <stdio.h> #include <stdlib.h> void @@ -254,7 +251,7 @@ aaa(void) } """) -test.write(['work2', 'bbb.c'], r""" + test.write(['work2', 'bbb.c'], r""" #include <stdio.h> #include <stdlib.h> void @@ -264,20 +261,20 @@ bbb(void) } """) -# -test.writable('repository.new', 0) + # + test.writable('repository.new', 0) -test.run(chdir = 'work2', arguments = '.') + test.run(chdir = 'work2', arguments = '.') -test.run(program = work2_foo, stdout = """repository.new/aaa.c + test.run(program = work2_foo, stdout = """repository.new/aaa.c work2/bbb.c repository.old/foo.c """) -test.up_to_date(chdir = 'work2', arguments = '.') + test.up_to_date(chdir = 'work2', arguments = '.') -# -test.write(['work2', 'aaa.c'], r""" + # + test.write(['work2', 'aaa.c'], r""" #include <stdio.h> #include <stdlib.h> void @@ -287,7 +284,7 @@ aaa(void) } """) -test.write(['work2', 'foo.c'], r""" + test.write(['work2', 'foo.c'], r""" #include <stdio.h> #include <stdlib.h> extern void aaa(void); @@ -303,59 +300,59 @@ main(int argc, char *argv[]) } """) -# -test.run(chdir = 'work2', arguments = '.') + # + test.run(chdir = 'work2', arguments = '.') -test.run(program = work2_foo, stdout = """work2/aaa.c + test.run(program = work2_foo, stdout = """work2/aaa.c work2/bbb.c work2/foo.c """) -test.up_to_date(chdir = 'work2', arguments = '.') + test.up_to_date(chdir = 'work2', arguments = '.') -# -test.unlink(['work2', 'aaa.c']) -test.unlink(['work2', 'bbb.c']) + # + test.unlink(['work2', 'aaa.c']) + test.unlink(['work2', 'bbb.c']) -# -test.run(chdir = 'work2', arguments = '.') + # + test.run(chdir = 'work2', arguments = '.') -test.run(program = work2_foo, stdout = """repository.new/aaa.c + test.run(program = work2_foo, stdout = """repository.new/aaa.c repository.old/bbb.c work2/foo.c """) -test.up_to_date(chdir = 'work2', arguments = '.') + test.up_to_date(chdir = 'work2', arguments = '.') -# -test.unlink(['work2', 'foo.c']) + # + test.unlink(['work2', 'foo.c']) -# -test.run(chdir = 'work2', arguments = '.') + # + test.run(chdir = 'work2', arguments = '.') -test.run(program = work2_foo, stdout = """repository.new/aaa.c + test.run(program = work2_foo, stdout = """repository.new/aaa.c repository.old/bbb.c repository.old/foo.c """) -test.up_to_date(chdir = 'work2', arguments = '.') + test.up_to_date(chdir = 'work2', arguments = '.') -# -test.writable('repository.new', 1) + # + test.writable('repository.new', 1) -test.unlink(['repository.new', 'aaa.c']) + test.unlink(['repository.new', 'aaa.c']) -test.writable('repository.new', 0) + test.writable('repository.new', 0) -# -test.run(chdir = 'work2', arguments = '.') + # + test.run(chdir = 'work2', arguments = '.') -test.run(program = work2_foo, stdout = """repository.old/aaa.c + test.run(program = work2_foo, stdout = """repository.old/aaa.c repository.old/bbb.c repository.old/foo.c """) -test.up_to_date(chdir = 'work2', arguments = '.') + test.up_to_date(chdir = 'work2', arguments = '.') # diff --git a/test/Repository/StaticLibrary.py b/test/Repository/StaticLibrary.py index 4f8160c..acd4b83 100644 --- a/test/Repository/StaticLibrary.py +++ b/test/Repository/StaticLibrary.py @@ -30,35 +30,37 @@ import TestSCons _obj = TestSCons._obj _exe = TestSCons._exe -test = TestSCons.TestSCons() - -# -test.subdir('repository', 'work1', 'work2', 'work3') - -# -workpath_repository = test.workpath('repository') -repository_aaa_obj = test.workpath('repository', 'aaa' + _obj) -repository_bbb_obj = test.workpath('repository', 'bbb' + _obj) -repository_foo_obj = test.workpath('repository', 'foo' + _obj) -repository_foo = test.workpath('repository', 'foo' + _exe) -work1_foo = test.workpath('work1', 'foo' + _exe) -work2_aaa_obj = test.workpath('work2', 'aaa' + _obj) -work2_foo_obj = test.workpath('work2', 'foo' + _obj) -work2_foo = test.workpath('work2', 'foo' + _exe) -work3_aaa_obj = test.workpath('work3', 'aaa' + _obj) -work3_bbb_obj = test.workpath('work3', 'bbb' + _obj) -work3_foo = test.workpath('work3', 'foo' + _exe) - -opts = '-Y ' + workpath_repository - -# -test.write(['repository', 'SConstruct'], """ -env = Environment(LIBS = ['xxx'], LIBPATH = '.') +for implicit_deps in ['0', '1', '2', 'all']: + test = TestSCons.TestSCons() + + # + test.subdir('repository', 'work1', 'work2', 'work3') + + # + workpath_repository = test.workpath('repository') + repository_aaa_obj = test.workpath('repository', 'aaa' + _obj) + repository_bbb_obj = test.workpath('repository', 'bbb' + _obj) + repository_foo_obj = test.workpath('repository', 'foo' + _obj) + repository_foo = test.workpath('repository', 'foo' + _exe) + work1_foo = test.workpath('work1', 'foo' + _exe) + work2_aaa_obj = test.workpath('work2', 'aaa' + _obj) + work2_foo_obj = test.workpath('work2', 'foo' + _obj) + work2_foo = test.workpath('work2', 'foo' + _exe) + work3_aaa_obj = test.workpath('work3', 'aaa' + _obj) + work3_bbb_obj = test.workpath('work3', 'bbb' + _obj) + work3_foo = test.workpath('work3', 'foo' + _exe) + + opts = '-Y ' + workpath_repository + + # + test.write(['repository', 'SConstruct'], """ +env = Environment(LIBS = ['xxx'], LIBPATH = '.', + IMPLICIT_COMMAND_DEPENDENCIES=%s) env.Library(target = 'xxx', source = ['aaa.c', 'bbb.c']) env.Program(target = 'foo', source = 'foo.c') -""") +""" % implicit_deps) -test.write(['repository', 'aaa.c'], r""" + test.write(['repository', 'aaa.c'], r""" #include <stdio.h> #include <stdlib.h> void @@ -68,7 +70,7 @@ aaa(void) } """) -test.write(['repository', 'bbb.c'], r""" + test.write(['repository', 'bbb.c'], r""" #include <stdio.h> #include <stdlib.h> void @@ -78,7 +80,7 @@ bbb(void) } """) -test.write(['repository', 'foo.c'], r""" + test.write(['repository', 'foo.c'], r""" #include <stdio.h> #include <stdlib.h> extern void aaa(void); @@ -94,29 +96,29 @@ main(int argc, char *argv[]) } """) -# Make the repository non-writable, -# so we'll detect if we try to write into it accidentally. -test.writable('repository', 0) + # Make the repository non-writable, + # so we'll detect if we try to write into it accidentally. + test.writable('repository', 0) -# -test.run(chdir = 'work1', options = opts, arguments = ".", - stderr=TestSCons.noisy_ar, - match=TestSCons.match_re_dotall) + # + test.run(chdir = 'work1', options = opts, arguments = ".", + stderr=TestSCons.noisy_ar, + match=TestSCons.match_re_dotall) -test.run(program = work1_foo, stdout = + test.run(program = work1_foo, stdout = """repository/aaa.c repository/bbb.c repository/foo.c """) -test.fail_test(os.path.exists(repository_aaa_obj)) -test.fail_test(os.path.exists(repository_bbb_obj)) -test.fail_test(os.path.exists(repository_foo_obj)) -test.fail_test(os.path.exists(repository_foo)) + test.fail_test(os.path.exists(repository_aaa_obj)) + test.fail_test(os.path.exists(repository_bbb_obj)) + test.fail_test(os.path.exists(repository_foo_obj)) + test.fail_test(os.path.exists(repository_foo)) -test.up_to_date(chdir = 'work1', options = opts, arguments = ".") + test.up_to_date(chdir = 'work1', options = opts, arguments = ".") -test.write(['work1', 'bbb.c'], r""" + test.write(['work1', 'bbb.c'], r""" #include <stdio.h> #include <stdlib.h> void @@ -126,49 +128,49 @@ bbb(void) } """) -test.run(chdir = 'work1', options = opts, arguments = ".", - stderr=TestSCons.noisy_ar, - match=TestSCons.match_re_dotall) + test.run(chdir = 'work1', options = opts, arguments = ".", + stderr=TestSCons.noisy_ar, + match=TestSCons.match_re_dotall) -test.run(program = work1_foo, stdout = + test.run(program = work1_foo, stdout = """repository/aaa.c work1/bbb.c repository/foo.c """) -test.fail_test(os.path.exists(repository_aaa_obj)) -test.fail_test(os.path.exists(repository_bbb_obj)) -test.fail_test(os.path.exists(repository_foo_obj)) -test.fail_test(os.path.exists(repository_foo)) + test.fail_test(os.path.exists(repository_aaa_obj)) + test.fail_test(os.path.exists(repository_bbb_obj)) + test.fail_test(os.path.exists(repository_foo_obj)) + test.fail_test(os.path.exists(repository_foo)) -test.up_to_date(chdir = 'work1', options = opts, arguments = ".") + test.up_to_date(chdir = 'work1', options = opts, arguments = ".") -# -test.writable('repository', 1) + # + test.writable('repository', 1) -test.run(chdir = 'repository', options = opts, arguments = ".", - stderr=TestSCons.noisy_ar, - match=TestSCons.match_re_dotall) + test.run(chdir = 'repository', options = opts, arguments = ".", + stderr=TestSCons.noisy_ar, + match=TestSCons.match_re_dotall) -test.run(program = repository_foo, stdout = + test.run(program = repository_foo, stdout = """repository/aaa.c repository/bbb.c repository/foo.c """) -test.fail_test(not os.path.exists(repository_aaa_obj)) -test.fail_test(not os.path.exists(repository_bbb_obj)) -test.fail_test(not os.path.exists(repository_foo_obj)) + test.fail_test(not os.path.exists(repository_aaa_obj)) + test.fail_test(not os.path.exists(repository_bbb_obj)) + test.fail_test(not os.path.exists(repository_foo_obj)) -test.up_to_date(chdir = 'repository', options = opts, arguments = ".") + test.up_to_date(chdir = 'repository', options = opts, arguments = ".") -# -test.writable('repository', 0) + # + test.writable('repository', 0) -# -test.up_to_date(chdir = 'work2', options = opts, arguments = ".") + # + test.up_to_date(chdir = 'work2', options = opts, arguments = ".") -test.write(['work2', 'bbb.c'], r""" + test.write(['work2', 'bbb.c'], r""" #include <stdio.h> #include <stdlib.h> void @@ -178,25 +180,25 @@ bbb(void) } """) -test.run(chdir = 'work2', options = opts, arguments = ".", - stderr=TestSCons.noisy_ar, - match=TestSCons.match_re_dotall) + test.run(chdir = 'work2', options = opts, arguments = ".", + stderr=TestSCons.noisy_ar, + match=TestSCons.match_re_dotall) -test.run(program = work2_foo, stdout = + test.run(program = work2_foo, stdout = """repository/aaa.c work2/bbb.c repository/foo.c """) -test.fail_test(os.path.exists(work2_aaa_obj)) -test.fail_test(os.path.exists(work2_foo_obj)) + test.fail_test(os.path.exists(work2_aaa_obj)) + test.fail_test(os.path.exists(work2_foo_obj)) -test.up_to_date(chdir = 'work2', options = opts, arguments = ".") + test.up_to_date(chdir = 'work2', options = opts, arguments = ".") -# -test.up_to_date(chdir = 'work3', options = opts, arguments = ".") + # + test.up_to_date(chdir = 'work3', options = opts, arguments = ".") -test.write(['work3', 'foo.c'], r""" + test.write(['work3', 'foo.c'], r""" #include <stdio.h> #include <stdlib.h> extern void aaa(void); @@ -212,18 +214,19 @@ main(int argc, char *argv[]) } """) -test.run(chdir = 'work3', options = opts, arguments = ".") + test.run(chdir = 'work3', options = opts, arguments = ".") -test.run(program = work3_foo, stdout = + test.run(program = work3_foo, stdout = """repository/aaa.c repository/bbb.c work3/foo.c """) -test.fail_test(os.path.exists(work3_aaa_obj)) -test.fail_test(os.path.exists(work3_bbb_obj)) + test.fail_test(os.path.exists(work3_aaa_obj)) + test.fail_test(os.path.exists(work3_bbb_obj)) + + test.up_to_date(chdir = 'work3', options = opts, arguments = ".") -test.up_to_date(chdir = 'work3', options = opts, arguments = ".") # test.pass_test() diff --git a/test/implicit/IMPLICIT_COMMAND_DEPENDENCIES.py b/test/implicit/IMPLICIT_COMMAND_DEPENDENCIES.py index 2431a61..bec2255 100644 --- a/test/implicit/IMPLICIT_COMMAND_DEPENDENCIES.py +++ b/test/implicit/IMPLICIT_COMMAND_DEPENDENCIES.py @@ -40,6 +40,8 @@ _python_ = TestSCons._python_ test = TestSCons.TestSCons() +test.write('file.in', "file.in\n") + generate_build_py_py_contents = """\ #!%(python)s import os @@ -61,17 +63,17 @@ os.chmod(sys.argv[1], 0o755) """ -extra = '' -test.write('generate_build_py.py', generate_build_py_py_contents % locals()) - +workpath = test.workpath() test.write('SConstruct', """ DefaultEnvironment(tools=[]) generate = Builder(action = r'%(_python_)s $GENERATE $TARGET') -build = Builder(action = r'$BUILD_PY $TARGET $SOURCES') +build = Builder(action = r'%(_python_)s $BUILD_PY $TARGET $SOURCES') +cd_and_build = Builder(action = r'cd %(workpath)s && %(_python_)s $BUILD_PY $TARGET $SOURCES') env = Environment(tools=[], BUILDERS = { 'GenerateBuild' : generate, 'BuildFile' : build, + 'CdAndBuildFile': cd_and_build, }, GENERATE = 'generate_build_py.py', BUILD_PY = 'build.py', @@ -80,6 +82,8 @@ env.PrependENVPath('PATH', '.') env.PrependENVPath('PATHEXT', '.PY') env0 = env.Clone(IMPLICIT_COMMAND_DEPENDENCIES = 0) env1 = env.Clone(IMPLICIT_COMMAND_DEPENDENCIES = 1) +env2 = env.Clone(IMPLICIT_COMMAND_DEPENDENCIES = 2) +envAll = env.Clone(IMPLICIT_COMMAND_DEPENDENCIES = 'all') envNone = env.Clone(IMPLICIT_COMMAND_DEPENDENCIES = None) envFalse = env.Clone(IMPLICIT_COMMAND_DEPENDENCIES = False) envTrue = env.Clone(IMPLICIT_COMMAND_DEPENDENCIES = True) @@ -90,44 +94,59 @@ AlwaysBuild(build_py) env.BuildFile('file.out', 'file.in') env0.BuildFile('file0.out', 'file.in') env1.BuildFile('file1.out', 'file.in') +env2.BuildFile('file2.out', 'file.in') +envAll.BuildFile('fileall.out', 'file.in') envNone.BuildFile('fileNone.out', 'file.in') envFalse.BuildFile('fileFalse.out', 'file.in') envTrue.BuildFile('fileTrue.out', 'file.in') envTrue.BuildFile('fileQuote.out', 'file.in', BUILD_PY='"build.py"') -""" % locals()) - +env1.CdAndBuildFile('cd_file1.out', 'file.in') +env2.CdAndBuildFile('cd_file2.out', 'file.in') +envAll.CdAndBuildFile('cd_fileall.out', 'file.in') +""" % locals()) -test.write('file.in', "file.in\n") -test.run(arguments = '--tree=all .') -expect_none = 'build.py %s file.in\nfile.in\n' +def run_test(extra, python, _python_): + # Write the generate_build_py.py file. This uses the contents of the + # variable "extra" while writing build.py. + test.write('generate_build_py.py', + generate_build_py_py_contents % locals()) -test.must_match('file.out', expect_none % 'file.out', mode='r') -test.must_match('file0.out', expect_none % 'file0.out', mode='r') -test.must_match('file1.out', expect_none % 'file1.out', mode='r') -test.must_match('fileNone.out', expect_none % 'fileNone.out', mode='r') -test.must_match('fileFalse.out', expect_none % 'fileFalse.out', mode='r') -test.must_match('fileTrue.out', expect_none % 'fileTrue.out', mode='r') -test.must_match('fileQuote.out', expect_none % 'fileQuote.out', mode='r') + # Run the SConscript file. + test.run(arguments = '--tree=all .') + # Generate some expected data of actions involving build.py. This expected + # data depends on the value of "extra". + build_none = 'build.py %s file.in\nfile.in\n' + build_extra = (build_none if not extra else + 'build.py %s file.in\n{}file.in\n'.format( + extra.replace('\\\\n', '\n'))) + build_extra_abs = '{} %s file.in\n{}file.in\n'.format( + test.workpath('build.py'), + extra.replace('\\\\n', '\n')) + empty_none = 'empty.py %s file.in\nfile.in\n' -extra = 'xyzzy\\\\n' -test.write('generate_build_py.py', generate_build_py_py_contents % locals()) + # Verify that the output matches what is expected. + test.must_match('file.out', build_none % 'file.out', mode='r') + test.must_match('file0.out', build_none % 'file0.out', mode='r') + test.must_match('file1.out', build_none % 'file1.out', mode='r') + test.must_match('file2.out', build_extra % 'file2.out', mode='r') + test.must_match('fileall.out', build_extra % 'fileall.out', mode='r') + test.must_match('fileNone.out', build_none % 'fileNone.out', mode='r') + test.must_match('fileFalse.out', build_none % 'fileFalse.out', mode='r') + test.must_match('fileTrue.out', build_none % 'fileTrue.out', mode='r') + test.must_match('fileQuote.out', build_none % 'fileQuote.out', mode='r') + test.must_match('cd_file1.out', build_none % 'cd_file1.out', mode='r') + test.must_match('cd_file2.out', build_extra % 'cd_file2.out', mode='r') + test.must_match('cd_fileall.out', build_extra % 'cd_fileall.out', mode='r') -test.run(arguments = '--tree=all .') -expect_extra = 'build.py %s file.in\nxyzzy\nfile.in\n' +run_test('', python, _python_) +run_test('xyzzy\\\\n', python, _python_) -test.must_match('file.out', expect_extra % 'file.out', mode='r') -test.must_match('file0.out', expect_none % 'file0.out', mode='r') -test.must_match('file1.out', expect_extra % 'file1.out', mode='r') -test.must_match('fileNone.out', expect_none % 'fileNone.out', mode='r') -test.must_match('fileFalse.out', expect_none % 'fileFalse.out', mode='r') -test.must_match('fileTrue.out', expect_extra % 'fileTrue.out', mode='r') -test.must_match('fileQuote.out', expect_extra % 'fileQuote.out', mode='r') test.pass_test() |