summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorWilliam Deegan <bill@baddogconsulting.com>2020-02-21 02:53:57 (GMT)
committerGitHub <noreply@github.com>2020-02-21 02:53:57 (GMT)
commit962736590a7367450460a594ef2cd3f594fe72ec (patch)
tree22fe52c634a5b1a483a9ad208b4e41b751a7392a
parent54c36335c575a98fcf6de2ff84c16b66efa7da5e (diff)
parent0814deb56ca870f631a415a3255ce8dd99688e01 (diff)
downloadSCons-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-xsrc/CHANGES.txt4
-rw-r--r--src/engine/SCons/Action.py81
-rw-r--r--src/engine/SCons/Action.xml39
-rw-r--r--test/Batch/action-changed.py16
-rw-r--r--test/Repository/Program.py195
-rw-r--r--test/Repository/StaticLibrary.py165
-rw-r--r--test/implicit/IMPLICIT_COMMAND_DEPENDENCIES.py73
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()