diff options
author | William Deegan <bill@baddogconsulting.com> | 2017-05-08 19:43:25 (GMT) |
---|---|---|
committer | William Deegan <bill@baddogconsulting.com> | 2017-05-08 19:43:25 (GMT) |
commit | 1976be7b15db2b89bf7c6361ea28806824c9c399 (patch) | |
tree | 0c8c5591ff3c06456615358417a9688dc6231699 | |
parent | 8c13f3194dc2e59c0322021630032441793b6d94 (diff) | |
parent | 6b86f7db0794b07ad5023fa96b041590a89a8d77 (diff) | |
download | SCons-1976be7b15db2b89bf7c6361ea28806824c9c399.zip SCons-1976be7b15db2b89bf7c6361ea28806824c9c399.tar.gz SCons-1976be7b15db2b89bf7c6361ea28806824c9c399.tar.bz2 |
merge
64 files changed, 1064 insertions, 1016 deletions
diff --git a/QMTest/TestCmd.py b/QMTest/TestCmd.py index 2c0c0d3..20f1f14 100644 --- a/QMTest/TestCmd.py +++ b/QMTest/TestCmd.py @@ -453,13 +453,17 @@ def pass_test(self = None, condition = 1, function = None): sys.exit(0) -def match_exact(lines = None, matches = None): +def match_exact(lines = None, matches = None, newline = '\n'): """ """ + + if isinstance(lines, bytes) or bytes is str: + newline = to_bytes('\n') + if not is_List(lines): - lines = lines.split("\n") + lines = lines.split(newline) if not is_List(matches): - matches = matches.split("\n") + matches = matches.split(newline) if len(lines) != len(matches): return for i in range(len(lines)): @@ -737,16 +741,6 @@ class Popen(subprocess.Popen): setattr(self, which, None) - def to_bytes (s): - if isinstance (s, bytes) or bytes is str: - return s - return bytes (s, 'utf-8') - - def to_str (s): - if bytes is str or is_String(s): - return s - return str (s, 'utf-8') - if sys.platform == 'win32':# and subprocess.mswindows: def send(self, input): input = to_bytes(input) @@ -993,7 +987,7 @@ class TestCmd(object): condition = self.condition if self._preserve[condition]: for dir in self._dirlist: - print("Preserved directory " + dir + "\n") + print(u"Preserved directory " + dir + "\n") else: list = self._dirlist[:] list.reverse() diff --git a/QMTest/TestSCons.py b/QMTest/TestSCons.py index b6366a6..9c89249 100644 --- a/QMTest/TestSCons.py +++ b/QMTest/TestSCons.py @@ -598,23 +598,33 @@ class TestSCons(TestCommon): return s + @staticmethod + def to_bytes_re_sub(pattern, repl, str, count=0, flags=0): + """ + Wrapper around re.sub to change pattern and repl to bytes to work with + both python 2 & 3 + """ + pattern = to_bytes(pattern) + repl = to_bytes(repl) + return re.sub(pattern, repl, str, count, flags) + def normalize_pdf(self, s): - s = re.sub(r'/(Creation|Mod)Date \(D:[^)]*\)', - r'/\1Date (D:XXXX)', s) - s = re.sub(r'/ID \[<[0-9a-fA-F]*> <[0-9a-fA-F]*>\]', - r'/ID [<XXXX> <XXXX>]', s) - s = re.sub(r'/(BaseFont|FontName) /[A-Z]{6}', - r'/\1 /XXXXXX', s) - s = re.sub(r'/Length \d+ *\n/Filter /FlateDecode\n', - r'/Length XXXX\n/Filter /FlateDecode\n', s) + s = self.to_bytes_re_sub(r'/(Creation|Mod)Date \(D:[^)]*\)', + r'/\1Date (D:XXXX)', s) + s = self.to_bytes_re_sub(r'/ID \[<[0-9a-fA-F]*> <[0-9a-fA-F]*>\]', + r'/ID [<XXXX> <XXXX>]', s) + s = self.to_bytes_re_sub(r'/(BaseFont|FontName) /[A-Z]{6}', + r'/\1 /XXXXXX', s) + s = self.to_bytes_re_sub(r'/Length \d+ *\n/Filter /FlateDecode\n', + r'/Length XXXX\n/Filter /FlateDecode\n', s) try: import zlib except ImportError: pass else: - begin_marker = '/FlateDecode\n>>\nstream\n' - end_marker = 'endstream\nendobj' + begin_marker = to_bytes('/FlateDecode\n>>\nstream\n') + end_marker = to_bytes('endstream\nendobj') encoded = [] b = s.find(begin_marker, 0) @@ -629,16 +639,16 @@ class TestSCons(TestCommon): for b, e in encoded: r.append(s[x:b]) d = zlib.decompress(s[b:e]) - d = re.sub(r'%%CreationDate: [^\n]*\n', - r'%%CreationDate: 1970 Jan 01 00:00:00\n', d) - d = re.sub(r'%DVIPSSource: TeX output \d\d\d\d\.\d\d\.\d\d:\d\d\d\d', - r'%DVIPSSource: TeX output 1970.01.01:0000', d) - d = re.sub(r'/(BaseFont|FontName) /[A-Z]{6}', - r'/\1 /XXXXXX', d) + d = self.to_bytes_re_sub(r'%%CreationDate: [^\n]*\n', + r'%%CreationDate: 1970 Jan 01 00:00:00\n', d) + d = self.to_bytes_re_sub(r'%DVIPSSource: TeX output \d\d\d\d\.\d\d\.\d\d:\d\d\d\d', + r'%DVIPSSource: TeX output 1970.01.01:0000', d) + d = self.to_bytes_re_sub(r'/(BaseFont|FontName) /[A-Z]{6}', + r'/\1 /XXXXXX', d) r.append(d) x = e r.append(s[x:]) - s = ''.join(r) + s = to_bytes('').join(r) return s @@ -1236,7 +1246,10 @@ try: import distutils.sysconfig exec_prefix = distutils.sysconfig.EXEC_PREFIX print(distutils.sysconfig.get_python_inc()) - print(os.path.join(exec_prefix, 'libs')) + lib_path = os.path.join(exec_prefix, 'libs') + if not os.path.exists(lib_path): + lib_path = os.path.join(exec_prefix, 'lib') + print(lib_path) except: print(os.path.join(sys.prefix, 'include', py_ver)) print(os.path.join(sys.prefix, 'lib', py_ver, 'config')) @@ -162,6 +162,15 @@ import distutils.command no_winpack_templates = not os.path.exists(os.path.join(os.path.split(distutils.command.__file__)[0],'wininst-9.0.exe')) skip_win_packages = ARGUMENTS.get('SKIP_WIN_PACKAGES',False) or no_winpack_templates + +if sys.version_info[0] > 2: + # TODO: Resolve this issue. Currently fails when run on windows with + # File "/opt/local/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/distutils/command/bdist_wininst.py", line 262, in create_exe + # cfgdata = cfgdata.encode("mbcs") + # LookupError: unknown encoding: mbcs + print("Temporary PY3: Skipping windows package builds") + skip_win_packages = True + if skip_win_packages: print("Skipping the build of Windows packages...") @@ -327,17 +336,20 @@ try: def zipit(env, target, source): print("Zipping %s:" % str(target[0])) - def visit(arg, dirname, names): - for name in names: - path = os.path.join(dirname, name) + def visit(arg, dirname, filenames): + for filename in filenames: + path = os.path.join(dirname, filename) if os.path.isfile(path): arg.write(path) # default ZipFile compression is ZIP_STORED zf = zipfile.ZipFile(str(target[0]), 'w', compression=zipfile.ZIP_DEFLATED) olddir = os.getcwd() os.chdir(env['CD']) - try: os.path.walk(env['PSV'], visit, zf) - finally: os.chdir(olddir) + try: + for dirname, dirnames, filenames in os.walk(env['PSV']): + visit(zf, dirname, filenames) + finally: + os.chdir(olddir) zf.close() def unzipit(env, target, source): @@ -364,6 +376,7 @@ except ImportError: zipit = "cd $CD && $ZIP $ZIPFLAGS $( ${TARGET.abspath} $) $PSV" unzipit = "$UNZIP $UNZIPFLAGS $SOURCES" + def SCons_revision(target, source, env): """Interpolate specific values from the environment into a file. @@ -372,7 +385,7 @@ def SCons_revision(target, source, env): """ t = str(target[0]) s = source[0].rstr() - with open(s, 'rb') as fp: + with open(s, 'r') as fp: contents = fp.read() # Note: We construct the __*__ substitution strings here # so that they don't get replaced when this file gets @@ -387,9 +400,10 @@ def SCons_revision(target, source, env): contents = contents.replace('__REVISION' + '__', env['REVISION']) contents = contents.replace('__VERSION' + '__', env['VERSION']) contents = contents.replace('__NULL' + '__', '') - open(t, 'wb').write(contents) + open(t, 'w').write(contents) os.chmod(t, os.stat(s)[0]) + revaction = SCons_revision revbuilder = Builder(action = Action(SCons_revision, varlist=['COPYRIGHT', 'VERSION'])) @@ -819,6 +833,7 @@ for p in [ scons ]: s = p['filemap'].get(b, b) if not s[0] == '$' and not os.path.isabs(s): s = os.path.join(src, s) + builder = p['buildermap'].get(b, env.SCons_revision) x = builder(os.path.join(build, b), s) Local(x) @@ -835,7 +850,7 @@ for p in [ scons ]: def write_src_files(target, source, **kw): global src_files src_files.sort() - f = open(str(target[0]), 'wb') + f = open(str(target[0]), 'w') for file in src_files: f.write(file + "\n") f.close() @@ -1017,10 +1032,10 @@ for p in [ scons ]: list generated from our MANIFEST(s), so we don't have to maintain multiple lists. """ - c = open(str(source[0]), 'rb').read() + c = open(str(source[0]), 'r').read() c = c.replace('__VERSION' + '__', env['VERSION']) c = c.replace('__RPM_FILES' + '__', env['RPM_FILES']) - open(str(target[0]), 'wb').write(c) + open(str(target[0]), 'w').write(c) rpm_files.sort() rpm_files_str = "\n".join(rpm_files) + "\n" @@ -1391,3 +1406,4 @@ for pf, help_text in packaging_flavors: os.path.join(build_dir, 'runtest.py'), ]) + @@ -686,6 +686,7 @@ if testlistfile: tests = [x for x in tests if x[0] != '#'] tests = [x[:-1] for x in tests] tests = [x.strip() for x in tests] + tests = [x for x in tests if len(x) > 0] else: testpaths = [] diff --git a/src/CHANGES.txt b/src/CHANGES.txt index cb6ab21..9e6151a 100644 --- a/src/CHANGES.txt +++ b/src/CHANGES.txt @@ -13,6 +13,16 @@ RELEASE VERSION/DATE TO BE FILLED IN LATER RELEASE VERSION/DATE TO BE FILLED IN LATER + From Daniel Moody: + - Updated the QMTest/TestCmd.py so when using the + preserve option, it will be able to print unicode directories. + - Also updated test/scons-time/run/option/verbose.py that was + not taking into account the output when PRESERVE was enabled + + From Gaurav Juvekar: + - Fix issue #2910: Make --tree=all handle Unicode. (PR #427) + - Fix issue #2788: Fix typo in documentation example for sconf. (PR #388) + From Manish Vachharajani: - Update debian rules, compat, and control to not use features deprecated or obsolete in later versions of debhelpers @@ -36,6 +46,11 @@ RELEASE VERSION/DATE TO BE FILLED IN LATER From William Deegan: - Removed deprecated tools CVS, Perforce, BitKeeper, RCS, SCCS, Subversion. + - Removed deprecated module SCons.Sig + - Added prioritized list of xsltproc tools to docbook. The order will now be as + follows: xsltproc, saxon, saxon-xslt, xalan (with first being highest priority, first + tool found is used) + - Fixed MSVSProject example code (http://scons.tigris.org/issues/show_bug.cgi?id=2979) From Daniel Moody: - Fixed msvs.py for Visual Studio generated projects which were diff --git a/src/engine/MANIFEST.in b/src/engine/MANIFEST.in index af91b7c..32f4965 100644 --- a/src/engine/MANIFEST.in +++ b/src/engine/MANIFEST.in @@ -47,7 +47,6 @@ SCons/Script/Interactive.py SCons/Script/Main.py SCons/Script/SConscript.py SCons/Script/SConsOptions.py -SCons/Sig.py SCons/Subst.py SCons/Taskmaster.py SCons/Tool/__init__.py @@ -170,7 +169,7 @@ SCons/Variables/PackageVariable.py SCons/Variables/PathVariable.py SCons/Warnings.py SCons/Tool/GettextCommon.py -SCons/Tool/gettext.py +SCons/Tool/gettext_tool.py SCons/Tool/msgfmt.py SCons/Tool/msginit.py SCons/Tool/msgmerge.py diff --git a/src/engine/SCons/Defaults.py b/src/engine/SCons/Defaults.py index f1d5bca..69d5c94 100644 --- a/src/engine/SCons/Defaults.py +++ b/src/engine/SCons/Defaults.py @@ -264,7 +264,10 @@ def copy_func(dest, src, symlinks=True): shutil.copy2(src, dest) return 0 else: - return shutil.copytree(src, dest, symlinks) + shutil.copytree(src, dest, symlinks) + # copytree returns None in python2 and destination string in python3 + # A error is raised in both cases, so we can just return 0 for success + return 0 Copy = ActionFactory( copy_func, @@ -485,6 +488,7 @@ def processDefines(defs): l = [str(defs)] return l + def _defines(prefix, defs, suffix, env, c=_concat_ixes): """A wrapper around _concat_ixes that turns a list or string into a list of C preprocessor command-line definitions. @@ -492,6 +496,7 @@ def _defines(prefix, defs, suffix, env, c=_concat_ixes): return c(prefix, env.subst_path(processDefines(defs)), suffix, env) + class NullCmdGenerator(object): """This is a callable class that can be used in place of other command generators if you don't want them to do anything. @@ -510,6 +515,7 @@ class NullCmdGenerator(object): def __call__(self, target, source, env, for_signature=None): return self.cmd + class Variable_Method_Caller(object): """A class for finding a construction variable on the stack and calling one of its methods. diff --git a/src/engine/SCons/Node/FS.py b/src/engine/SCons/Node/FS.py index 33e4a2d..64b2e3f 100644 --- a/src/engine/SCons/Node/FS.py +++ b/src/engine/SCons/Node/FS.py @@ -351,33 +351,6 @@ class _Null(object): _null = _Null() -DefaultSCCSBuilder = None -DefaultRCSBuilder = None - -def get_DefaultSCCSBuilder(): - global DefaultSCCSBuilder - if DefaultSCCSBuilder is None: - import SCons.Builder - # "env" will get filled in by Executor.get_build_env() - # calling SCons.Defaults.DefaultEnvironment() when necessary. - act = SCons.Action.Action('$SCCSCOM', '$SCCSCOMSTR') - DefaultSCCSBuilder = SCons.Builder.Builder(action = act, - env = None, - name = "DefaultSCCSBuilder") - return DefaultSCCSBuilder - -def get_DefaultRCSBuilder(): - global DefaultRCSBuilder - if DefaultRCSBuilder is None: - import SCons.Builder - # "env" will get filled in by Executor.get_build_env() - # calling SCons.Defaults.DefaultEnvironment() when necessary. - act = SCons.Action.Action('$RCS_COCOM', '$RCS_COCOMSTR') - DefaultRCSBuilder = SCons.Builder.Builder(action = act, - env = None, - name = "DefaultRCSBuilder") - return DefaultRCSBuilder - # Cygwin's os.path.normcase pretends it's on a case-sensitive filesystem. _is_cygwin = sys.platform == "cygwin" if os.path.normcase("TeSt") == os.path.normpath("TeSt") and not _is_cygwin: @@ -422,46 +395,12 @@ def do_diskcheck_match(node, predicate, errorfmt): def ignore_diskcheck_match(node, predicate, errorfmt): pass -def do_diskcheck_rcs(node, name): - try: - rcs_dir = node.rcs_dir - except AttributeError: - if node.entry_exists_on_disk('RCS'): - rcs_dir = node.Dir('RCS') - else: - rcs_dir = None - node.rcs_dir = rcs_dir - if rcs_dir: - return rcs_dir.entry_exists_on_disk(name+',v') - return None - -def ignore_diskcheck_rcs(node, name): - return None - -def do_diskcheck_sccs(node, name): - try: - sccs_dir = node.sccs_dir - except AttributeError: - if node.entry_exists_on_disk('SCCS'): - sccs_dir = node.Dir('SCCS') - else: - sccs_dir = None - node.sccs_dir = sccs_dir - if sccs_dir: - return sccs_dir.entry_exists_on_disk('s.'+name) - return None -def ignore_diskcheck_sccs(node, name): - return None diskcheck_match = DiskChecker('match', do_diskcheck_match, ignore_diskcheck_match) -diskcheck_rcs = DiskChecker('rcs', do_diskcheck_rcs, ignore_diskcheck_rcs) -diskcheck_sccs = DiskChecker('sccs', do_diskcheck_sccs, ignore_diskcheck_sccs) diskcheckers = [ diskcheck_match, - diskcheck_rcs, - diskcheck_sccs, ] def set_diskcheck(list): @@ -477,6 +416,11 @@ class EntryProxy(SCons.Util.Proxy): __str__ = SCons.Util.Delegate('__str__') + # In PY3 if a class defines __eq__, then it must explicitly provide + # __hash__. Since SCons.Util.Proxy provides __eq__ we need the following + # see: https://docs.python.org/3.1/reference/datamodel.html#object.__hash__ + __hash__ = SCons.Util.Delegate('__hash__') + def __get_abspath(self): entry = self.get() return SCons.Subst.SpecialAttrWrapper(entry.get_abspath(), @@ -574,6 +518,7 @@ class EntryProxy(SCons.Util.Proxy): else: return attr_function(self) + class Base(SCons.Node.Node): """A generic class for file system entries. This class is for when we don't know yet whether the entry being looked up is a file @@ -976,8 +921,6 @@ class Entry(Base): 'root', 'dirname', 'on_disk_entries', - 'sccs_dir', - 'rcs_dir', 'released_target_info', 'contentsig'] @@ -1523,8 +1466,6 @@ class Dir(Base): 'root', 'dirname', 'on_disk_entries', - 'sccs_dir', - 'rcs_dir', 'released_target_info', 'contentsig'] @@ -2090,9 +2031,7 @@ class Dir(Base): return node def file_on_disk(self, name): - if self.entry_exists_on_disk(name) or \ - diskcheck_rcs(self, name) or \ - diskcheck_sccs(self, name): + if self.entry_exists_on_disk(name): try: return self.File(name) except TypeError: pass node = self.srcdir_duplicate(name) @@ -2589,8 +2528,6 @@ class File(Base): 'root', 'dirname', 'on_disk_entries', - 'sccs_dir', - 'rcs_dir', 'released_target_info', 'contentsig'] @@ -3031,12 +2968,7 @@ class File(Base): return None scb = self.dir.src_builder() if scb is _null: - if diskcheck_sccs(self.dir, self.name): - scb = get_DefaultSCCSBuilder() - elif diskcheck_rcs(self.dir, self.name): - scb = get_DefaultRCSBuilder() - else: - scb = None + scb = None if scb is not None: try: b = self.builder diff --git a/src/engine/SCons/Node/FSTests.py b/src/engine/SCons/Node/FSTests.py index 7366b7b..306f1a2 100644 --- a/src/engine/SCons/Node/FSTests.py +++ b/src/engine/SCons/Node/FSTests.py @@ -3269,18 +3269,11 @@ class has_src_builderTestCase(unittest.TestCase): fs = SCons.Node.FS.FS(test.workpath('')) os.chdir(test.workpath('')) test.subdir('sub1') - test.subdir('sub2', ['sub2', 'SCCS'], ['sub2', 'RCS']) sub1 = fs.Dir('sub1', '.') f1 = fs.File('f1', sub1) f2 = fs.File('f2', sub1) f3 = fs.File('f3', sub1) - sub2 = fs.Dir('sub2', '.') - f4 = fs.File('f4', sub2) - f5 = fs.File('f5', sub2) - f6 = fs.File('f6', sub2) - f7 = fs.File('f7', sub2) - f8 = fs.File('f8', sub2) h = f1.has_src_builder() assert not h, h @@ -3305,32 +3298,6 @@ class has_src_builderTestCase(unittest.TestCase): assert h, h assert f3.builder is b1, f3.builder - f7.set_src_builder(b1) - f8.builder_set(b1) - - test.write(['sub2', 'SCCS', 's.f5'], "sub2/SCCS/s.f5\n") - test.write(['sub2', 'RCS', 'f6,v'], "sub2/RCS/f6,v\n") - h = f4.has_src_builder() - assert not h, h - h = f4.has_builder() - assert not h, h - h = f5.has_src_builder() - assert h, h - h = f5.has_builder() - assert h, h - h = f6.has_src_builder() - assert h, h - h = f6.has_builder() - assert h, h - h = f7.has_src_builder() - assert h, h - h = f7.has_builder() - assert h, h - h = f8.has_src_builder() - assert not h, h - h = f8.has_builder() - assert h, h - class prepareTestCase(unittest.TestCase): def runTest(self): """Test the prepare() method""" diff --git a/src/engine/SCons/Node/Python.py b/src/engine/SCons/Node/Python.py index f151fc5..8c47c97 100644 --- a/src/engine/SCons/Node/Python.py +++ b/src/engine/SCons/Node/Python.py @@ -58,7 +58,7 @@ class ValueNodeInfo(SCons.Node.NodeInfoBase): del state['__weakref__'] except KeyError: pass - + return state def __setstate__(self, state): @@ -77,7 +77,7 @@ class ValueBuildInfo(SCons.Node.BuildInfoBase): current_version_id = 2 class Value(SCons.Node.Node): - """A class for Python variables, typically passed on the command line + """A class for Python variables, typically passed on the command line or generated by a script, but not from a file or some other source. """ @@ -108,7 +108,7 @@ class Value(SCons.Node.Node): is_up_to_date = SCons.Node.Node.children_are_up_to_date def is_under(self, dir): - # Make Value nodes get built regardless of + # Make Value nodes get built regardless of # what directory scons was run from. Value nodes # are outside the filesystem: return 1 @@ -133,10 +133,17 @@ class Value(SCons.Node.Node): ###TODO: something reasonable about universal newlines contents = str(self.value) for kid in self.children(None): - contents = contents + kid.get_contents() + contents = contents + kid.get_contents().decode() return contents - get_contents = get_text_contents ###TODO should return 'bytes' value + def get_contents(self): + text_contents = self.get_text_contents() + try: + return text_contents.encode() + except UnicodeDecodeError: + # Already encoded as python2 str are bytes + return text_contents + def changed_since_last_build(self, target, prev_ni): cur_csig = self.get_csig() diff --git a/src/engine/SCons/Node/PythonTests.py b/src/engine/SCons/Node/PythonTests.py index e2e36bf..346542b 100644 --- a/src/engine/SCons/Node/PythonTests.py +++ b/src/engine/SCons/Node/PythonTests.py @@ -90,15 +90,15 @@ class ValueTestCase(unittest.TestCase): """ v1 = SCons.Node.Python.Value('aaa') csig = v1.get_csig(None) - assert csig == 'aaa', csig + assert csig.decode() == 'aaa', csig v2 = SCons.Node.Python.Value(7) csig = v2.get_csig(None) - assert csig == '7', csig + assert csig.decode() == '7', csig v3 = SCons.Node.Python.Value(None) csig = v3.get_csig(None) - assert csig == 'None', csig + assert csig.decode() == 'None', csig class ValueNodeInfoTestCase(unittest.TestCase): def test___init__(self): diff --git a/src/engine/SCons/Platform/darwin.py b/src/engine/SCons/Platform/darwin.py index 1cf4aeb..c85b75b 100644 --- a/src/engine/SCons/Platform/darwin.py +++ b/src/engine/SCons/Platform/darwin.py @@ -63,6 +63,10 @@ def generate(env): env.AppendENVPath('PATHOSX', line.strip('\n')) f.close() + # Not sure why this wasn't the case all along? + if env['ENV'].get('PATHOSX', False) and os.environ.get('SCONS_USE_MAC_PATHS', False): + env.AppendENVPath('PATH',env['ENV']['PATHOSX']) + # Local Variables: # tab-width:4 # indent-tabs-mode:nil diff --git a/src/engine/SCons/SConf.py b/src/engine/SCons/SConf.py index 31d9402..889af4a 100644 --- a/src/engine/SCons/SConf.py +++ b/src/engine/SCons/SConf.py @@ -110,7 +110,7 @@ def _createConfigH(target, source, env): #define %(DEFNAME)s_SEEN """ % {'DEFNAME' : defname}) - t.write(source[0].get_contents()) + t.write(source[0].get_contents().decode()) t.write(""" #endif /* %(DEFNAME)s_SEEN */ """ % {'DEFNAME' : defname}) @@ -164,11 +164,11 @@ class ConfigureCacheError(SConfError): # define actions for building text files def _createSource( target, source, env ): fd = open(str(target[0]), "w") - fd.write(source[0].get_contents()) + fd.write(source[0].get_contents().decode()) fd.close() def _stringSource( target, source, env ): return (str(target[0]) + ' <-\n |' + - source[0].get_contents().replace( '\n', "\n |" ) ) + source[0].get_contents().decode().replace( '\n', "\n |" ) ) class SConfBuildInfo(SCons.Node.FS.FileBuildInfo): """ @@ -609,7 +609,7 @@ class SConfBase(object): ok = self.TryBuild(self.env.SConfActionBuilder, text, extension) del self.env['BUILDERS']['SConfActionBuilder'] if ok: - outputStr = self.lastTarget.get_contents() + outputStr = self.lastTarget.get_contents().decode() return (1, outputStr) return (0, "") diff --git a/src/engine/SCons/SConfTests.py b/src/engine/SCons/SConfTests.py index b2aa3b4..f4c6f89 100644 --- a/src/engine/SCons/SConfTests.py +++ b/src/engine/SCons/SConfTests.py @@ -48,7 +48,7 @@ class SConfTestCase(unittest.TestCase): def setUp(self): # we always want to start with a clean directory self.save_cwd = os.getcwd() - self.test = TestCmd.TestCmd(workdir = '') + self.test = TestCmd.TestCmd(workdir = '') os.chdir(self.test.workpath('')) def tearDown(self): @@ -102,14 +102,17 @@ class SConfTestCase(unittest.TestCase): import SCons.Platform.win32 - file = SCons.Platform.win32._builtin_file - open = SCons.Platform.win32._builtin_open + try: + file = SCons.Platform.win32._builtin_file + open = SCons.Platform.win32._builtin_open + except AttributeError: + pass def _baseTryXXX(self, TryFunc): # TryCompile and TryLink are much the same, so we can test them # in one method, we pass the function as a string ('TryCompile', # 'TryLink'), so we are aware of reloading modules. - + def checks(self, sconf, TryFuncString): TryFunc = self.SConf.SConfBase.__dict__[TryFuncString] res1 = TryFunc( sconf, "int main() { return 0; }\n", ".c" ) @@ -128,7 +131,7 @@ class SConfTestCase(unittest.TestCase): assert res[0] and not res[1], res finally: sconf.Finish() - + # 2.1 test the error caching mechanism (no dependencies have changed) self._resetSConfState() sconf = self.SConf.SConf(self.scons_env, @@ -139,9 +142,9 @@ class SConfTestCase(unittest.TestCase): assert res[0] and not res[1], res finally: sconf.Finish() - # we should have exactly one one error cached + # we should have exactly one one error cached log = str(self.test.read( self.test.workpath('config.log') )) - expr = re.compile( ".*failed in a previous run and all", re.DOTALL ) + expr = re.compile( ".*failed in a previous run and all", re.DOTALL ) firstOcc = expr.match( log ) assert firstOcc is not None, log secondOcc = expr.match( log, firstOcc.end(0) ) @@ -239,11 +242,11 @@ class SConfTestCase(unittest.TestCase): """Test SConf.TryCompile """ self._baseTryXXX( "TryCompile" ) #self.SConf.SConf.TryCompile ) - + def test_TryLink(self): """Test SConf.TryLink """ - self._baseTryXXX( "TryLink" ) #self.SConf.SConf.TryLink ) + self._baseTryXXX( "TryLink" ) #self.SConf.SConf.TryLink ) def test_TryRun(self): """Test SConf.TryRun @@ -256,10 +259,10 @@ int main() { return 0; } """ - res1 = sconf.TryRun( prog, ".c" ) + res1 = sconf.TryRun( prog, ".c" ) res2 = sconf.TryRun( "not a c program\n", ".c" ) return (res1, res2) - + self._resetSConfState() sconf = self.SConf.SConf(self.scons_env, conf_dir=self.test.workpath('config.tests'), @@ -307,7 +310,7 @@ int main() { log_file=self.test.workpath('config.log')) try: (ret, output) = sconf.TryAction(action=actionOK) - assert ret and output == bytearray("RUN OK"+os.linesep,'utf-8'), (ret, output) + assert ret and output.encode('utf-8') == bytearray("RUN OK"+os.linesep,'utf-8'), (ret, output) (ret, output) = sconf.TryAction(action=actionFAIL) assert not ret and output == "", (ret, output) finally: @@ -354,7 +357,7 @@ int main() { try: self._test_check_compilers('CC', sconf.CheckCC, 'CheckCC') except AssertionError: - sys.stderr.write(self.test.read('config.log')) + sys.stderr.write(self.test.read('config.log', mode='r')) raise finally: sconf.Finish() @@ -370,7 +373,7 @@ int main() { try: self._test_check_compilers('SHCC', sconf.CheckSHCC, 'CheckSHCC') except AssertionError: - sys.stderr.write(self.test.read('config.log')) + sys.stderr.write(self.test.read('config.log', mode='r')) raise finally: sconf.Finish() @@ -386,7 +389,7 @@ int main() { try: self._test_check_compilers('CXX', sconf.CheckCXX, 'CheckCXX') except AssertionError: - sys.stderr.write(self.test.read('config.log')) + sys.stderr.write(self.test.read('config.log', mode='r')) raise finally: sconf.Finish() @@ -402,7 +405,7 @@ int main() { try: self._test_check_compilers('SHCXX', sconf.CheckSHCXX, 'CheckSHCXX') except AssertionError: - sys.stderr.write(self.test.read('config.log')) + sys.stderr.write(self.test.read('config.log', mode='r')) raise finally: sconf.Finish() @@ -627,8 +630,8 @@ int main() { else: r = sconf.CheckProg('cmd.exe') self.assertIn('cmd.exe',r) - - + + r = sconf.CheckProg('hopefully-not-a-program') assert r is None @@ -717,7 +720,7 @@ int main() { # In ANSI C, malloc should be available in stdlib r = sconf.CheckDeclaration('malloc', includes = "#include <stdlib.h>") assert r, "malloc not declared ??" - # For C++, __cplusplus should be declared + # For C++, __cplusplus should be declared r = sconf.CheckDeclaration('__cplusplus', language = 'C++') assert r, "__cplusplus not declared in C++ ??" r = sconf.CheckDeclaration('__cplusplus', language = 'C') @@ -761,7 +764,7 @@ int main() { test.Result( ret ) assert ret and output == "Hello", (ret, output) return ret - + self._resetSConfState() sconf = self.SConf.SConf(self.scons_env, @@ -773,7 +776,7 @@ int main() { assert ret, ret finally: sconf.Finish() - + if __name__ == "__main__": suite = unittest.makeSuite(SConfTestCase, 'test_') diff --git a/src/engine/SCons/Scanner/LaTeX.py b/src/engine/SCons/Scanner/LaTeX.py index 0c54bf3..e5abb0c 100644 --- a/src/engine/SCons/Scanner/LaTeX.py +++ b/src/engine/SCons/Scanner/LaTeX.py @@ -37,7 +37,9 @@ import SCons.Util # list of graphics file extensions for TeX and LaTeX TexGraphics = ['.eps', '.ps'] -LatexGraphics = ['.pdf', '.png', '.jpg', '.gif', '.tif'] +#LatexGraphics = ['.pdf', '.png', '.jpg', '.gif', '.tif'] +LatexGraphics = [ '.png', '.jpg', '.gif', '.tif'] + # Used as a return value of modify_env_var if the variable is not set. class _Null(object): @@ -397,6 +399,7 @@ class LaTeX(SCons.Scanner.Base): include = queue.pop() inc_type, inc_subdir, inc_filename = include + try: if seen[inc_filename] == 1: continue diff --git a/src/engine/SCons/Script/Main.py b/src/engine/SCons/Script/Main.py index f9df813..c810634 100644 --- a/src/engine/SCons/Script/Main.py +++ b/src/engine/SCons/Script/Main.py @@ -47,6 +47,7 @@ import os import sys import time import traceback +import sysconfig import SCons.CacheDir import SCons.Debug @@ -1251,16 +1252,19 @@ def _build_targets(fs, options, targets, target_top): # various print_* settings, tree_printer list, etc. BuildTask.options = options + + python_has_threads = sysconfig.get_config_var('WITH_THREAD') + # to check if python configured with threads. global num_jobs num_jobs = options.num_jobs jobs = SCons.Job.Jobs(num_jobs, taskmaster) if num_jobs > 1: msg = None - if jobs.num_jobs == 1: + if sys.platform == 'win32': + msg = fetch_win32_parallel_msg() + elif jobs.num_jobs == 1 or not python_has_threads: msg = "parallel builds are unsupported by this version of Python;\n" + \ "\tignoring -j or num_jobs option.\n" - elif sys.platform == 'win32': - msg = fetch_win32_parallel_msg() if msg: SCons.Warnings.warn(SCons.Warnings.NoParallelSupportWarning, msg) diff --git a/src/engine/SCons/Script/SConscript.py b/src/engine/SCons/Script/SConscript.py index 8609892..558e28f 100644 --- a/src/engine/SCons/Script/SConscript.py +++ b/src/engine/SCons/Script/SConscript.py @@ -179,10 +179,10 @@ def _SConscript(fs, *files, **kw): fs.chdir(top, change_os_dir=1) if f.rexists(): actual = f.rfile() - _file_ = open(actual.get_abspath(), "r") + _file_ = open(actual.get_abspath(), "rb") elif f.srcnode().rexists(): actual = f.srcnode().rfile() - _file_ = open(actual.get_abspath(), "r") + _file_ = open(actual.get_abspath(), "rb") elif f.has_src_builder(): # The SConscript file apparently exists in a source # code management system. Build it, but then clear @@ -192,7 +192,7 @@ def _SConscript(fs, *files, **kw): f.built() f.builder_set(None) if f.exists(): - _file_ = open(f.get_abspath(), "r") + _file_ = open(f.get_abspath(), "rb") if _file_: # Chdir to the SConscript directory. Use a path # name relative to the SConstruct file so that if @@ -248,6 +248,7 @@ def _SConscript(fs, *files, **kw): pass try: try: +# _file_ = SCons.Util.to_str(_file_) exec(compile(_file_.read(), _file_.name, 'exec'), call_stack[-1].globals) except SConscriptReturn: diff --git a/src/engine/SCons/Sig.py b/src/engine/SCons/Sig.py deleted file mode 100644 index 35fffc1..0000000 --- a/src/engine/SCons/Sig.py +++ /dev/null @@ -1,63 +0,0 @@ -# -# __COPYRIGHT__ -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" - -__doc__ = """Place-holder for the old SCons.Sig module hierarchy - -This is no longer used, but code out there (such as the NSIS module on -the SCons wiki) may try to import SCons.Sig. If so, we generate a warning -that points them to the line that caused the import, and don't die. - -If someone actually tried to use the sub-modules or functions within -the package (for example, SCons.Sig.MD5.signature()), then they'll still -get an AttributeError, but at least they'll know where to start looking. -""" - -import SCons.Util -import SCons.Warnings - -msg = 'The SCons.Sig module no longer exists.\n' \ - ' Remove the following "import SCons.Sig" line to eliminate this warning:' - -SCons.Warnings.warn(SCons.Warnings.DeprecatedSigModuleWarning, msg) - -default_calc = None -default_module = None - -class MD5Null(SCons.Util.Null): - def __repr__(self): - return "MD5Null()" - -class TimeStampNull(SCons.Util.Null): - def __repr__(self): - return "TimeStampNull()" - -MD5 = MD5Null() -TimeStamp = TimeStampNull() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/src/engine/SCons/Taskmaster.py b/src/engine/SCons/Taskmaster.py index ec7b9f2..9bd9597 100644 --- a/src/engine/SCons/Taskmaster.py +++ b/src/engine/SCons/Taskmaster.py @@ -546,9 +546,17 @@ class Task(object): if sys.version_info[0] == 2: exec("raise exc_type, exc_value, exc_traceback") else: # sys.version_info[0] == 3: - exec("raise exc_type(exc_value).with_traceback(exc_traceback)") + if isinstance(exc_value, Exception): #hasattr(exc_value, 'with_traceback'): + # If exc_value is an exception, then just reraise + exec("raise exc_value.with_traceback(exc_traceback)") + else: + # else we'll create an exception using the value and raise that + exec("raise exc_type(exc_value).with_traceback(exc_traceback)") + # raise e.__class__, e.__class__(e), sys.exc_info()[2] + # exec("raise exc_type(exc_value).with_traceback(exc_traceback)") + class AlwaysTask(Task): diff --git a/src/engine/SCons/Tool/GettextCommon.py b/src/engine/SCons/Tool/GettextCommon.py index e23a2d7..3b840a6 100644 --- a/src/engine/SCons/Tool/GettextCommon.py +++ b/src/engine/SCons/Tool/GettextCommon.py @@ -29,15 +29,32 @@ __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" import SCons.Warnings import re + ############################################################################# class XgettextToolWarning(SCons.Warnings.Warning): pass + + class XgettextNotFound(XgettextToolWarning): pass + + class MsginitToolWarning(SCons.Warnings.Warning): pass + + class MsginitNotFound(MsginitToolWarning): pass + + class MsgmergeToolWarning(SCons.Warnings.Warning): pass + + class MsgmergeNotFound(MsgmergeToolWarning): pass + + class MsgfmtToolWarning(SCons.Warnings.Warning): pass + + class MsgfmtNotFound(MsgfmtToolWarning): pass + + ############################################################################# SCons.Warnings.enableWarningClass(XgettextToolWarning) SCons.Warnings.enableWarningClass(XgettextNotFound) @@ -47,367 +64,406 @@ SCons.Warnings.enableWarningClass(MsgmergeToolWarning) SCons.Warnings.enableWarningClass(MsgmergeNotFound) SCons.Warnings.enableWarningClass(MsgfmtToolWarning) SCons.Warnings.enableWarningClass(MsgfmtNotFound) + + ############################################################################# ############################################################################# class _POTargetFactory(object): - """ A factory of `PO` target files. - - Factory defaults differ from these of `SCons.Node.FS.FS`. We set `precious` - (this is required by builders and actions gettext) and `noclean` flags by - default for all produced nodes. - """ - def __init__( self, env, nodefault = True, alias = None, precious = True - , noclean = True ): - """ Object constructor. - - **Arguments** - - - *env* (`SCons.Environment.Environment`) - - *nodefault* (`boolean`) - if `True`, produced nodes will be ignored - from default target `'.'` - - *alias* (`string`) - if provided, produced nodes will be automatically - added to this alias, and alias will be set as `AlwaysBuild` - - *precious* (`boolean`) - if `True`, the produced nodes will be set as - `Precious`. - - *noclen* (`boolean`) - if `True`, the produced nodes will be excluded - from `Clean`. + """ A factory of `PO` target files. + + Factory defaults differ from these of `SCons.Node.FS.FS`. We set `precious` + (this is required by builders and actions gettext) and `noclean` flags by + default for all produced nodes. """ - self.env = env - self.alias = alias - self.precious = precious - self.noclean = noclean - self.nodefault = nodefault - - def _create_node(self, name, factory, directory = None, create = 1): - """ Create node, and set it up to factory settings. """ - import SCons.Util - node = factory(name, directory, create) - node.set_noclean(self.noclean) - node.set_precious(self.precious) - if self.nodefault: - self.env.Ignore('.', node) - if self.alias: - self.env.AlwaysBuild(self.env.Alias(self.alias, node)) - return node - - def Entry(self, name, directory = None, create = 1): - """ Create `SCons.Node.FS.Entry` """ - return self._create_node(name, self.env.fs.Entry, directory, create) - - def File(self, name, directory = None, create = 1): - """ Create `SCons.Node.FS.File` """ - return self._create_node(name, self.env.fs.File, directory, create) + + def __init__(self, env, nodefault=True, alias=None, precious=True + , noclean=True): + """ Object constructor. + + **Arguments** + + - *env* (`SCons.Environment.Environment`) + - *nodefault* (`boolean`) - if `True`, produced nodes will be ignored + from default target `'.'` + - *alias* (`string`) - if provided, produced nodes will be automatically + added to this alias, and alias will be set as `AlwaysBuild` + - *precious* (`boolean`) - if `True`, the produced nodes will be set as + `Precious`. + - *noclen* (`boolean`) - if `True`, the produced nodes will be excluded + from `Clean`. + """ + self.env = env + self.alias = alias + self.precious = precious + self.noclean = noclean + self.nodefault = nodefault + + def _create_node(self, name, factory, directory=None, create=1): + """ Create node, and set it up to factory settings. """ + import SCons.Util + node = factory(name, directory, create) + node.set_noclean(self.noclean) + node.set_precious(self.precious) + if self.nodefault: + self.env.Ignore('.', node) + if self.alias: + self.env.AlwaysBuild(self.env.Alias(self.alias, node)) + return node + + def Entry(self, name, directory=None, create=1): + """ Create `SCons.Node.FS.Entry` """ + return self._create_node(name, self.env.fs.Entry, directory, create) + + def File(self, name, directory=None, create=1): + """ Create `SCons.Node.FS.File` """ + return self._create_node(name, self.env.fs.File, directory, create) + + ############################################################################# ############################################################################# _re_comment = re.compile(r'(#[^\n\r]+)$', re.M) _re_lang = re.compile(r'([a-zA-Z0-9_]+)', re.M) + + ############################################################################# -def _read_linguas_from_files(env, linguas_files = None): - """ Parse `LINGUAS` file and return list of extracted languages """ - import SCons.Util - import SCons.Environment - global _re_comment - global _re_lang - if not SCons.Util.is_List(linguas_files) \ - and not SCons.Util.is_String(linguas_files) \ - and not isinstance(linguas_files, SCons.Node.FS.Base) \ - and linguas_files: - # If, linguas_files==True or such, then read 'LINGUAS' file. - linguas_files = [ 'LINGUAS' ] - if linguas_files is None: - return [] - fnodes = env.arg2nodes(linguas_files) - linguas = [] - for fnode in fnodes: - contents = _re_comment.sub("", fnode.get_text_contents()) - ls = [ l for l in _re_lang.findall(contents) if l ] - linguas.extend(ls) - return linguas +def _read_linguas_from_files(env, linguas_files=None): + """ Parse `LINGUAS` file and return list of extracted languages """ + import SCons.Util + import SCons.Environment + global _re_comment + global _re_lang + if not SCons.Util.is_List(linguas_files) \ + and not SCons.Util.is_String(linguas_files) \ + and not isinstance(linguas_files, SCons.Node.FS.Base) \ + and linguas_files: + # If, linguas_files==True or such, then read 'LINGUAS' file. + linguas_files = ['LINGUAS'] + if linguas_files is None: + return [] + fnodes = env.arg2nodes(linguas_files) + linguas = [] + for fnode in fnodes: + contents = _re_comment.sub("", fnode.get_text_contents()) + ls = [l for l in _re_lang.findall(contents) if l] + linguas.extend(ls) + return linguas + + ############################################################################# ############################################################################# from SCons.Builder import BuilderBase + + ############################################################################# class _POFileBuilder(BuilderBase): - """ `PO` file builder. - - This is multi-target single-source builder. In typical situation the source - is single `POT` file, e.g. `messages.pot`, and there are multiple `PO` - targets to be updated from this `POT`. We must run - `SCons.Builder.BuilderBase._execute()` separatelly for each target to track - dependencies separatelly for each target file. + """ `PO` file builder. - **NOTE**: if we call `SCons.Builder.BuilderBase._execute(.., target, ...)` - with target being list of all targets, all targets would be rebuilt each time - one of the targets from this list is missing. This would happen, for example, - when new language `ll` enters `LINGUAS_FILE` (at this moment there is no - `ll.po` file yet). To avoid this, we override - `SCons.Builder.BuilerBase._execute()` and call it separatelly for each - target. Here we also append to the target list the languages read from - `LINGUAS_FILE`. - """ - # - #* The argument for overriding _execute(): We must use environment with - # builder overrides applied (see BuilderBase.__init__(). Here it comes for - # free. - #* The argument against using 'emitter': The emitter is called too late - # by BuilderBase._execute(). If user calls, for example: - # - # env.POUpdate(LINGUAS_FILE = 'LINGUAS') - # - # the builder throws error, because it is called with target=None, - # source=None and is trying to "generate" sources or target list first. - # If user calls - # - # env.POUpdate(['foo', 'baz'], LINGUAS_FILE = 'LINGUAS') - # - # the env.BuilderWrapper() calls our builder with target=None, - # source=['foo', 'baz']. The BuilderBase._execute() then splits execution - # and execute iterativelly (recursion) self._execute(None, source[i]). - # After that it calls emitter (which is quite too late). The emitter is - # also called in each iteration, what makes things yet worse. - def __init__(self, env, **kw): - if not 'suffix' in kw: - kw['suffix'] = '$POSUFFIX' - if not 'src_suffix' in kw: - kw['src_suffix'] = '$POTSUFFIX' - if not 'src_builder' in kw: - kw['src_builder'] = '_POTUpdateBuilder' - if not 'single_source' in kw: - kw['single_source'] = True - alias = None - if 'target_alias' in kw: - alias = kw['target_alias'] - del kw['target_alias'] - if not 'target_factory' in kw: - kw['target_factory'] = _POTargetFactory(env, alias=alias).File - BuilderBase.__init__(self, **kw) - - def _execute(self, env, target, source, *args, **kw): - """ Execute builder's actions. + This is multi-target single-source builder. In typical situation the source + is single `POT` file, e.g. `messages.pot`, and there are multiple `PO` + targets to be updated from this `POT`. We must run + `SCons.Builder.BuilderBase._execute()` separatelly for each target to track + dependencies separatelly for each target file. - Here we append to `target` the languages read from `$LINGUAS_FILE` and - apply `SCons.Builder.BuilderBase._execute()` separatelly to each target. - The arguments and return value are same as for - `SCons.Builder.BuilderBase._execute()`. + **NOTE**: if we call `SCons.Builder.BuilderBase._execute(.., target, ...)` + with target being list of all targets, all targets would be rebuilt each time + one of the targets from this list is missing. This would happen, for example, + when new language `ll` enters `LINGUAS_FILE` (at this moment there is no + `ll.po` file yet). To avoid this, we override + `SCons.Builder.BuilerBase._execute()` and call it separatelly for each + target. Here we also append to the target list the languages read from + `LINGUAS_FILE`. """ - import SCons.Util - import SCons.Node - linguas_files = None - if 'LINGUAS_FILE' in env and env['LINGUAS_FILE']: - linguas_files = env['LINGUAS_FILE'] - # This prevents endless recursion loop (we'll be invoked once for - # each target appended here, we must not extend the list again). - env['LINGUAS_FILE'] = None - linguas = _read_linguas_from_files(env,linguas_files) - if SCons.Util.is_List(target): - target.extend(linguas) - elif target is not None: - target = [target] + linguas - else: - target = linguas - if not target: - # Let the SCons.BuilderBase to handle this patologic situation - return BuilderBase._execute( self, env, target, source, *args, **kw) - # The rest is ours - if not SCons.Util.is_List(target): - target = [ target ] - result = [] - for tgt in target: - r = BuilderBase._execute( self, env, [tgt], source, *args, **kw) - result.extend(r) - if linguas_files is not None: - env['LINGUAS_FILE'] = linguas_files - return SCons.Node.NodeList(result) + + # + # * The argument for overriding _execute(): We must use environment with + # builder overrides applied (see BuilderBase.__init__(). Here it comes for + # free. + # * The argument against using 'emitter': The emitter is called too late + # by BuilderBase._execute(). If user calls, for example: + # + # env.POUpdate(LINGUAS_FILE = 'LINGUAS') + # + # the builder throws error, because it is called with target=None, + # source=None and is trying to "generate" sources or target list first. + # If user calls + # + # env.POUpdate(['foo', 'baz'], LINGUAS_FILE = 'LINGUAS') + # + # the env.BuilderWrapper() calls our builder with target=None, + # source=['foo', 'baz']. The BuilderBase._execute() then splits execution + # and execute iterativelly (recursion) self._execute(None, source[i]). + # After that it calls emitter (which is quite too late). The emitter is + # also called in each iteration, what makes things yet worse. + def __init__(self, env, **kw): + if not 'suffix' in kw: + kw['suffix'] = '$POSUFFIX' + if not 'src_suffix' in kw: + kw['src_suffix'] = '$POTSUFFIX' + if not 'src_builder' in kw: + kw['src_builder'] = '_POTUpdateBuilder' + if not 'single_source' in kw: + kw['single_source'] = True + alias = None + if 'target_alias' in kw: + alias = kw['target_alias'] + del kw['target_alias'] + if not 'target_factory' in kw: + kw['target_factory'] = _POTargetFactory(env, alias=alias).File + BuilderBase.__init__(self, **kw) + + def _execute(self, env, target, source, *args, **kw): + """ Execute builder's actions. + + Here we append to `target` the languages read from `$LINGUAS_FILE` and + apply `SCons.Builder.BuilderBase._execute()` separatelly to each target. + The arguments and return value are same as for + `SCons.Builder.BuilderBase._execute()`. + """ + import SCons.Util + import SCons.Node + linguas_files = None + if 'LINGUAS_FILE' in env and env['LINGUAS_FILE']: + linguas_files = env['LINGUAS_FILE'] + # This prevents endless recursion loop (we'll be invoked once for + # each target appended here, we must not extend the list again). + env['LINGUAS_FILE'] = None + linguas = _read_linguas_from_files(env, linguas_files) + if SCons.Util.is_List(target): + target.extend(linguas) + elif target is not None: + target = [target] + linguas + else: + target = linguas + if not target: + # Let the SCons.BuilderBase to handle this patologic situation + return BuilderBase._execute(self, env, target, source, *args, **kw) + # The rest is ours + if not SCons.Util.is_List(target): + target = [target] + result = [] + for tgt in target: + r = BuilderBase._execute(self, env, [tgt], source, *args, **kw) + result.extend(r) + if linguas_files is not None: + env['LINGUAS_FILE'] = linguas_files + return SCons.Node.NodeList(result) + + ############################################################################# import SCons.Environment + + ############################################################################# def _translate(env, target=None, source=SCons.Environment._null, *args, **kw): - """ Function for `Translate()` pseudo-builder """ - if target is None: target = [] - pot = env.POTUpdate(None, source, *args, **kw) - po = env.POUpdate(target, pot, *args, **kw) - return po + """ Function for `Translate()` pseudo-builder """ + if target is None: target = [] + pot = env.POTUpdate(None, source, *args, **kw) + po = env.POUpdate(target, pot, *args, **kw) + return po + + ############################################################################# ############################################################################# class RPaths(object): - """ Callable object, which returns pathnames relative to SCons current - working directory. - - It seems like `SCons.Node.FS.Base.get_path()` returns absolute paths - for nodes that are outside of current working directory (`env.fs.getcwd()`). - Here, we often have `SConscript`, `POT` and `PO` files within `po/` - directory and source files (e.g. `*.c`) outside of it. When generating `POT` - template file, references to source files are written to `POT` template, so - a translator may later quickly jump to appropriate source file and line from - its `PO` editor (e.g. `poedit`). Relative paths in `PO` file are usually - interpreted by `PO` editor as paths relative to the place, where `PO` file - lives. The absolute paths would make resultant `POT` file nonportable, as - the references would be correct only on the machine, where `POT` file was - recently re-created. For such reason, we need a function, which always - returns relative paths. This is the purpose of `RPaths` callable object. - - The `__call__` method returns paths relative to current working directory, but - we assume, that *xgettext(1)* is run from the directory, where target file is - going to be created. - - Note, that this may not work for files distributed over several hosts or - across different drives on windows. We assume here, that single local - filesystem holds both source files and target `POT` templates. - - Intended use of `RPaths` - in `xgettext.py`:: - - def generate(env): - from GettextCommon import RPaths - ... - sources = '$( ${_concat( "", SOURCES, "", __env__, XgettextRPaths, TARGET, SOURCES)} $)' - env.Append( - ... - XGETTEXTCOM = 'XGETTEXT ... ' + sources, - ... - XgettextRPaths = RPaths(env) - ) - """ - # NOTE: This callable object returns pathnames of dirs/files relative to - # current working directory. The pathname remains relative also for entries - # that are outside of current working directory (node, that - # SCons.Node.FS.File and siblings return absolute path in such case). For - # simplicity we compute path relative to current working directory, this - # seems be enough for our purposes (don't need TARGET variable and - # SCons.Defaults.Variable_Caller stuff). + """ Callable object, which returns pathnames relative to SCons current + working directory. - def __init__(self, env): - """ Initialize `RPaths` callable object. - - **Arguments**: - - - *env* - a `SCons.Environment.Environment` object, defines *current - working dir*. + It seems like `SCons.Node.FS.Base.get_path()` returns absolute paths + for nodes that are outside of current working directory (`env.fs.getcwd()`). + Here, we often have `SConscript`, `POT` and `PO` files within `po/` + directory and source files (e.g. `*.c`) outside of it. When generating `POT` + template file, references to source files are written to `POT` template, so + a translator may later quickly jump to appropriate source file and line from + its `PO` editor (e.g. `poedit`). Relative paths in `PO` file are usually + interpreted by `PO` editor as paths relative to the place, where `PO` file + lives. The absolute paths would make resultant `POT` file nonportable, as + the references would be correct only on the machine, where `POT` file was + recently re-created. For such reason, we need a function, which always + returns relative paths. This is the purpose of `RPaths` callable object. + + The `__call__` method returns paths relative to current working directory, but + we assume, that *xgettext(1)* is run from the directory, where target file is + going to be created. + + Note, that this may not work for files distributed over several hosts or + across different drives on windows. We assume here, that single local + filesystem holds both source files and target `POT` templates. + + Intended use of `RPaths` - in `xgettext.py`:: + + def generate(env): + from GettextCommon import RPaths + ... + sources = '$( ${_concat( "", SOURCES, "", __env__, XgettextRPaths, TARGET, SOURCES)} $)' + env.Append( + ... + XGETTEXTCOM = 'XGETTEXT ... ' + sources, + ... + XgettextRPaths = RPaths(env) + ) """ - self.env = env - # FIXME: I'm not sure, how it should be implemented (what the *args are in - # general, what is **kw). - def __call__(self, nodes, *args, **kw): - """ Return nodes' paths (strings) relative to current working directory. - - **Arguments**: + # NOTE: This callable object returns pathnames of dirs/files relative to + # current working directory. The pathname remains relative also for entries + # that are outside of current working directory (node, that + # SCons.Node.FS.File and siblings return absolute path in such case). For + # simplicity we compute path relative to current working directory, this + # seems be enough for our purposes (don't need TARGET variable and + # SCons.Defaults.Variable_Caller stuff). - - *nodes* ([`SCons.Node.FS.Base`]) - list of nodes. - - *args* - currently unused. - - *kw* - currently unused. + def __init__(self, env): + """ Initialize `RPaths` callable object. + + **Arguments**: + + - *env* - a `SCons.Environment.Environment` object, defines *current + working dir*. + """ + self.env = env + + # FIXME: I'm not sure, how it should be implemented (what the *args are in + # general, what is **kw). + def __call__(self, nodes, *args, **kw): + """ Return nodes' paths (strings) relative to current working directory. + + **Arguments**: + + - *nodes* ([`SCons.Node.FS.Base`]) - list of nodes. + - *args* - currently unused. + - *kw* - currently unused. + + **Returns**: + + - Tuple of strings, which represent paths relative to current working + directory (for given environment). + """ + import os + import SCons.Node.FS + rpaths = () + cwd = self.env.fs.getcwd().get_abspath() + for node in nodes: + rpath = None + if isinstance(node, SCons.Node.FS.Base): + rpath = os.path.relpath(node.get_abspath(), cwd) + # FIXME: Other types possible here? + if rpath is not None: + rpaths += (rpath,) + return rpaths - **Returns**: - - Tuple of strings, which represent paths relative to current working - directory (for given environment). - """ - import os - import SCons.Node.FS - rpaths = () - cwd = self.env.fs.getcwd().get_abspath() - for node in nodes: - rpath = None - if isinstance(node, SCons.Node.FS.Base): - rpath = os.path.relpath(node.get_abspath(), cwd) - # FIXME: Other types possible here? - if rpath is not None: - rpaths += (rpath,) - return rpaths ############################################################################# - + ############################################################################# def _init_po_files(target, source, env): - """ Action function for `POInit` builder. """ - nop = lambda target, source, env : 0 - if 'POAUTOINIT' in env: - autoinit = env['POAUTOINIT'] - else: - autoinit = False - # Well, if everything outside works well, this loop should do single - # iteration. Otherwise we are rebuilding all the targets even, if just - # one has changed (but is this our fault?). - for tgt in target: - if not tgt.exists(): - if autoinit: - action = SCons.Action.Action('$MSGINITCOM', '$MSGINITCOMSTR') - else: - msg = 'File ' + repr(str(tgt)) + ' does not exist. ' \ - + 'If you are a translator, you can create it through: \n' \ - + '$MSGINITCOM' - action = SCons.Action.Action(nop, msg) - status = action([tgt], source, env) - if status: return status - return 0 + """ Action function for `POInit` builder. """ + nop = lambda target, source, env: 0 + if 'POAUTOINIT' in env: + autoinit = env['POAUTOINIT'] + else: + autoinit = False + # Well, if everything outside works well, this loop should do single + # iteration. Otherwise we are rebuilding all the targets even, if just + # one has changed (but is this our fault?). + for tgt in target: + if not tgt.exists(): + if autoinit: + action = SCons.Action.Action('$MSGINITCOM', '$MSGINITCOMSTR') + else: + msg = 'File ' + repr(str(tgt)) + ' does not exist. ' \ + + 'If you are a translator, you can create it through: \n' \ + + '$MSGINITCOM' + action = SCons.Action.Action(nop, msg) + status = action([tgt], source, env) + if status: return status + return 0 + + ############################################################################# ############################################################################# def _detect_xgettext(env): - """ Detects *xgettext(1)* binary """ - if 'XGETTEXT' in env: - return env['XGETTEXT'] - xgettext = env.Detect('xgettext'); - if xgettext: - return xgettext - raise SCons.Errors.StopError(XgettextNotFound,"Could not detect xgettext") - return None + """ Detects *xgettext(1)* binary """ + if 'XGETTEXT' in env: + return env['XGETTEXT'] + xgettext = env.Detect('xgettext'); + if xgettext: + return xgettext + raise SCons.Errors.StopError(XgettextNotFound, "Could not detect xgettext") + return None + + ############################################################################# def _xgettext_exists(env): - return _detect_xgettext(env) + return _detect_xgettext(env) + + ############################################################################# ############################################################################# def _detect_msginit(env): - """ Detects *msginit(1)* program. """ - if 'MSGINIT' in env: - return env['MSGINIT'] - msginit = env.Detect('msginit'); - if msginit: - return msginit - raise SCons.Errors.StopError(MsginitNotFound, "Could not detect msginit") - return None + """ Detects *msginit(1)* program. """ + if 'MSGINIT' in env: + return env['MSGINIT'] + msginit = env.Detect('msginit'); + if msginit: + return msginit + raise SCons.Errors.StopError(MsginitNotFound, "Could not detect msginit") + return None + + ############################################################################# def _msginit_exists(env): - return _detect_msginit(env) + return _detect_msginit(env) + + ############################################################################# ############################################################################# def _detect_msgmerge(env): - """ Detects *msgmerge(1)* program. """ - if 'MSGMERGE' in env: - return env['MSGMERGE'] - msgmerge = env.Detect('msgmerge'); - if msgmerge: - return msgmerge - raise SCons.Errors.StopError(MsgmergeNotFound, "Could not detect msgmerge") - return None + """ Detects *msgmerge(1)* program. """ + if 'MSGMERGE' in env: + return env['MSGMERGE'] + msgmerge = env.Detect('msgmerge'); + if msgmerge: + return msgmerge + raise SCons.Errors.StopError(MsgmergeNotFound, "Could not detect msgmerge") + return None + + ############################################################################# def _msgmerge_exists(env): - return _detect_msgmerge(env) + return _detect_msgmerge(env) + + ############################################################################# ############################################################################# def _detect_msgfmt(env): - """ Detects *msgmfmt(1)* program. """ - if 'MSGFMT' in env: - return env['MSGFMT'] - msgfmt = env.Detect('msgfmt'); - if msgfmt: - return msgfmt - raise SCons.Errors.StopError(MsgfmtNotFound, "Could not detect msgfmt") - return None + """ Detects *msgmfmt(1)* program. """ + if 'MSGFMT' in env: + return env['MSGFMT'] + msgfmt = env.Detect('msgfmt'); + if msgfmt: + return msgfmt + raise SCons.Errors.StopError(MsgfmtNotFound, "Could not detect msgfmt") + return None + + ############################################################################# def _msgfmt_exists(env): - return _detect_msgfmt(env) + return _detect_msgfmt(env) + + ############################################################################# ############################################################################# def tool_list(platform, env): - """ List tools that shall be generated by top-level `gettext` tool """ - return [ 'xgettext', 'msginit', 'msgmerge', 'msgfmt' ] -############################################################################# + """ List tools that shall be generated by top-level `gettext` tool """ + return ['xgettext', 'msginit', 'msgmerge', 'msgfmt'] +############################################################################# diff --git a/src/engine/SCons/Tool/__init__.py b/src/engine/SCons/Tool/__init__.py index 99e0770..61b7788 100644 --- a/src/engine/SCons/Tool/__init__.py +++ b/src/engine/SCons/Tool/__init__.py @@ -761,6 +761,7 @@ def LibSymlinksStrFun(target, source, env, *args): LibSymlinksAction = SCons.Action.Action(LibSymlinksActionFunction, LibSymlinksStrFun) + def createSharedLibBuilder(env): """This is a utility function that creates the SharedLibrary Builder in an Environment if it is not there already. diff --git a/src/engine/SCons/Tool/applelink.py b/src/engine/SCons/Tool/applelink.py index ba955a4..c5f4376 100644 --- a/src/engine/SCons/Tool/applelink.py +++ b/src/engine/SCons/Tool/applelink.py @@ -51,6 +51,14 @@ def generate(env): env['SHLINKFLAGS'] = SCons.Util.CLVar('$LINKFLAGS -dynamiclib') env['SHLINKCOM'] = env['SHLINKCOM'] + ' $_FRAMEWORKPATH $_FRAMEWORKS $FRAMEWORKSFLAGS' + + # TODO: Work needed to generate versioned shared libraries + # Leaving this commented out, and also going to disable versioned library checking for now + # see: http://docstore.mik.ua/orelly/unix3/mac/ch05_04.htm for proper naming + #link._setup_versioned_lib_variables(env, tool = 'applelink')#, use_soname = use_soname) + #env['LINKCALLBACKS'] = link._versioned_lib_callbacks() + + # override the default for loadable modules, which are different # on OS X than dynamic shared libs. echoing what XCode does for # pre/suffixes: diff --git a/src/engine/SCons/Tool/docbook/__init__.py b/src/engine/SCons/Tool/docbook/__init__.py index 8a7b2e7..d3ea8ae 100644 --- a/src/engine/SCons/Tool/docbook/__init__.py +++ b/src/engine/SCons/Tool/docbook/__init__.py @@ -157,6 +157,11 @@ def __create_output_dir(base_dir): # # Supported command line tools and their call "signature" # +xsltproc_com_priority = ['xsltproc', 'saxon', 'saxon-xslt', 'xalan'] + +# TODO: Set minimum version of saxon-xslt to be 8.x (lower than this only supports xslt 1.0. +# see: http://saxon.sourceforge.net/saxon6.5.5/ +# see: http://saxon.sourceforge.net/ xsltproc_com = {'xsltproc' : '$DOCBOOK_XSLTPROC $DOCBOOK_XSLTPROCFLAGS -o $TARGET $DOCBOOK_XSL $SOURCE', 'saxon' : '$DOCBOOK_XSLTPROC $DOCBOOK_XSLTPROCFLAGS -o $TARGET $DOCBOOK_XSL $SOURCE $DOCBOOK_XSLTPROCPARAMS', 'saxon-xslt' : '$DOCBOOK_XSLTPROC $DOCBOOK_XSLTPROCFLAGS -o $TARGET $DOCBOOK_XSL $SOURCE $DOCBOOK_XSLTPROCPARAMS', @@ -166,19 +171,23 @@ fop_com = {'fop' : '$DOCBOOK_FOP $DOCBOOK_FOPFLAGS -fo $SOURCE -pdf $TARGET', 'xep' : '$DOCBOOK_FOP $DOCBOOK_FOPFLAGS -valid -fo $SOURCE -pdf $TARGET', 'jw' : '$DOCBOOK_FOP $DOCBOOK_FOPFLAGS -f docbook -b pdf $SOURCE -o $TARGET'} -def __detect_cl_tool(env, chainkey, cdict): +def __detect_cl_tool(env, chainkey, cdict, cpriority=None): """ Helper function, picks a command line tool from the list and initializes its environment variables. """ if env.get(chainkey,'') == '': clpath = '' - for cltool in cdict: + + if cpriority is None: + cpriority = cdict.keys() + for cltool in cpriority: clpath = env.WhereIs(cltool) if clpath: env[chainkey] = clpath if not env[chainkey + 'COM']: env[chainkey + 'COM'] = cdict[cltool] + break def _detect(env): """ @@ -192,7 +201,7 @@ def _detect(env): if ((not has_libxml2 and not has_lxml) or (prefer_xsltproc)): # Try to find the XSLT processors - __detect_cl_tool(env, 'DOCBOOK_XSLTPROC', xsltproc_com) + __detect_cl_tool(env, 'DOCBOOK_XSLTPROC', xsltproc_com, xsltproc_com_priority) __detect_cl_tool(env, 'DOCBOOK_XMLLINT', xmllint_com) __detect_cl_tool(env, 'DOCBOOK_FOP', fop_com) diff --git a/src/engine/SCons/Tool/link.py b/src/engine/SCons/Tool/link.py index b7db947..07e9250 100644 --- a/src/engine/SCons/Tool/link.py +++ b/src/engine/SCons/Tool/link.py @@ -243,8 +243,10 @@ def _versioned_lib_callbacks(): 'VersionedLdModSoname' : _versioned_ldmod_soname, }.copy() -# Setup all variables required by the versioning machinery def _setup_versioned_lib_variables(env, **kw): + """ + Setup all variables required by the versioning machinery + """ tool = None try: tool = kw['tool'] diff --git a/src/engine/SCons/Tool/msvs.xml b/src/engine/SCons/Tool/msvs.xml index e85b27c..c4701e1 100644 --- a/src/engine/SCons/Tool/msvs.xml +++ b/src/engine/SCons/Tool/msvs.xml @@ -127,22 +127,24 @@ compilation error messages displayed in the Visual Studio console output window. This can be remedied by adding the Visual C/C++ <literal>/FC</literal> compiler option to the &cv-link-CCFLAGS; variable so that the compiler will print the full path name of any files that cause compilation errors. </para> -<para> Example usage: </para> <example_commands>barsrcs = ['bar.cpp'], -barincs = ['bar.h'], +<para> Example usage: </para> + <example_commands> +barsrcs = ['bar.cpp'] +barincs = ['bar.h'] barlocalincs = ['StdAfx.h'] barresources = ['bar.rc','resource.h'] barmisc = ['bar_readme.txt'] dll = env.SharedLibrary(target = 'bar.dll', source = barsrcs) - +buildtarget = [s for s in dll if str(s).endswith('dll')] env.MSVSProject(target = 'Bar' + env['MSVSPROJECTSUFFIX'], srcs = barsrcs, incs = barincs, localincs = barlocalincs, resources = barresources, misc = barmisc, - buildtarget = dll, + buildtarget = buildtarget, variant = 'Release') </example_commands> <para>Starting with version 2.4 of diff --git a/src/engine/SCons/Tool/textfile.py b/src/engine/SCons/Tool/textfile.py index 0e4d943..42a79cd 100644 --- a/src/engine/SCons/Tool/textfile.py +++ b/src/engine/SCons/Tool/textfile.py @@ -71,7 +71,11 @@ def _do_subst(node, subs): contents = re.sub(k, v, contents) if 'b' in TEXTFILE_FILE_WRITE_MODE: - contents = bytearray(contents, 'utf-8') + try: + contents = bytearray(contents, 'utf-8') + except UnicodeDecodeError: + # contents is already utf-8 encoded python 2 str i.e. a byte array + contents = bytearray(contents) return contents diff --git a/src/engine/SCons/Tool/xgettext.py b/src/engine/SCons/Tool/xgettext.py index e9b49b7..2c0ce40 100644 --- a/src/engine/SCons/Tool/xgettext.py +++ b/src/engine/SCons/Tool/xgettext.py @@ -26,311 +26,333 @@ Tool specific initialization of `xgettext` tool. __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" + ############################################################################# class _CmdRunner(object): - """ Callabe object, which runs shell command storing its stdout and stderr to - variables. It also provides `strfunction()` method, which shall be used by - scons Action objects to print command string. """ - - def __init__(self, command, commandstr = None): - self.out = None - self.err = None - self.status = None - self.command = command - self.commandstr = commandstr - - def __call__(self, target, source, env): - import SCons.Action - import subprocess - import os - import sys - kw = { - 'stdin' : 'devnull', - 'stdout' : subprocess.PIPE, - 'stderr' : subprocess.PIPE, - 'universal_newlines' : True, - 'shell' : True - } - command = env.subst(self.command, target = target, source = source) - proc = SCons.Action._subproc(env, command, **kw) - self.out, self.err = proc.communicate() - self.status = proc.wait() - if self.err: - sys.stderr.write(unicode(self.err)) - return self.status - - def strfunction(self, target, source, env): - import os - comstr = self.commandstr - if env.subst(comstr, target = target, source = source) == "": - comstr = self.command - s = env.subst(comstr, target = target, source = source) - return s + """ Callable object, which runs shell command storing its stdout and stderr to + variables. It also provides `strfunction()` method, which shall be used by + scons Action objects to print command string. """ + + def __init__(self, command, commandstr=None): + self.out = None + self.err = None + self.status = None + self.command = command + self.commandstr = commandstr + + def __call__(self, target, source, env): + import SCons.Action + import subprocess + import os + import sys + kw = { + 'stdin': 'devnull', + 'stdout': subprocess.PIPE, + 'stderr': subprocess.PIPE, + 'universal_newlines': True, + 'shell': True + } + command = env.subst(self.command, target=target, source=source) + proc = SCons.Action._subproc(env, command, **kw) + self.out, self.err = proc.communicate() + self.status = proc.wait() + if self.err: + sys.stderr.write(unicode(self.err)) + return self.status + + def strfunction(self, target, source, env): + import os + comstr = self.commandstr + if env.subst(comstr, target=target, source=source) == "": + comstr = self.command + s = env.subst(comstr, target=target, source=source) + return s + + ############################################################################# ############################################################################# def _update_pot_file(target, source, env): - """ Action function for `POTUpdate` builder """ - import re - import os - import SCons.Action - nop = lambda target, source, env : 0 - - # Save scons cwd and os cwd (NOTE: they may be different. After the job, we - # revert each one to its original state). - save_cwd = env.fs.getcwd() - save_os_cwd = os.getcwd() - chdir = target[0].dir - chdir_str = repr(chdir.get_abspath()) - # Print chdir message (employ SCons.Action.Action for that. It knows better - # than me how to to this correctly). - env.Execute(SCons.Action.Action(nop, "Entering " + chdir_str)) - # Go to target's directory and do our job - env.fs.chdir(chdir, 1) # Go into target's directory - try: - cmd = _CmdRunner('$XGETTEXTCOM', '$XGETTEXTCOMSTR') - action = SCons.Action.Action(cmd, strfunction=cmd.strfunction) - status = action([ target[0] ], source, env) - except: - # Something went wrong. + """ Action function for `POTUpdate` builder """ + import re + import os + import SCons.Action + nop = lambda target, source, env: 0 + + # Save scons cwd and os cwd (NOTE: they may be different. After the job, we + # revert each one to its original state). + save_cwd = env.fs.getcwd() + save_os_cwd = os.getcwd() + chdir = target[0].dir + chdir_str = repr(chdir.get_abspath()) + # Print chdir message (employ SCons.Action.Action for that. It knows better + # than me how to to this correctly). + env.Execute(SCons.Action.Action(nop, "Entering " + chdir_str)) + # Go to target's directory and do our job + env.fs.chdir(chdir, 1) # Go into target's directory + try: + cmd = _CmdRunner('$XGETTEXTCOM', '$XGETTEXTCOMSTR') + action = SCons.Action.Action(cmd, strfunction=cmd.strfunction) + status = action([target[0]], source, env) + except: + # Something went wrong. + env.Execute(SCons.Action.Action(nop, "Leaving " + chdir_str)) + # Revert working dirs to previous state and re-throw exception. + env.fs.chdir(save_cwd, 0) + os.chdir(save_os_cwd) + raise + # Print chdir message. env.Execute(SCons.Action.Action(nop, "Leaving " + chdir_str)) - # Revert working dirs to previous state and re-throw exception. + # Revert working dirs to previous state. env.fs.chdir(save_cwd, 0) os.chdir(save_os_cwd) - raise - # Print chdir message. - env.Execute(SCons.Action.Action(nop, "Leaving " + chdir_str)) - # Revert working dirs to previous state. - env.fs.chdir(save_cwd, 0) - os.chdir(save_os_cwd) - # If the command was not successfull, return error code. - if status: return status - - new_content = cmd.out - - if not new_content: - # When xgettext finds no internationalized messages, no *.pot is created - # (because we don't want to bother translators with empty POT files). - needs_update = False - explain = "no internationalized messages encountered" - else: - if target[0].exists(): - # If the file already exists, it's left unaltered unless its messages - # are outdated (w.r.t. to these recovered by xgettext from sources). - old_content = target[0].get_text_contents() - re_cdate = re.compile(r'^"POT-Creation-Date: .*"$[\r\n]?', re.M) - old_content_nocdate = re.sub(re_cdate,"",old_content) - new_content_nocdate = re.sub(re_cdate,"",new_content) - if(old_content_nocdate == new_content_nocdate): - # Messages are up-to-date + # If the command was not successfull, return error code. + if status: return status + + new_content = cmd.out + + if not new_content: + # When xgettext finds no internationalized messages, no *.pot is created + # (because we don't want to bother translators with empty POT files). needs_update = False - explain = "messages in file found to be up-to-date" - else: - # Messages are outdated - needs_update = True - explain = "messages in file were outdated" + explain = "no internationalized messages encountered" else: - # No POT file found, create new one - needs_update = True - explain = "new file" - if needs_update: - # Print message employing SCons.Action.Action for that. - msg = "Writing " + repr(str(target[0])) + " (" + explain + ")" - env.Execute(SCons.Action.Action(nop, msg)) - f = open(str(target[0]),"w") - f.write(new_content) - f.close() - return 0 - else: - # Print message employing SCons.Action.Action for that. - msg = "Not writing " + repr(str(target[0])) + " (" + explain + ")" - env.Execute(SCons.Action.Action(nop, msg)) - return 0 + if target[0].exists(): + # If the file already exists, it's left unaltered unless its messages + # are outdated (w.r.t. to these recovered by xgettext from sources). + old_content = target[0].get_text_contents() + re_cdate = re.compile(r'^"POT-Creation-Date: .*"$[\r\n]?', re.M) + old_content_nocdate = re.sub(re_cdate, "", old_content) + new_content_nocdate = re.sub(re_cdate, "", new_content) + if (old_content_nocdate == new_content_nocdate): + # Messages are up-to-date + needs_update = False + explain = "messages in file found to be up-to-date" + else: + # Messages are outdated + needs_update = True + explain = "messages in file were outdated" + else: + # No POT file found, create new one + needs_update = True + explain = "new file" + if needs_update: + # Print message employing SCons.Action.Action for that. + msg = "Writing " + repr(str(target[0])) + " (" + explain + ")" + env.Execute(SCons.Action.Action(nop, msg)) + f = open(str(target[0]), "w") + f.write(new_content) + f.close() + return 0 + else: + # Print message employing SCons.Action.Action for that. + msg = "Not writing " + repr(str(target[0])) + " (" + explain + ")" + env.Execute(SCons.Action.Action(nop, msg)) + return 0 + + ############################################################################# ############################################################################# from SCons.Builder import BuilderBase + + ############################################################################# class _POTBuilder(BuilderBase): - def _execute(self, env, target, source, *args): - if not target: - if 'POTDOMAIN' in env and env['POTDOMAIN']: - domain = env['POTDOMAIN'] - else: - domain = 'messages' - target = [ domain ] - return BuilderBase._execute(self, env, target, source, *args) + def _execute(self, env, target, source, *args): + if not target: + if 'POTDOMAIN' in env and env['POTDOMAIN']: + domain = env['POTDOMAIN'] + else: + domain = 'messages' + target = [domain] + return BuilderBase._execute(self, env, target, source, *args) + + ############################################################################# ############################################################################# -def _scan_xgettext_from_files(target, source, env, files = None, path = None): - """ Parses `POTFILES.in`-like file and returns list of extracted file names. - """ - import re - import SCons.Util - import SCons.Node.FS - - if files is None: +def _scan_xgettext_from_files(target, source, env, files=None, path=None): + """ Parses `POTFILES.in`-like file and returns list of extracted file names. + """ + import re + import SCons.Util + import SCons.Node.FS + + if files is None: + return 0 + if not SCons.Util.is_List(files): + files = [files] + + if path is None: + if 'XGETTEXTPATH' in env: + path = env['XGETTEXTPATH'] + else: + path = [] + if not SCons.Util.is_List(path): + path = [path] + + path = SCons.Util.flatten(path) + + dirs = () + for p in path: + if not isinstance(p, SCons.Node.FS.Base): + if SCons.Util.is_String(p): + p = env.subst(p, source=source, target=target) + p = env.arg2nodes(p, env.fs.Dir) + dirs += tuple(p) + # cwd is the default search path (when no path is defined by user) + if not dirs: + dirs = (env.fs.getcwd(),) + + # Parse 'POTFILE.in' files. + re_comment = re.compile(r'^#[^\n\r]*$\r?\n?', re.M) + re_emptyln = re.compile(r'^[ \t\r]*$\r?\n?', re.M) + re_trailws = re.compile(r'[ \t\r]+$') + for f in files: + # Find files in search path $XGETTEXTPATH + if isinstance(f, SCons.Node.FS.Base) and f.rexists(): + contents = f.get_text_contents() + contents = re_comment.sub("", contents) + contents = re_emptyln.sub("", contents) + contents = re_trailws.sub("", contents) + depnames = contents.splitlines() + for depname in depnames: + depfile = SCons.Node.FS.find_file(depname, dirs) + if not depfile: + depfile = env.arg2nodes(depname, dirs[0].File) + env.Depends(target, depfile) return 0 - if not SCons.Util.is_List(files): - files = [ files ] - if path is None: - if 'XGETTEXTPATH' in env: - path = env['XGETTEXTPATH'] - else: - path = [] - if not SCons.Util.is_List(path): - path = [ path ] - - path = SCons.Util.flatten(path) - - dirs = () - for p in path: - if not isinstance(p, SCons.Node.FS.Base): - if SCons.Util.is_String(p): - p = env.subst(p, source = source, target = target) - p = env.arg2nodes(p, env.fs.Dir) - dirs += tuple(p) - # cwd is the default search path (when no path is defined by user) - if not dirs: - dirs = (env.fs.getcwd(),) - - # Parse 'POTFILE.in' files. - re_comment = re.compile(r'^#[^\n\r]*$\r?\n?', re.M) - re_emptyln = re.compile(r'^[ \t\r]*$\r?\n?', re.M) - re_trailws = re.compile(r'[ \t\r]+$') - for f in files: - # Find files in search path $XGETTEXTPATH - if isinstance(f, SCons.Node.FS.Base) and f.rexists(): - contents = f.get_text_contents() - contents = re_comment.sub("", contents) - contents = re_emptyln.sub("", contents) - contents = re_trailws.sub("", contents) - depnames = contents.splitlines() - for depname in depnames: - depfile = SCons.Node.FS.find_file(depname, dirs) - if not depfile: - depfile = env.arg2nodes(depname, dirs[0].File) - env.Depends(target, depfile) - return 0 + ############################################################################# ############################################################################# def _pot_update_emitter(target, source, env): - """ Emitter function for `POTUpdate` builder """ - from SCons.Tool.GettextCommon import _POTargetFactory - import SCons.Util - import SCons.Node.FS - - if 'XGETTEXTFROM' in env: - xfrom = env['XGETTEXTFROM'] - else: + """ Emitter function for `POTUpdate` builder """ + from SCons.Tool.GettextCommon import _POTargetFactory + import SCons.Util + import SCons.Node.FS + + if 'XGETTEXTFROM' in env: + xfrom = env['XGETTEXTFROM'] + else: + return target, source + if not SCons.Util.is_List(xfrom): + xfrom = [xfrom] + + xfrom = SCons.Util.flatten(xfrom) + + # Prepare list of 'POTFILE.in' files. + files = [] + for xf in xfrom: + if not isinstance(xf, SCons.Node.FS.Base): + if SCons.Util.is_String(xf): + # Interpolate variables in strings + xf = env.subst(xf, source=source, target=target) + xf = env.arg2nodes(xf) + files.extend(xf) + if files: + env.Depends(target, files) + _scan_xgettext_from_files(target, source, env, files) return target, source - if not SCons.Util.is_List(xfrom): - xfrom = [ xfrom ] - - xfrom = SCons.Util.flatten(xfrom) - - # Prepare list of 'POTFILE.in' files. - files = [] - for xf in xfrom: - if not isinstance(xf, SCons.Node.FS.Base): - if SCons.Util.is_String(xf): - # Interpolate variables in strings - xf = env.subst(xf, source = source, target = target) - xf = env.arg2nodes(xf) - files.extend(xf) - if files: - env.Depends(target, files) - _scan_xgettext_from_files(target, source, env, files) - return target, source + + ############################################################################# ############################################################################# from SCons.Environment import _null + + ############################################################################# def _POTUpdateBuilderWrapper(env, target=None, source=_null, **kw): - return env._POTUpdateBuilder(target, source, **kw) + return env._POTUpdateBuilder(target, source, **kw) + + ############################################################################# ############################################################################# def _POTUpdateBuilder(env, **kw): - """ Creates `POTUpdate` builder object """ - import SCons.Action - from SCons.Tool.GettextCommon import _POTargetFactory - kw['action'] = SCons.Action.Action(_update_pot_file, None) - kw['suffix'] = '$POTSUFFIX' - kw['target_factory'] = _POTargetFactory(env, alias='$POTUPDATE_ALIAS').File - kw['emitter'] = _pot_update_emitter - return _POTBuilder(**kw) + """ Creates `POTUpdate` builder object """ + import SCons.Action + from SCons.Tool.GettextCommon import _POTargetFactory + kw['action'] = SCons.Action.Action(_update_pot_file, None) + kw['suffix'] = '$POTSUFFIX' + kw['target_factory'] = _POTargetFactory(env, alias='$POTUPDATE_ALIAS').File + kw['emitter'] = _pot_update_emitter + return _POTBuilder(**kw) + + ############################################################################# ############################################################################# -def generate(env,**kw): - """ Generate `xgettext` tool """ - import SCons.Util - from SCons.Tool.GettextCommon import RPaths, _detect_xgettext - - try: - env['XGETTEXT'] = _detect_xgettext(env) - except: - env['XGETTEXT'] = 'xgettext' - # NOTE: sources="$SOURCES" would work as well. However, we use following - # construction to convert absolute paths provided by scons onto paths - # relative to current working dir. Note, that scons expands $SOURCE(S) to - # absolute paths for sources $SOURCE(s) outside of current subtree (e.g. in - # "../"). With source=$SOURCE these absolute paths would be written to the - # resultant *.pot file (and its derived *.po files) as references to lines in - # source code (e.g. referring lines in *.c files). Such references would be - # correct (e.g. in poedit) only on machine on which *.pot was generated and - # would be of no use on other hosts (having a copy of source code located - # in different place in filesystem). - sources = '$( ${_concat( "", SOURCES, "", __env__, XgettextRPaths, TARGET' \ - + ', SOURCES)} $)' - - # NOTE: the output from $XGETTEXTCOM command must go to stdout, not to a file. - # This is required by the POTUpdate builder's action. - xgettextcom = '$XGETTEXT $XGETTEXTFLAGS $_XGETTEXTPATHFLAGS' \ - + ' $_XGETTEXTFROMFLAGS -o - ' + sources - - xgettextpathflags = '$( ${_concat( XGETTEXTPATHPREFIX, XGETTEXTPATH' \ - + ', XGETTEXTPATHSUFFIX, __env__, RDirs, TARGET, SOURCES)} $)' - xgettextfromflags = '$( ${_concat( XGETTEXTFROMPREFIX, XGETTEXTFROM' \ - + ', XGETTEXTFROMSUFFIX, __env__, target=TARGET, source=SOURCES)} $)' - - env.SetDefault( - _XGETTEXTDOMAIN = '${TARGET.filebase}', - XGETTEXTFLAGS = [ ], - XGETTEXTCOM = xgettextcom, - XGETTEXTCOMSTR = '', - XGETTEXTPATH = [ ], - XGETTEXTPATHPREFIX = '-D', - XGETTEXTPATHSUFFIX = '', - XGETTEXTFROM = None, - XGETTEXTFROMPREFIX = '-f', - XGETTEXTFROMSUFFIX = '', - _XGETTEXTPATHFLAGS = xgettextpathflags, - _XGETTEXTFROMFLAGS = xgettextfromflags, - POTSUFFIX = ['.pot'], - POTUPDATE_ALIAS = 'pot-update', - XgettextRPaths = RPaths(env) - ) - env.Append( BUILDERS = { - '_POTUpdateBuilder' : _POTUpdateBuilder(env) - } ) - env.AddMethod(_POTUpdateBuilderWrapper, 'POTUpdate') - env.AlwaysBuild(env.Alias('$POTUPDATE_ALIAS')) +def generate(env, **kw): + """ Generate `xgettext` tool """ + import SCons.Util + from SCons.Tool.GettextCommon import RPaths, _detect_xgettext + + try: + env['XGETTEXT'] = _detect_xgettext(env) + except: + env['XGETTEXT'] = 'xgettext' + # NOTE: sources="$SOURCES" would work as well. However, we use following + # construction to convert absolute paths provided by scons onto paths + # relative to current working dir. Note, that scons expands $SOURCE(S) to + # absolute paths for sources $SOURCE(s) outside of current subtree (e.g. in + # "../"). With source=$SOURCE these absolute paths would be written to the + # resultant *.pot file (and its derived *.po files) as references to lines in + # source code (e.g. referring lines in *.c files). Such references would be + # correct (e.g. in poedit) only on machine on which *.pot was generated and + # would be of no use on other hosts (having a copy of source code located + # in different place in filesystem). + sources = '$( ${_concat( "", SOURCES, "", __env__, XgettextRPaths, TARGET' \ + + ', SOURCES)} $)' + + # NOTE: the output from $XGETTEXTCOM command must go to stdout, not to a file. + # This is required by the POTUpdate builder's action. + xgettextcom = '$XGETTEXT $XGETTEXTFLAGS $_XGETTEXTPATHFLAGS' \ + + ' $_XGETTEXTFROMFLAGS -o - ' + sources + + xgettextpathflags = '$( ${_concat( XGETTEXTPATHPREFIX, XGETTEXTPATH' \ + + ', XGETTEXTPATHSUFFIX, __env__, RDirs, TARGET, SOURCES)} $)' + xgettextfromflags = '$( ${_concat( XGETTEXTFROMPREFIX, XGETTEXTFROM' \ + + ', XGETTEXTFROMSUFFIX, __env__, target=TARGET, source=SOURCES)} $)' + + env.SetDefault( + _XGETTEXTDOMAIN='${TARGET.filebase}', + XGETTEXTFLAGS=[], + XGETTEXTCOM=xgettextcom, + XGETTEXTCOMSTR='', + XGETTEXTPATH=[], + XGETTEXTPATHPREFIX='-D', + XGETTEXTPATHSUFFIX='', + XGETTEXTFROM=None, + XGETTEXTFROMPREFIX='-f', + XGETTEXTFROMSUFFIX='', + _XGETTEXTPATHFLAGS=xgettextpathflags, + _XGETTEXTFROMFLAGS=xgettextfromflags, + POTSUFFIX=['.pot'], + POTUPDATE_ALIAS='pot-update', + XgettextRPaths=RPaths(env) + ) + env.Append(BUILDERS={ + '_POTUpdateBuilder': _POTUpdateBuilder(env) + }) + env.AddMethod(_POTUpdateBuilderWrapper, 'POTUpdate') + env.AlwaysBuild(env.Alias('$POTUPDATE_ALIAS')) + + ############################################################################# ############################################################################# def exists(env): - """ Check, whether the tool exists """ - from SCons.Tool.GettextCommon import _xgettext_exists - try: - return _xgettext_exists(env) - except: - return False + """ Check, whether the tool exists """ + from SCons.Tool.GettextCommon import _xgettext_exists + try: + return _xgettext_exists(env) + except: + return False + ############################################################################# # Local Variables: diff --git a/src/engine/SCons/Util.py b/src/engine/SCons/Util.py index a8a6990..2d8e75a 100644 --- a/src/engine/SCons/Util.py +++ b/src/engine/SCons/Util.py @@ -31,6 +31,7 @@ import sys import copy import re import types +import codecs try: from UserDict import UserDict @@ -148,7 +149,7 @@ class NodeList(UserList): # else: # self.data = [ initlist,] - + def __nonzero__(self): return len(self.data) != 0 @@ -170,10 +171,10 @@ class NodeList(UserList): return self.__class__(result) def __getitem__(self, index): - """ + """ This comes for free on py2, but py3 slices of NodeList are returning a list - breaking slicing nodelist and refering to + breaking slicing nodelist and refering to properties and methods on contained object """ # return self.__class__(self.data[index]) @@ -289,6 +290,17 @@ def print_tree(root, child_func, prune=0, showtags=0, margin=[0], visited=None): """ rname = str(root) + if sys.version_info.major < 3: + # Python 2 UTF-8 encoded str are str. escape_encode is a str to str + # encoding + rname = codecs.escape_encode(rname)[0] + else: + # Python 3 UTF-8 encoded str are bytes. escape_encode is a byte to byte + # encoding here. + rname = rname.encode('utf-8') + rname = codecs.escape_encode(rname)[0] + # Finally, we need a string again. + rname = rname.decode('ascii') # Initialize 'visited' dict, if required if visited is None: diff --git a/src/script/sconsign.py b/src/script/sconsign.py index 8f1722a..2e7a550 100644 --- a/src/script/sconsign.py +++ b/src/script/sconsign.py @@ -241,6 +241,11 @@ def default_mapper(entry, name): val = eval("entry."+name) except: val = None + if sys.version_info.major >= 3 and isinstance(val, bytes): + # This is a dirty hack for py 2/3 compatibility. csig is a bytes object + # in Python3 while Python2 bytes are str. Hence, we decode the csig to a + # Python3 string + val = val.decode() return str(val) def map_action(entry, name): diff --git a/test/CPPDEFINES/append.py b/test/CPPDEFINES/append.py index 14ea7b3..7973f22 100644 --- a/test/CPPDEFINES/append.py +++ b/test/CPPDEFINES/append.py @@ -54,10 +54,24 @@ print(env_2300_2.subst('$_CPPDEFFLAGS')) # http://scons.tigris.org/issues/show_bug.cgi?id=1152 # http://scons.tigris.org/issues/show_bug.cgi?id=2900 +# Python3 dicts dont preserve order. Hence we supply subclass of OrderedDict +# whose __str__ and __repr__ act like a normal dict. +from collections import OrderedDict +class OrderedPrintingDict(OrderedDict): + def __repr__(self): + return '{' + ', '.join(['%r: %r'%(k, v) for (k, v) in self.items()]) + '}' + + __str__ = __repr__ + + # Because dict-like objects (except dict and UserDict) are not deep copied + # directly when constructing Environment(CPPDEFINES = OrderedPrintingDict(...)) + def __semi_deepcopy__(self): + return self.copy() + cases=[('string', 'FOO'), ('list', ['NAME1', 'NAME2']), ('list-of-2lists', [('NAME1','VAL1'), ['NAME2','VAL2']]), - ('dict', {'NAME1' : 'VAL1', 'NAME2' : 'VAL2', 'NAME3' : None}) + ('dict', OrderedPrintingDict([('NAME2', 'VAL2'), ('NAME3', None), ('NAME1', 'VAL1')])) ] for (t1, c1) in cases: @@ -180,7 +194,7 @@ AppendUnique: ==== Testing CPPDEFINES, appending a string to a dict orig = {'NAME2': 'VAL2', 'NAME3': None, 'NAME1': 'VAL1'}, append = FOO Append: - result={'FOO': None, 'NAME2': 'VAL2', 'NAME3': None, 'NAME1': 'VAL1'} + result={'NAME2': 'VAL2', 'NAME3': None, 'NAME1': 'VAL1', 'FOO': None} final=-DFOO -DNAME1=VAL1 -DNAME2=VAL2 -DNAME3 AppendUnique: result=[('NAME2', 'VAL2'), ('NAME3',), ('NAME1', 'VAL1'), 'FOO'] diff --git a/test/CPPDEFINES/pkg-config.py b/test/CPPDEFINES/pkg-config.py index 4e81dec..42f38b6 100644 --- a/test/CPPDEFINES/pkg-config.py +++ b/test/CPPDEFINES/pkg-config.py @@ -56,6 +56,20 @@ int main(int argc, char *argv[]) """) test.write('SConstruct', """\ +# Python3 dicts dont preserve order. Hence we supply subclass of OrderedDict +# whose __str__ and __repr__ act like a normal dict. +from collections import OrderedDict +class OrderedPrintingDict(OrderedDict): + def __repr__(self): + return '{' + ', '.join(['%r: %r'%(k, v) for (k, v) in self.items()]) + '}' + + __str__ = __repr__ + + # Because dict-like objects (except dict and UserDict) are not deep copied + # directly when constructing Environment(CPPDEFINES = OrderedPrintingDict(...)) + def __semi_deepcopy__(self): + return self.copy() +""" + """ # http://scons.tigris.org/issues/show_bug.cgi?id=2671 # Passing test cases env_1 = Environment(CPPDEFINES=[('DEBUG','1'), 'TEST']) @@ -67,11 +81,11 @@ env_2.MergeFlags('-DSOMETHING -DVARIABLE=2') print(env_2.subst('$_CPPDEFFLAGS')) # Failing test cases -env_3 = Environment(CPPDEFINES={'DEBUG':1, 'TEST':None}) +env_3 = Environment(CPPDEFINES=OrderedPrintingDict([('DEBUG', 1), ('TEST', None)])) env_3.ParseConfig('PKG_CONFIG_PATH=. %(pkg_config_path)s --cflags bug') print(env_3.subst('$_CPPDEFFLAGS')) -env_4 = Environment(CPPDEFINES={'DEBUG':1, 'TEST':None}) +env_4 = Environment(CPPDEFINES=OrderedPrintingDict([('DEBUG', 1), ('TEST', None)])) env_4.MergeFlags('-DSOMETHING -DVARIABLE=2') print(env_4.subst('$_CPPDEFFLAGS')) diff --git a/test/D/MixedDAndC/sconstest-dmd.py b/test/D/MixedDAndC/sconstest-dmd.py index df66255..d96c5c3 100644 --- a/test/D/MixedDAndC/sconstest-dmd.py +++ b/test/D/MixedDAndC/sconstest-dmd.py @@ -27,6 +27,16 @@ Test compiling and executing a project with a C module. __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" +import sys +if sys.platform == 'darwin': + import TestSCons + test = TestSCons.TestSCons() + + msg = "Skipping Mixed dmd test until a good way to ensure proper gcc is called." + "Calling default(system /usr/bin/gcc) gcc yields a surplus of linking errors\n" + test.skip_test(msg) + + from Common.common import testForTool testForTool('dmd') diff --git a/test/Deprecated/Sig.py b/test/Deprecated/Sig.py deleted file mode 100644 index 1ae118b..0000000 --- a/test/Deprecated/Sig.py +++ /dev/null @@ -1,68 +0,0 @@ -#!/usr/bin/env python -# -# __COPYRIGHT__ -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" - -""" -Verify that we generate the proper warning, but don't die, when someone -tries to import the SCons.Sig module (which no longer exists) and -use the things we used to define therein. -""" - -import TestSCons - -test = TestSCons.TestSCons() - -SConstruct = test.workpath('SConstruct') - -test.write(SConstruct, """ -import SCons.Sig -x = SCons.Sig.default_calc -x = SCons.Sig.default_module -x = SCons.Sig.MD5.current() -x = SCons.Sig.MD5.collect() -x = SCons.Sig.MD5.signature() -x = SCons.Sig.MD5.to_string() -x = SCons.Sig.MD5.from_string() -x = SCons.Sig.TimeStamp.current() -x = SCons.Sig.TimeStamp.collect() -x = SCons.Sig.TimeStamp.signature() -x = SCons.Sig.TimeStamp.to_string() -x = SCons.Sig.TimeStamp.from_string() -""") - -expect = """ -scons: warning: The SCons.Sig module no longer exists. - Remove the following "import SCons.Sig" line to eliminate this warning: -""" + test.python_file_line(SConstruct, 2) - -test.run(arguments = '.', stderr=expect) - -test.pass_test() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/Errors/InternalError.py b/test/Errors/InternalError.py index 8ed6da1..a709597 100644 --- a/test/Errors/InternalError.py +++ b/test/Errors/InternalError.py @@ -47,7 +47,7 @@ test.run(stdout = "scons: Reading SConscript files ...\ninternal error\n", File ".+", line \d+, in .+ File ".+SConstruct", line \d+, in .+ raise InternalError\('error inside'\) -InternalError: error inside +(SCons\.Errors\.|)InternalError: error inside """, status=2) test.pass_test() diff --git a/test/GetBuildFailures/option-k.py b/test/GetBuildFailures/option-k.py index 0ff22e6..12ae07b 100644 --- a/test/GetBuildFailures/option-k.py +++ b/test/GetBuildFailures/option-k.py @@ -64,7 +64,7 @@ Command('f6', 'f6.in', r'@%(_python_)s mypass.py f5 - $TARGET $SOURCE') def print_build_failures(): from SCons.Script import GetBuildFailures - for bf in sorted(GetBuildFailures(), key=lambda a: a.filename): + for bf in sorted(GetBuildFailures(), key=lambda a: a.filename or 'None'): print("%%s failed: %%s" %% (bf.node, bf.errstr)) import atexit diff --git a/test/GetBuildFailures/parallel.py b/test/GetBuildFailures/parallel.py index ee0e831..f9503e0 100644 --- a/test/GetBuildFailures/parallel.py +++ b/test/GetBuildFailures/parallel.py @@ -80,7 +80,7 @@ Command('f6', 'f6.in', r'@%(_python_)s mypass.py f5 - $TARGET $SOURCE') def print_build_failures(): from SCons.Script import GetBuildFailures - for bf in sorted(GetBuildFailures(), key=lambda t: t.filename): + for bf in sorted(GetBuildFailures(), key=lambda t: t.filename or 'None'): print("%%s failed: %%s" %% (bf.node, bf.errstr)) import atexit diff --git a/test/LINK/VersionedLib-VariantDir.py b/test/LINK/VersionedLib-VariantDir.py index 0a631b0..0350c6e 100644 --- a/test/LINK/VersionedLib-VariantDir.py +++ b/test/LINK/VersionedLib-VariantDir.py @@ -35,11 +35,19 @@ import sys import SCons.Platform import SCons.Defaults +test = TestSCons.TestSCons() + +import sys +if sys.platform == 'darwin': + # Skipping until logic is fixed for macosx + test.skip_test("Not working on darwin yet\n") + + env = SCons.Defaults.DefaultEnvironment() platform = SCons.Platform.platform_default() tool_list = SCons.Platform.DefaultToolList(platform, env) -test = TestSCons.TestSCons() + test.subdir(['src']) test.subdir(['src','lib']) diff --git a/test/LINK/VersionedLib-j2.py b/test/LINK/VersionedLib-j2.py index 249b54f..4646a37 100644 --- a/test/LINK/VersionedLib-j2.py +++ b/test/LINK/VersionedLib-j2.py @@ -39,6 +39,11 @@ import SCons.Defaults test = TestSCons.TestSCons() +if sys.platform == 'darwin': + # Skipping until logic is fixed for macosx + test.skip_test("Not working on darwin yet\n") + + test.write('foo.c', """ #if _WIN32 __declspec(dllexport) diff --git a/test/LINK/VersionedLib-subdir.py b/test/LINK/VersionedLib-subdir.py index a2e141b..91f3011 100644 --- a/test/LINK/VersionedLib-subdir.py +++ b/test/LINK/VersionedLib-subdir.py @@ -41,6 +41,11 @@ import SCons.Defaults test = TestSCons.TestSCons() +if sys.platform == 'darwin': + # Skipping until logic is fixed for macosx + test.skip_test("Not working on darwin yet\n") + + test.write('foo.c', """ #if _WIN32 __declspec(dllexport) diff --git a/test/LINK/VersionedLib.py b/test/LINK/VersionedLib.py index 468e3e5..c05c159 100644 --- a/test/LINK/VersionedLib.py +++ b/test/LINK/VersionedLib.py @@ -33,6 +33,12 @@ import TestSCons import SCons.Platform import SCons.Defaults +import sys +if sys.platform == 'darwin': + # Skipping until logic is fixed for macosx + test = TestSCons.TestSCons() + test.skip_test("Not working on darwin yet\n") + env = SCons.Defaults.DefaultEnvironment() platform = SCons.Platform.platform_default() tool_list = SCons.Platform.DefaultToolList(platform, env) diff --git a/test/MSVS/vs-10.0-scc-files.py b/test/MSVS/vs-10.0-scc-files.py index 32dd9ef..0b8bd76 100644 --- a/test/MSVS/vs-10.0-scc-files.py +++ b/test/MSVS/vs-10.0-scc-files.py @@ -69,9 +69,9 @@ env.MSVSProject(target = 'Test.vcxproj', expected_sln_sccinfo = """\ \tGlobalSection(SourceCodeControl) = preSolution \t\tSccNumberOfProjects = 2 -\t\tSccProjectName0 = Perforce\u0020Project +\t\tSccProjectName0 = Perforce\\u0020Project \t\tSccLocalPath0 = . -\t\tSccProvider0 = MSSCCI:Perforce\u0020SCM +\t\tSccProvider0 = MSSCCI:Perforce\\u0020SCM \t\tCanCheckoutShared = true \t\tSccProjectUniqueName1 = Test.vcxproj \t\tSccLocalPath1 = . diff --git a/test/MSVS/vs-11.0-scc-files.py b/test/MSVS/vs-11.0-scc-files.py index 6d12c79..a25b954 100644 --- a/test/MSVS/vs-11.0-scc-files.py +++ b/test/MSVS/vs-11.0-scc-files.py @@ -69,9 +69,9 @@ env.MSVSProject(target = 'Test.vcxproj', expected_sln_sccinfo = """\ \tGlobalSection(SourceCodeControl) = preSolution \t\tSccNumberOfProjects = 2 -\t\tSccProjectName0 = Perforce\u0020Project +\t\tSccProjectName0 = Perforce\\u0020Project \t\tSccLocalPath0 = . -\t\tSccProvider0 = MSSCCI:Perforce\u0020SCM +\t\tSccProvider0 = MSSCCI:Perforce\\u0020SCM \t\tCanCheckoutShared = true \t\tSccProjectUniqueName1 = Test.vcxproj \t\tSccLocalPath1 = . diff --git a/test/MSVS/vs-14.0-scc-files.py b/test/MSVS/vs-14.0-scc-files.py index c934ac9..b6db6d5 100644 --- a/test/MSVS/vs-14.0-scc-files.py +++ b/test/MSVS/vs-14.0-scc-files.py @@ -69,9 +69,9 @@ env.MSVSProject(target = 'Test.vcxproj', expected_sln_sccinfo = """\ \tGlobalSection(SourceCodeControl) = preSolution \t\tSccNumberOfProjects = 2 -\t\tSccProjectName0 = Perforce\u0020Project +\t\tSccProjectName0 = Perforce\\u0020Project \t\tSccLocalPath0 = . -\t\tSccProvider0 = MSSCCI:Perforce\u0020SCM +\t\tSccProvider0 = MSSCCI:Perforce\\u0020SCM \t\tCanCheckoutShared = true \t\tSccProjectUniqueName1 = Test.vcxproj \t\tSccLocalPath1 = . diff --git a/test/MSVS/vs-7.0-scc-files.py b/test/MSVS/vs-7.0-scc-files.py index 76b4380..8586060 100644 --- a/test/MSVS/vs-7.0-scc-files.py +++ b/test/MSVS/vs-7.0-scc-files.py @@ -71,9 +71,9 @@ env.MSVSProject(target = 'Test.vcproj', expected_sln_sccinfo = """\ \tGlobalSection(SourceCodeControl) = preSolution \t\tSccNumberOfProjects = 2 -\t\tSccProjectName0 = Perforce\u0020Project +\t\tSccProjectName0 = Perforce\\u0020Project \t\tSccLocalPath0 = . -\t\tSccProvider0 = MSSCCI:Perforce\u0020SCM +\t\tSccProvider0 = MSSCCI:Perforce\\u0020SCM \t\tCanCheckoutShared = true \t\tSolutionUniqueID = {SLNGUID} \t\tSccProjectUniqueName1 = Test.vcproj diff --git a/test/MSVS/vs-7.1-scc-files.py b/test/MSVS/vs-7.1-scc-files.py index 219bd0a..8404422 100644 --- a/test/MSVS/vs-7.1-scc-files.py +++ b/test/MSVS/vs-7.1-scc-files.py @@ -71,9 +71,9 @@ env.MSVSProject(target = 'Test.vcproj', expected_sln_sccinfo = """\ \tGlobalSection(SourceCodeControl) = preSolution \t\tSccNumberOfProjects = 2 -\t\tSccProjectName0 = Perforce\u0020Project +\t\tSccProjectName0 = Perforce\\u0020Project \t\tSccLocalPath0 = . -\t\tSccProvider0 = MSSCCI:Perforce\u0020SCM +\t\tSccProvider0 = MSSCCI:Perforce\\u0020SCM \t\tCanCheckoutShared = true \t\tSolutionUniqueID = {SLNGUID} \t\tSccProjectUniqueName1 = Test.vcproj diff --git a/test/MSVS/vs-8.0-scc-files.py b/test/MSVS/vs-8.0-scc-files.py index 1baa407..05a8a5f 100644 --- a/test/MSVS/vs-8.0-scc-files.py +++ b/test/MSVS/vs-8.0-scc-files.py @@ -69,9 +69,9 @@ env.MSVSProject(target = 'Test.vcproj', expected_sln_sccinfo = """\ \tGlobalSection(SourceCodeControl) = preSolution \t\tSccNumberOfProjects = 2 -\t\tSccProjectName0 = Perforce\u0020Project +\t\tSccProjectName0 = Perforce\\u0020Project \t\tSccLocalPath0 = . -\t\tSccProvider0 = MSSCCI:Perforce\u0020SCM +\t\tSccProvider0 = MSSCCI:Perforce\\u0020SCM \t\tCanCheckoutShared = true \t\tSccProjectUniqueName1 = Test.vcproj \t\tSccLocalPath1 = . diff --git a/test/MSVS/vs-9.0-scc-files.py b/test/MSVS/vs-9.0-scc-files.py index e3d37c0..c270010 100644 --- a/test/MSVS/vs-9.0-scc-files.py +++ b/test/MSVS/vs-9.0-scc-files.py @@ -69,9 +69,9 @@ env.MSVSProject(target = 'Test.vcproj', expected_sln_sccinfo = """\ \tGlobalSection(SourceCodeControl) = preSolution \t\tSccNumberOfProjects = 2 -\t\tSccProjectName0 = Perforce\u0020Project +\t\tSccProjectName0 = Perforce\\u0020Project \t\tSccLocalPath0 = . -\t\tSccProvider0 = MSSCCI:Perforce\u0020SCM +\t\tSccProvider0 = MSSCCI:Perforce\\u0020SCM \t\tCanCheckoutShared = true \t\tSccProjectUniqueName1 = Test.vcproj \t\tSccLocalPath1 = . diff --git a/test/Progress/TARGET.py b/test/Progress/TARGET.py index d7ff3c9..9af5565 100644 --- a/test/Progress/TARGET.py +++ b/test/Progress/TARGET.py @@ -50,12 +50,12 @@ test.write('S2.in', "S2.in\n") test.write('S3.in', "S3.in\n") test.write('S4.in', "S4.in\n") -expect = """\ +expect = bytearray("""\ S1.in\r \rS1.out\rCopy("S1.out", "S1.in") \rS2.in\r \rS2.out\rCopy("S2.out", "S2.in") \rS3.in\r \rS3.out\rCopy("S3.out", "S3.in") \rS4.in\r \rS4.out\rCopy("S4.out", "S4.in") - \rSConstruct\r \r.\r""" + \rSConstruct\r \r.\r""",'utf-8') if os.linesep != '\n': expect = expect.replace('\n', os.linesep) diff --git a/test/Progress/spinner.py b/test/Progress/spinner.py index 85ca32f..59c0a89 100644 --- a/test/Progress/spinner.py +++ b/test/Progress/spinner.py @@ -33,6 +33,7 @@ import os import TestSCons + test = TestSCons.TestSCons(universal_newlines=None) test.write('SConstruct', r""" @@ -50,12 +51,12 @@ test.write('S2.in', "S2.in\n") test.write('S3.in', "S3.in\n") test.write('S4.in', "S4.in\n") -expect = """\ +expect = bytearray("""\ \\\r|\rCopy("S1.out", "S1.in") /\r-\rCopy("S2.out", "S2.in") \\\r|\rCopy("S3.out", "S3.in") /\r-\rCopy("S4.out", "S4.in") -\\\r|\r""" +\\\r|\r""",'utf-8') if os.linesep != '\n': expect = expect.replace('\n', os.linesep) diff --git a/test/SConsignFile/use-dbm.py b/test/SConsignFile/use-dbm.py index 90983b3..129d5b6 100644 --- a/test/SConsignFile/use-dbm.py +++ b/test/SConsignFile/use-dbm.py @@ -34,10 +34,16 @@ _python_ = TestSCons._python_ test = TestSCons.TestSCons() + try: import dbm.ndbm + use_db = 'dbm.ndbm' except ImportError: - test.skip_test('No dbm in this version of Python; skipping test.\n') + try: + import dbm + use_db = 'dbm' + except ImportError: + test.skip_test('No dbm.ndbm in this version of Python; skipping test.\n') test.subdir('subdir') @@ -53,8 +59,8 @@ sys.exit(0) # test.write('SConstruct', """ import sys -import dbm -SConsignFile('.sconsign', dbm) +import %(use_db)s +SConsignFile('.sconsign', %(use_db)s) B = Builder(action = '%(_python_)s build.py $TARGETS $SOURCES') env = Environment(BUILDERS = { 'B' : B }) env.B(target = 'f1.out', source = 'f1.in') diff --git a/test/SWIG/recursive-includes-cpp.py b/test/SWIG/recursive-includes-cpp.py index 364bd73..dbcac6d 100644 --- a/test/SWIG/recursive-includes-cpp.py +++ b/test/SWIG/recursive-includes-cpp.py @@ -64,6 +64,7 @@ test.write("mod.i", """\ test.write('SConstruct', """\ import distutils.sysconfig +import sys DefaultEnvironment( tools = [ 'swig' ] ) @@ -77,6 +78,9 @@ env = Environment( SHLIBPREFIX = "" ) +if sys.platform == 'darwin': + env['LIBS']=['python%d.%d'%(sys.version_info[0],sys.version_info[1])] + env.SharedLibrary( 'mod.so', [ diff --git a/test/Subst/TypeError.py b/test/Subst/TypeError.py index 371ceff..628db2f 100644 --- a/test/Subst/TypeError.py +++ b/test/Subst/TypeError.py @@ -58,9 +58,7 @@ expect = expect_build % (r' \[foo\.bar\]', r'\$\{NONE\[0\]\}') test.run(status=2, stderr=expect) - - -expect_build = r"""scons: \*\*\*%s TypeError `(not enough arguments; expected 3, got 1|func\(\) takes exactly 3 arguments \(1 given\))' trying to evaluate `%s' +expect_build = r"""scons: \*\*\*%s TypeError `(not enough arguments; expected 3, got 1|func\(\) takes exactly 3 arguments \(1 given\)|func\(\) missing 2 required positional arguments: 'b' and 'c')' trying to evaluate `%s' """ expect_read = "\n" + expect_build + TestSCons.file_expr diff --git a/test/TEX/auxiliaries.py b/test/TEX/auxiliaries.py index 98cbf41..8d220c5 100644 --- a/test/TEX/auxiliaries.py +++ b/test/TEX/auxiliaries.py @@ -120,7 +120,7 @@ test.write(['docs', 'test.tex'], tex_input) test.run(stderr=None) pdf_output_1 = test.read(['build', 'docs', 'test.pdf']) -ps_output_1 = test.read(['build', 'docs', 'test.ps']) +ps_output_1 = test.read(['build', 'docs', 'test.ps'], mode='r') # Adding blank lines will cause SCons to re-run the builds, but the # actual contents of the output files should be the same modulo @@ -130,7 +130,7 @@ test.write(['docs', 'test.tex'], tex_input + "\n\n\n") test.run(stderr=None) pdf_output_2 = test.read(['build', 'docs', 'test.pdf']) -ps_output_2 = test.read(['build', 'docs', 'test.ps']) +ps_output_2 = test.read(['build', 'docs', 'test.ps'], mode='r') diff --git a/test/diskcheck.py b/test/diskcheck.py index adbeea1..36cfa4e 100644 --- a/test/diskcheck.py +++ b/test/diskcheck.py @@ -52,30 +52,6 @@ test.must_contain_all_lines(test.stderr(), ["found where file expected"]) -test.write('SConstruct', """ -SetOption('diskcheck', ['rcs', 'sccs']) -Dir('file') -""") - -test.run() - -test.run(arguments='--diskcheck=match', status=2, stderr=None) -test.must_contain_all_lines(test.stderr(), ["found where directory expected"]) - - - -test.write('SConstruct', """ -SetOption('diskcheck', 'rcs,sccs') -Dir('file/subdir') -""") - -test.run() - -test.run(arguments='--diskcheck=match', status=2, stderr=None) -test.must_contain_all_lines(test.stderr(), ["found where directory expected"]) - - - test.pass_test() # Local Variables: diff --git a/test/gettext/POTUpdate/UserExamples.py b/test/gettext/POTUpdate/UserExamples.py index 6269081..06203fa 100644 --- a/test/gettext/POTUpdate/UserExamples.py +++ b/test/gettext/POTUpdate/UserExamples.py @@ -62,19 +62,19 @@ test.must_not_exist( ['ex1', 'po', 'bar.pot'] ) test.run(arguments = 'foo.pot', chdir = path.join('ex1', 'po')) test.must_exist( ['ex1', 'po', 'foo.pot'] ) test.must_not_exist( ['ex1', 'po', 'bar.pot'] ) -test.must_contain( ['ex1', 'po', 'foo.pot'], "Hello from a.cpp" ) -test.must_contain( ['ex1', 'po', 'foo.pot'], "Hello from b.cpp" ) -test.must_not_contain( ['ex1', 'po', 'foo.pot'], "Hello from c.cpp" ) -test.must_not_contain( ['ex1', 'po', 'foo.pot'], "Hello from d.cpp" ) +test.must_contain( ['ex1', 'po', 'foo.pot'], "Hello from a.cpp", mode='r') +test.must_contain( ['ex1', 'po', 'foo.pot'], "Hello from b.cpp", mode='r') +test.must_not_contain( ['ex1', 'po', 'foo.pot'], "Hello from c.cpp", mode='r') +test.must_not_contain( ['ex1', 'po', 'foo.pot'], "Hello from d.cpp", mode='r') # scons 'pot-update' creates foo.pot and bar.pot test.run(arguments = 'pot-update', chdir = path.join('ex1', 'po')) test.must_exist( ['ex1', 'po', 'foo.pot'] ) test.must_exist( ['ex1', 'po', 'bar.pot'] ) -test.must_not_contain( ['ex1', 'po', 'bar.pot'], "Hello from a.cpp" ) -test.must_not_contain( ['ex1', 'po', 'bar.pot'], "Hello from b.cpp" ) -test.must_contain( ['ex1', 'po', 'bar.pot'], "Hello from c.cpp" ) -test.must_contain( ['ex1', 'po', 'bar.pot'], "Hello from d.cpp" ) +test.must_not_contain( ['ex1', 'po', 'bar.pot'], "Hello from a.cpp", mode='r') +test.must_not_contain( ['ex1', 'po', 'bar.pot'], "Hello from b.cpp", mode='r') +test.must_contain( ['ex1', 'po', 'bar.pot'], "Hello from c.cpp", mode='r') +test.must_contain( ['ex1', 'po', 'bar.pot'], "Hello from d.cpp", mode='r') # scons -c does not clean anything test.run(arguments = '-c', chdir = path.join('ex1', 'po')) @@ -101,16 +101,16 @@ test.write(['ex2', 'd.cpp'], """ gettext("Hello from d.cpp") """) test.run(arguments = 'pot-update', chdir = path.join('ex2')) test.must_exist( ['ex2', 'foo.pot']) -test.must_contain( ['ex2', 'foo.pot'], "Hello from a.cpp" ) -test.must_contain( ['ex2', 'foo.pot'], "Hello from b.cpp" ) -test.must_not_contain( ['ex2', 'foo.pot'], "Hello from c.cpp" ) -test.must_not_contain( ['ex2', 'foo.pot'], "Hello from d.cpp" ) +test.must_contain( ['ex2', 'foo.pot'], "Hello from a.cpp", mode='r' ) +test.must_contain( ['ex2', 'foo.pot'], "Hello from b.cpp", mode='r' ) +test.must_not_contain( ['ex2', 'foo.pot'], "Hello from c.cpp", mode='r' ) +test.must_not_contain( ['ex2', 'foo.pot'], "Hello from d.cpp", mode='r' ) test.must_exist( ['ex2', 'bar.pot']) -test.must_not_contain( ['ex2', 'bar.pot'], "Hello from a.cpp" ) -test.must_not_contain( ['ex2', 'bar.pot'], "Hello from b.cpp" ) -test.must_contain( ['ex2', 'bar.pot'], "Hello from c.cpp" ) -test.must_contain( ['ex2', 'bar.pot'], "Hello from d.cpp" ) +test.must_not_contain( ['ex2', 'bar.pot'], "Hello from a.cpp", mode='r' ) +test.must_not_contain( ['ex2', 'bar.pot'], "Hello from b.cpp", mode='r' ) +test.must_contain( ['ex2', 'bar.pot'], "Hello from c.cpp", mode='r' ) +test.must_contain( ['ex2', 'bar.pot'], "Hello from d.cpp", mode='r' ) ############################################################################# @@ -192,9 +192,9 @@ test.write(['ex5', '0', '1', 'a.cpp'], """ gettext("Hello from ../a.cpp") """) test.run(arguments = 'pot-update', chdir = path.join('ex5', '0', '1', 'po')) test.must_exist( ['ex5', '0', '1', 'po', 'messages.pot']) test.must_contain( ['ex5', '0', '1', 'po', 'messages.pot'], - 'Hello from ../a.cpp' ) + 'Hello from ../a.cpp', mode='r' ) test.must_not_contain( ['ex5', '0', '1', 'po', 'messages.pot'], - 'Hello from ../../a.cpp' ) + 'Hello from ../../a.cpp', mode='r' ) test.write(['ex5', '0', '1', 'po', 'SConstruct'], """ @@ -204,9 +204,9 @@ env.POTUpdate(XGETTEXTFROM = 'POTFILES.in', XGETTEXTPATH=['../../', '../']) """) test.run(arguments = 'pot-update', chdir = path.join('ex5', '0', '1', 'po')) test.must_contain( ['ex5', '0', '1', 'po', 'messages.pot'], - 'Hello from ../../a.cpp' ) + 'Hello from ../../a.cpp', mode='r' ) test.must_not_contain( ['ex5', '0', '1', 'po', 'messages.pot'], - 'Hello from ../a.cpp') + 'Hello from ../a.cpp', mode='r') test.pass_test() diff --git a/test/gettext/POUpdate/UserExamples.py b/test/gettext/POUpdate/UserExamples.py index 01d3706..5f3e6c7 100644 --- a/test/gettext/POUpdate/UserExamples.py +++ b/test/gettext/POUpdate/UserExamples.py @@ -182,8 +182,8 @@ test.write(['ex1', 'pl.po'], pl_po_contents) test.run(arguments = 'po-update', chdir = 'ex1', stderr = None) test.must_exist( ['ex1', 'en.po'] ) test.must_exist( ['ex1', 'pl.po'] ) -test.must_contain( ['ex1', 'en.po'], "Hello from a.cpp" ) -test.must_contain( ['ex1', 'pl.po'], "Hello from a.cpp" ) +test.must_contain( ['ex1', 'en.po'], "Hello from a.cpp", mode='r' ) +test.must_contain( ['ex1', 'pl.po'], "Hello from a.cpp", mode='r' ) ############################################################################# # POUpdate: Example 2 @@ -205,8 +205,8 @@ test.write(['ex2', 'pl.po'], pl_po_contents) test.run(arguments = 'po-update', chdir = 'ex2', stderr = None) test.must_exist( ['ex2', 'en.po'] ) test.must_exist( ['ex2', 'pl.po'] ) -test.must_contain( ['ex2', 'en.po'], "Hello from a.cpp" ) -test.must_contain( ['ex2', 'pl.po'], "Hello from a.cpp" ) +test.must_contain( ['ex2', 'en.po'], "Hello from a.cpp", mode='r' ) +test.must_contain( ['ex2', 'pl.po'], "Hello from a.cpp", mode='r' ) ############################################################################# # POUpdate: Example 3 @@ -228,8 +228,8 @@ test.write(['ex3', 'pl.po'], pl_po_contents) test.run(arguments = 'po-update', chdir = 'ex3', stderr = None) test.must_exist( ['ex3', 'en.po'] ) test.must_exist( ['ex3', 'pl.po'] ) -test.must_contain( ['ex3', 'en.po'], "Hello from a.cpp" ) -test.must_contain( ['ex3', 'pl.po'], "Hello from a.cpp" ) +test.must_contain( ['ex3', 'en.po'], "Hello from a.cpp", mode='r' ) +test.must_contain( ['ex3', 'pl.po'], "Hello from a.cpp", mode='r' ) ############################################################################# # POUpdate: Example 4 @@ -258,8 +258,8 @@ test.run(arguments = 'po-update', chdir = 'ex4', stderr = None) test.must_exist( ['ex4', 'messages.pot'] ) test.must_exist( ['ex4', 'en.po'] ) test.must_exist( ['ex4', 'pl.po'] ) -test.must_contain( ['ex4', 'en.po'], "Hello from a.cpp" ) -test.must_contain( ['ex4', 'pl.po'], "Hello from a.cpp" ) +test.must_contain( ['ex4', 'en.po'], "Hello from a.cpp", mode='r' ) +test.must_contain( ['ex4', 'pl.po'], "Hello from a.cpp", mode='r' ) ############################################################################# # POUpdate: Example 5 @@ -286,8 +286,8 @@ test.write(['ex5', 'pl.po'], pl_po_contents) test.run(arguments = 'po-update', chdir= 'ex5', stderr = None) test.must_exist( ['ex5', 'en.po'] ) test.must_exist( ['ex5', 'pl.po'] ) -test.must_contain( ['ex5', 'en.po'], "Hello from a.cpp" ) -test.must_contain( ['ex5', 'pl.po'], "Hello from a.cpp" ) +test.must_contain( ['ex5', 'en.po'], "Hello from a.cpp", mode='r' ) +test.must_contain( ['ex5', 'pl.po'], "Hello from a.cpp", mode='r' ) ############################################################################# # POUpdate: Example 6 @@ -317,10 +317,10 @@ test.must_exist( ['ex6', 'en.po'] ) test.must_exist( ['ex6', 'pl.po'] ) test.must_exist( ['ex6', 'de.po'] ) test.must_exist( ['ex6', 'fr.po'] ) -test.must_contain( ['ex6', 'en.po'], "Hello from a.cpp" ) -test.must_contain( ['ex6', 'pl.po'], "Hello from a.cpp" ) -test.must_contain( ['ex6', 'de.po'], "Hello from a.cpp" ) -test.must_contain( ['ex6', 'fr.po'], "Hello from a.cpp" ) +test.must_contain( ['ex6', 'en.po'], "Hello from a.cpp", mode='r' ) +test.must_contain( ['ex6', 'pl.po'], "Hello from a.cpp", mode='r' ) +test.must_contain( ['ex6', 'de.po'], "Hello from a.cpp", mode='r' ) +test.must_contain( ['ex6', 'fr.po'], "Hello from a.cpp", mode='r' ) ############################################################################# # POUpdate: Example 7 @@ -351,8 +351,8 @@ test.write(['ex7', 'messages.pot'], pot_contents) test.run(arguments = 'po-update', chdir= 'ex7', stderr = None) test.must_exist( ['ex7', 'en.po'] ) test.must_exist( ['ex7', 'pl.po'] ) -test.must_contain( ['ex7', 'en.po'], "Hello from a.cpp" ) -test.must_contain( ['ex7', 'pl.po'], "Hello from a.cpp" ) +test.must_contain( ['ex7', 'en.po'], "Hello from a.cpp", mode='r' ) +test.must_contain( ['ex7', 'pl.po'], "Hello from a.cpp", mode='r' ) ############################################################################# # POUpdate: Example 8 @@ -389,8 +389,8 @@ test.run(arguments = 'po-update', chdir = 'ex8', stderr = None) test.must_exist( ['ex8', 'foo.pot'] ) test.must_exist( ['ex8', 'en.po'] ) test.must_exist( ['ex8', 'pl.po'] ) -test.must_contain( ['ex8', 'en.po'], "Hello from a.cpp" ) -test.must_contain( ['ex8', 'pl.po'], "Hello from a.cpp" ) +test.must_contain( ['ex8', 'en.po'], "Hello from a.cpp", mode='r' ) +test.must_contain( ['ex8', 'pl.po'], "Hello from a.cpp", mode='r' ) test.pass_test() diff --git a/test/gettext/Translate/UserExamples.py b/test/gettext/Translate/UserExamples.py index 384b96d..d7220a3 100644 --- a/test/gettext/Translate/UserExamples.py +++ b/test/gettext/Translate/UserExamples.py @@ -65,10 +65,10 @@ test.write(['ex1', 'b.cpp'], """ gettext("Hello from b.cpp") """) test.run(arguments = 'po-update', chdir = path.join('ex1','po'), stderr = None) test.must_exist( ['ex1', 'po', 'en.po'] ) test.must_exist( ['ex1', 'po', 'pl.po'] ) -test.must_contain( ['ex1', 'po', 'en.po'], "Hello from a.cpp" ) -test.must_contain( ['ex1', 'po', 'en.po'], "Hello from b.cpp" ) -test.must_contain( ['ex1', 'po', 'pl.po'], "Hello from a.cpp" ) -test.must_contain( ['ex1', 'po', 'pl.po'], "Hello from b.cpp" ) +test.must_contain( ['ex1', 'po', 'en.po'], "Hello from a.cpp", mode='r' ) +test.must_contain( ['ex1', 'po', 'en.po'], "Hello from b.cpp", mode='r' ) +test.must_contain( ['ex1', 'po', 'pl.po'], "Hello from a.cpp", mode='r' ) +test.must_contain( ['ex1', 'po', 'pl.po'], "Hello from b.cpp", mode='r' ) ############################################################################# # Translate: Example 2 @@ -100,10 +100,10 @@ test.write(['ex2', 'b.cpp'], """ gettext("Hello from b.cpp") """) test.run(arguments = 'po-update', chdir = path.join('ex2','po'), stderr = None) test.must_exist( ['ex2', 'po', 'en.po'] ) test.must_exist( ['ex2', 'po', 'pl.po'] ) -test.must_contain( ['ex2', 'po', 'en.po'], "Hello from a.cpp" ) -test.must_contain( ['ex2', 'po', 'en.po'], "Hello from b.cpp" ) -test.must_contain( ['ex2', 'po', 'pl.po'], "Hello from a.cpp" ) -test.must_contain( ['ex2', 'po', 'pl.po'], "Hello from b.cpp" ) +test.must_contain( ['ex2', 'po', 'en.po'], "Hello from a.cpp", mode='r' ) +test.must_contain( ['ex2', 'po', 'en.po'], "Hello from b.cpp", mode='r' ) +test.must_contain( ['ex2', 'po', 'pl.po'], "Hello from a.cpp", mode='r' ) +test.must_contain( ['ex2', 'po', 'pl.po'], "Hello from b.cpp", mode='r' ) ############################################################################# # Translate: Example 3 @@ -157,12 +157,12 @@ test.must_not_exist( ['ex3', 'build', 'po', 'messages.pot'] ) test.must_not_exist( ['ex3', 'build', 'po', 'en.po'] ) test.must_not_exist( ['ex3', 'build', 'po', 'pl.po'] ) # -test.must_contain( ['ex3', 'src', 'po', 'messages.pot'], "Hello from a.cpp" ) -test.must_contain( ['ex3', 'src', 'po', 'messages.pot'], "Hello from b.cpp" ) -test.must_contain( ['ex3', 'src', 'po', 'en.po'], "Hello from a.cpp" ) -test.must_contain( ['ex3', 'src', 'po', 'en.po'], "Hello from b.cpp" ) -test.must_contain( ['ex3', 'src', 'po', 'pl.po'], "Hello from a.cpp" ) -test.must_contain( ['ex3', 'src', 'po', 'pl.po'], "Hello from b.cpp" ) +test.must_contain( ['ex3', 'src', 'po', 'messages.pot'], "Hello from a.cpp", mode='r' ) +test.must_contain( ['ex3', 'src', 'po', 'messages.pot'], "Hello from b.cpp", mode='r' ) +test.must_contain( ['ex3', 'src', 'po', 'en.po'], "Hello from a.cpp", mode='r' ) +test.must_contain( ['ex3', 'src', 'po', 'en.po'], "Hello from b.cpp", mode='r' ) +test.must_contain( ['ex3', 'src', 'po', 'pl.po'], "Hello from a.cpp", mode='r' ) +test.must_contain( ['ex3', 'src', 'po', 'pl.po'], "Hello from b.cpp", mode='r' ) test.run(arguments = '.', chdir = 'ex3', stderr = None) test.must_exist( ['ex3', 'build', 'po', 'en.mo'] ) diff --git a/test/leaky-handles.py b/test/leaky-handles.py index 9502d1b..3787ee2 100644 --- a/test/leaky-handles.py +++ b/test/leaky-handles.py @@ -29,12 +29,13 @@ Verify that file handles aren't leaked to child processes """ import os +import sys import TestSCons test = TestSCons.TestSCons() -if os.name != 'posix': +if os.name != 'posix' or sys.platform == 'darwin': msg = "Skipping fork leak test on non-posix platform '%s'\n" % os.name test.skip_test(msg) diff --git a/test/option--max-drift.py b/test/option--max-drift.py index 08fafd1..4e063c9 100644 --- a/test/option--max-drift.py +++ b/test/option--max-drift.py @@ -109,14 +109,14 @@ atime = os.path.getatime(test.workpath('foo.in')) mtime = os.path.getmtime(test.workpath('foo.in')) test.run() -test.fail_test(test.read('foo.out') != 'foo.in\n') +test.must_match('foo.out', 'foo.in\n', mode='r') test.write('foo.in', 'foo.in delta\n') os.utime(test.workpath('foo.in'), (atime,mtime)) test.run() -test.fail_test(test.read('foo.out') != 'foo.in\n') +test.must_match('foo.out', 'foo.in\n', mode='r') test.pass_test() diff --git a/test/option--tree.py b/test/option--tree.py index a50433c..a9618d8 100644 --- a/test/option--tree.py +++ b/test/option--tree.py @@ -51,6 +51,30 @@ scons: warning: The --debug=tree option is deprecated; please use --tree=all ins """, status = 0, match=TestSCons.match_re_dotall) + +# Test that unicode characters can be printed (escaped) with the --tree option +test.write('SConstruct', +""" +env = Environment() +env.Tool("textfile") +try: + # Python 2 + write = unichr(0xe7).encode('utf-8') +except NameError: + # Python 3 + # str is utf-8 by default + write = chr(0xe7) +env.Textfile("Foo", write) +""") + +test.run(arguments = '-Q --tree=all', + stdout = """Creating 'Foo.txt' ++-. + +-Foo.txt + | +-\\xc3\\xa7 + +-SConstruct +""", + status = 0) test.pass_test() # Local Variables: diff --git a/test/option-j.py b/test/option-j.py index 3dbedc0..278fd30 100644 --- a/test/option-j.py +++ b/test/option-j.py @@ -122,7 +122,7 @@ test.fail_test(start2 < finish1) # succeeds. test.run(arguments='-j 2 out') -if sys.platform != 'win32': +if sys.platform != 'win32' and sys.version_info[0] == 2: # Test breaks on win32 when using real subprocess is not the only # package to import threading # diff --git a/test/packaging/use-builddir.py b/test/packaging/use-builddir.py index 76b9737..9ad7aa4 100644 --- a/test/packaging/use-builddir.py +++ b/test/packaging/use-builddir.py @@ -85,7 +85,7 @@ test.run(stderr = None) test.must_exist( 'libfoo-1.2.3.tar.gz' ) -os.popen( 'tar -C temp -xzf %s'%test.workpath('libfoo-1.2.3.tar.gz') ) +os.system('tar -C temp -xzf %s'%test.workpath('libfoo-1.2.3.tar.gz') ) test.must_exist( 'temp/libfoo-1.2.3/src/main.c' ) test.must_exist( 'temp/libfoo-1.2.3/SConstruct' ) diff --git a/test/redirection.py b/test/redirection.py index ffc76b5..ba35ed0 100644 --- a/test/redirection.py +++ b/test/redirection.py @@ -32,11 +32,22 @@ test = TestSCons.TestSCons() test.write('cat.py', r""" import sys +PY3K = sys.version_info >= (3, 0) + try: - input = open(sys.argv[1], 'r').read() + input = open(sys.argv[1], 'rb').read() except IndexError: - input = sys.stdin.read() -sys.stdout.write(input) + if PY3K: + source = sys.stdin.buffer + else: + source = sys.stdin + input = source.read() + +if PY3K: + sys.stdout.buffer.write(input) +else: + sys.stdout.write(input) + sys.exit(0) """) diff --git a/test/scons-time/run/option/verbose.py b/test/scons-time/run/option/verbose.py index 5fc6d64..0ef2a6c 100644 --- a/test/scons-time/run/option/verbose.py +++ b/test/scons-time/run/option/verbose.py @@ -30,7 +30,7 @@ Verify that the run -v and --verbose options display command output. import sys import re - +import os import TestSCons_time _python_ = re.escape('"' + sys.executable + '"') @@ -112,7 +112,9 @@ scons-time%(time_re)s: %(_python_)s %(scons_py)s %(scons_flags)s --profile=%(pro SCONS_LIB_DIR = %(src_engine)s SConstruct file directory: %(tmp_scons_time_foo)s scons-time%(time_re)s: cd .* -scons-time%(time_re)s: rm -rf %(tmp_scons_time)s +""" +if 'PRESERVE' not in os.environ or not os.environ['PRESERVE']: + expect += """scons-time%(time_re)s: rm -rf %(tmp_scons_time)s """ foo_tar_gz = re.escape(foo_tar_gz) |