diff options
author | Richard W <garlicbready@googlemail.com> | 2017-08-14 10:20:43 (GMT) |
---|---|---|
committer | Richard W <garlicbready@googlemail.com> | 2017-08-14 10:20:43 (GMT) |
commit | 8bfb071d974edae91cde09bd64f4a2b9673e1364 (patch) | |
tree | 16fccf1096e559ed4c3f01dd2840766c64636c5a | |
parent | a3acca060be98abf584ad0a5033bde05ecebd412 (diff) | |
parent | 90f842616029050abffdabec078fe9a1f1b1d1a9 (diff) | |
download | SCons-8bfb071d974edae91cde09bd64f4a2b9673e1364.zip SCons-8bfb071d974edae91cde09bd64f4a2b9673e1364.tar.gz SCons-8bfb071d974edae91cde09bd64f4a2b9673e1364.tar.bz2 |
Merged scons/scons into default
-rw-r--r-- | SConstruct | 110 | ||||
-rw-r--r-- | debian/changelog | 6 | ||||
-rw-r--r-- | debian/control | 2 | ||||
-rw-r--r-- | src/CHANGES.txt | 78 | ||||
-rw-r--r-- | src/engine/SCons/ActionTests.py | 6 | ||||
-rw-r--r-- | src/engine/SCons/Environment.py | 4 | ||||
-rw-r--r-- | src/engine/SCons/EnvironmentValues.py | 97 | ||||
-rw-r--r-- | src/engine/SCons/EnvironmentValuesTest.py | 16 | ||||
-rw-r--r-- | src/engine/SCons/Subst.py | 29 | ||||
-rw-r--r-- | src/engine/SCons/SubstTests.py | 25 | ||||
-rw-r--r-- | src/engine/SCons/Tool/MSCommon/common.py | 7 | ||||
-rw-r--r-- | src/engine/SCons/Tool/MSCommon/vc.py | 62 | ||||
-rw-r--r-- | src/engine/SCons/Tool/MSCommon/vs.py | 11 | ||||
-rw-r--r-- | src/engine/SCons/Tool/__init__.py | 8 | ||||
-rw-r--r-- | src/engine/SCons/Tool/gxx.py | 4 |
15 files changed, 340 insertions, 125 deletions
@@ -41,6 +41,7 @@ import re import stat import sys import tempfile +import time import bootstrap @@ -98,9 +99,13 @@ zip = whereis('zip') # date = ARGUMENTS.get('DATE') if not date: - import time date = time.strftime("%Y/%m/%d %H:%M:%S", time.localtime(time.time())) +# Datestring for debian +# Should look like: Mon, 03 Nov 2016 13:37:42 -0700 +deb_date = time.strftime("%a, %d %b %Y %H:%M:%S +0000", time.gmtime()) + + developer = ARGUMENTS.get('DEVELOPER') if not developer: for variable in ['USERNAME', 'LOGNAME', 'USER']: @@ -385,22 +390,35 @@ def SCons_revision(target, source, env): """ t = str(target[0]) s = source[0].rstr() - 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 - # copied into the tree for packaging. - contents = contents.replace('__BUILD' + '__', env['BUILD']) - contents = contents.replace('__BUILDSYS' + '__', env['BUILDSYS']) - contents = contents.replace('__COPYRIGHT' + '__', env['COPYRIGHT']) - contents = contents.replace('__DATE' + '__', env['DATE']) - contents = contents.replace('__DEVELOPER' + '__', env['DEVELOPER']) - contents = contents.replace('__FILE' + '__', str(source[0]).replace('\\', '/')) - contents = contents.replace('__MONTH_YEAR'+ '__', env['MONTH_YEAR']) - contents = contents.replace('__REVISION' + '__', env['REVISION']) - contents = contents.replace('__VERSION' + '__', env['VERSION']) - contents = contents.replace('__NULL' + '__', '') - open(t, 'w').write(contents) + + try: + 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 + # copied into the tree for packaging. + contents = contents.replace('__BUILD' + '__', env['BUILD']) + contents = contents.replace('__BUILDSYS' + '__', env['BUILDSYS']) + contents = contents.replace('__COPYRIGHT' + '__', env['COPYRIGHT']) + contents = contents.replace('__DATE' + '__', env['DATE']) + contents = contents.replace('__DEB_DATE' + '__', env['DEB_DATE']) + + contents = contents.replace('__DEVELOPER' + '__', env['DEVELOPER']) + contents = contents.replace('__FILE' + '__', str(source[0]).replace('\\', '/')) + contents = contents.replace('__MONTH_YEAR'+ '__', env['MONTH_YEAR']) + contents = contents.replace('__REVISION' + '__', env['REVISION']) + contents = contents.replace('__VERSION' + '__', env['VERSION']) + contents = contents.replace('__NULL' + '__', '') + open(t, 'w').write(contents) + except UnicodeDecodeError as e: + print("Error decoding file:%s just copying no revision edit") + with open(s, 'rb') as fp: + contents = fp.read() + open(t, 'wb').write(contents) + + os.chmod(t, os.stat(s)[0]) @@ -452,6 +470,7 @@ env = Environment( BUILDSYS = build_system, COPYRIGHT = copyright, DATE = date, + DEB_DATE = deb_date, DEVELOPER = developer, DISTDIR = os.path.join(build_dir, 'dist'), MONTH_YEAR = month_year, @@ -594,43 +613,6 @@ finally: except EnvironmentError: pass -# -# The original packaging scheme would have have required us to push -# the Python version number into the package name (python1.5-scons, -# python2.0-scons, etc.), which would have required a definition -# like the following. Leave this here in case we ever decide to do -# this in the future, but note that this would require some modification -# to src/engine/setup.py before it would really work. -# -#python2_scons = { -# 'pkg' : 'python2-' + project, -# 'src_subdir' : 'engine', -# 'inst_subdir' : os.path.join('lib', 'python2.2', 'site-packages'), -# -# 'debian_deps' : [ -# 'debian/changelog', -# 'debian/control', -# 'debian/copyright', -# 'debian/dirs', -# 'debian/docs', -# 'debian/postinst', -# 'debian/prerm', -# 'debian/rules', -# ], -# -# 'files' : [ -# 'LICENSE.txt', -# 'README.txt', -# 'setup.cfg', -# 'setup.py', -# ], -# 'filemap' : { -# 'LICENSE.txt' : '../LICENSE.txt', -# }, -# 'buildermap' : {}, -#} -# - scons_script = { 'pkg' : project + '-script', 'src_subdir' : 'script', @@ -836,6 +818,7 @@ for p in [ scons ]: builder = p['buildermap'].get(b, env.SCons_revision) x = builder(os.path.join(build, b), s) + Local(x) # @@ -944,11 +927,14 @@ for p in [ scons ]: ebuild = os.path.join(gentoo, 'scons-%s.ebuild' % version) digest = os.path.join(gentoo, 'files', 'digest-scons-%s' % version) env.Command(ebuild, os.path.join('gentoo', 'scons.ebuild.in'), SCons_revision) + def Digestify(target, source, env): - import md5 + import hashlib src = source[0].rfile() - contents = open(str(src)).read() - sig = md5.new(contents).hexdigest() + contents = open(str(src),'rb').read() + m = hashlib.md5() + m.update(contents) + sig = m.hexdigest() bytes = os.stat(str(src))[6] open(str(target[0]), 'w').write("MD5 %s %s %d\n" % (sig, src.name, @@ -1068,9 +1054,9 @@ for p in [ scons ]: # Our Debian packaging builds directly into build/dist, # so we don't need to Install() the .debs. # The built deb is called just x.y.z, not x.y.z.final.0 so strip those off: - deb_version = '.'.join(version.split('.')[0:3]) + deb_version = version #'.'.join(version.split('.')[0:3]) deb = os.path.join(build_dir, 'dist', "%s_%s_all.deb" % (pkg, deb_version)) - # print("Building deb into %s (version=%s)"%(deb, deb_version)) + print("Building deb into %s (version=%s)"%(deb, deb_version)) for d in p['debian_deps']: b = env.SCons_revision(os.path.join(build, d), d) env.Depends(deb, b) @@ -1268,7 +1254,11 @@ if sfiles: Local(src_tar_gz, src_zip) for file in sfiles: - env.SCons_revision(os.path.join(b_ps, file), file) + if file.endswith('jpg') or file.endswith('png'): + # don't revision binary files. + env.Install(os.path.dirname(os.path.join(b_ps,file)), file) + else: + env.SCons_revision(os.path.join(b_ps, file), file) b_ps_files = [os.path.join(b_ps, x) for x in sfiles] cmds = [ diff --git a/debian/changelog b/debian/changelog index 0808546..4e7470d 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,9 @@ +scons (__VERSION__) unstable; urgency=low + + * Maintenance Release + + -- __DEVELOPER__ <bill@baddogconsulting.com> __DATE__ + scons (2.5.1) unstable; urgency=low * Maintenance Release diff --git a/debian/control b/debian/control index 5fbf0eb..d0a61fa 100644 --- a/debian/control +++ b/debian/control @@ -7,7 +7,7 @@ Standards-Version: 3.5.6 Package: scons Architecture: all -Depends: python (>> 2.4) +Depends: python (>> 2.7) Description: A replacement for Make SCons is an Open Source software construction tool--that is, a build tool; an improved substitute for the classic Make utility; a better diff --git a/src/CHANGES.txt b/src/CHANGES.txt index a82a978..dd92ce1 100644 --- a/src/CHANGES.txt +++ b/src/CHANGES.txt @@ -11,17 +11,6 @@ NOTE: This is a major release. You should expect that some targets may rebuild Significant changes in some python action signatures. Also switching between PY 2 and PY 3.5, 3.6 may cause rebuilds. In no case should rebuilds not happen. - From Ibrahim Esmat: - - Added the capability to build Windows Store Compatible libraries that can be used - with Universal Windows Platform (UWP) Apps and published to the store - - From Richard West: - - Added nested / namespace tool support - - Added a small fix to the python3 tool loader when loading a tool as a package - - Added additional documentation to the user manual on using toolpaths with the environment - This includes the use of sys.path to search for tools installed via pip or package managers - - Added support for a PyPackageDir function for use with the toolpath - From William Blevins: - Updated D language scanner support to latest: 2.071.1. (PR #1924) https://dlang.org/spec/module.html accessed 11 August 2016 @@ -46,6 +35,10 @@ may cause rebuilds. In no case should rebuilds not happen. - Changes to Action Function and Action Class signiture creation. NOTE: This will cause rebuilds for many builds when upgrading to SCons 3.0 + From Ibrahim Esmat: + - Added the capability to build Windows Store Compatible libraries that can be used + with Universal Windows Platform (UWP) Apps and published to the store + From Daniel Holth: - Add basic support for PyPy (by deleting __slots__ from Node with a metaclass on PyPy); wrap most-used open() calls in 'with' statements to @@ -62,6 +55,7 @@ may cause rebuilds. In no case should rebuilds not happen. From Alexey Klimkin: - Use memoization to optimize PATH evaluation across all dependencies per node. (PR #345) + - Use set() where it is applicable (PR #344) From M. Limber: - Fixed msvs.py for Visual Studio Express editions that would report @@ -70,6 +64,13 @@ may cause rebuilds. In no case should rebuilds not happen. From Rick Lupton: - Update LaTeX scanner to understand \import and related commands + From Steve Robinson: + - Add support for Visual Studio 2017. This support requires vswhere.exe a helper + tool installed with newer installs of 2017. SCons expects it to be located at + "C:\Program Files (x86)\Microsoft Visual Studio\Installer\vswhere.exe" + It can be downloaded separately at + https://github.com/Microsoft/vswhere + From Paweł Tomulik: - Fixed the issue with LDMODULEVERSIONFLAGS reported by Tim Jennes (https://pairlist4.pair.net/pipermail/scons-users/2016-May/004893.html). @@ -88,6 +89,14 @@ may cause rebuilds. In no case should rebuilds not happen. - Fixed PCHPDBFLAGS causing a deprecation warning on MSVC v8 and later when using PCHs and PDBs together. + + From Richard West: + - Added nested / namespace tool support + - Added a small fix to the python3 tool loader when loading a tool as a package + - Added additional documentation to the user manual on using toolpaths with the environment + This includes the use of sys.path to search for tools installed via pip or package managers + - Added support for a PyPackageDir function for use with the toolpath + From Russel Winder: - Reordered the default D tools from "dmd, gdc, ldc" to "dmd, ldc, gdc". - Add a ProgramAllAtOnce builder to the dmd, ldc, and gdc tools. (PR #448) @@ -97,6 +106,9 @@ may cause rebuilds. In no case should rebuilds not happen. tools, must now include the ar tool to get this builder as is required for other compiler tools. - Add clang and clang++ tools based on Paweł Tomulik's work. + + From Tom Tanner: + - Allow nested $( ... $) sections RELEASE 2.5.1 - Mon, 03 Nov 2016 13:37:42 -0400 @@ -333,35 +345,40 @@ RELEASE 2.3.3 - Sun, 24 Aug 2014 21:08:33 -0400 RELEASE 2.3.2 + From Dirk Baechle: + - Update XML doc editor configuration + - Fix: Allow varlist to be specified as list of strings for Actions (#2754) + From veon on bitbucket: - Fixed handling of nested ifs in CPP scanner PreProcessor class. + From Shane Gannon: + - Support for Visual Studio 2013 (12.0) + From Michael Haubenwallner: - Respect user's CC/CXX values; don't always overwrite in generate() - Delegate linker Tool.exists() to CC/CXX Tool.exists(). + From Rob Managan: + - Updated the TeX builder to support use of the -synctex=1 + option and the files it creates. + - Updated the TeX builder to correctly clean auxiliary files when + the biblatex package is used. + + From Gary Oberbrunner: + - get default RPM architecture more robustly when building RPMs + From Amir Szekely: - Fixed NoClean() for multi-target builders (#2353). + From Paweł Tomulik: + - Fix SConf tests that write output + From Russel Winder: - Revamp of the D language support. Tools for DMD, GDC and LDC provided and integrated with the C and C++ linking. NOTE: This is only tested with D v2. Support for D v1 is now deprecated. - From Paweł Tomulik: - - Fix SConf tests that write output - ->>>>>>> other - From Gary Oberbrunner: - - get default RPM architecture more robustly when building RPMs - - From Shane Gannon: - - Support for Visual Studio 2013 (12.0) - - From Sye van der Veen: - - Support for Visual Studio 12.0Exp, and fixes for earlier MSVS - versions. - From Anatoly Techtonik: - Several improvements for running scons.py from source: * engine files form source directory take priority over all other @@ -376,15 +393,10 @@ RELEASE 2.3.2 SCons initialization (it will still be possible to use these tools explicitly) - From Dirk Baechle: - - Update XML doc editor configuration - - Fix: Allow varlist to be specified as list of strings for Actions (#2754) + From Sye van der Veen: + - Support for Visual Studio 12.0Exp, and fixes for earlier MSVS + versions. - From Rob Managan: - - Updated the TeX builder to support use of the -synctex=1 - option and the files it creates. - - Updated the TeX builder to correctly clean auxiliary files when - the biblatex package is used. RELEASE 2.3.1 diff --git a/src/engine/SCons/ActionTests.py b/src/engine/SCons/ActionTests.py index 9d856c9..2398c10 100644 --- a/src/engine/SCons/ActionTests.py +++ b/src/engine/SCons/ActionTests.py @@ -1241,8 +1241,8 @@ class CommandActionTestCase(unittest.TestCase): (env["foo"], env["bar"]) # The number 1 is there to make sure all args get converted to strings. - a = SCons.Action.CommandAction(["|", "$(", "$foo", "|", "$bar", - "$)", "|", "$baz", 1]) + a = SCons.Action.CommandAction(["|", "$(", "$foo", "|", "$(", "$bar", + "$)", "stuff", "$)", "|", "$baz", 1]) c = a.get_contents(target=[], source=[], env=Environment(foo = 'FFF', bar = 'BBB', baz = CmdGen)) @@ -1257,7 +1257,7 @@ class CommandActionTestCase(unittest.TestCase): c = a.get_contents(target=DummyNode('ttt'), source = DummyNode('sss'), env=SpecialEnvironment(foo = 'GGG', bar = 'CCC', baz = 'ZZZ')) - assert c == b'subst_target_source: | $( $foo | $bar $) | $baz 1', c + assert c == b'subst_target_source: | $( $foo | $( $bar $) stuff $) | $baz 1', c # We've discussed using the real target and source names in a # CommandAction's signature contents. This would have have the diff --git a/src/engine/SCons/Environment.py b/src/engine/SCons/Environment.py index 480a1d6..4f8e41b 100644 --- a/src/engine/SCons/Environment.py +++ b/src/engine/SCons/Environment.py @@ -167,7 +167,7 @@ def _set_SCANNERS(env, key, value): def _delete_duplicates(l, keep_last): """Delete duplicates from a sequence, keeping the first or last.""" - seen={} + seen=set() result=[] if keep_last: # reverse in & out, then keep first l.reverse() @@ -175,7 +175,7 @@ def _delete_duplicates(l, keep_last): try: if i not in seen: result.append(i) - seen[i]=1 + seen.add(i) except TypeError: # probably unhashable. Just keep it. result.append(i) diff --git a/src/engine/SCons/EnvironmentValues.py b/src/engine/SCons/EnvironmentValues.py new file mode 100644 index 0000000..e2efccb --- /dev/null +++ b/src/engine/SCons/EnvironmentValues.py @@ -0,0 +1,97 @@ +import re + +_is_valid_var = re.compile(r'[_a-zA-Z]\w*$') + +_rm = re.compile(r'\$[()]') +_remove = re.compile(r'\$\([^\$]*(\$[^\)][^\$]*)*\$\)') + +# Regular expressions for splitting strings and handling substitutions, +# for use by the scons_subst() and scons_subst_list() functions: +# +# The first expression compiled matches all of the $-introduced tokens +# that we need to process in some way, and is used for substitutions. +# The expressions it matches are: +# +# "$$" +# "$(" +# "$)" +# "$variable" [must begin with alphabetic or underscore] +# "${any stuff}" +# +# The second expression compiled is used for splitting strings into tokens +# to be processed, and it matches all of the tokens listed above, plus +# the following that affect how arguments do or don't get joined together: +# +# " " [white space] +# "non-white-space" [without any dollar signs] +# "$" [single dollar sign] +# +_dollar_exps_str = r'\$[\$\(\)]|\$[_a-zA-Z][\.\w]*|\${[^}]*}' +_dollar_exps = re.compile(r'(%s)' % _dollar_exps_str) +_separate_args = re.compile(r'(%s|\s+|[^\s\$]+|\$)' % _dollar_exps_str) + +# This regular expression is used to replace strings of multiple white +# space characters in the string result from the scons_subst() function. +_space_sep = re.compile(r'[\t ]+(?![^{]*})') + +class ValueTypes(object): + """ + Enum to store what type of value the variable holds. + """ + UNKNOWN = 0 + STRING = 1 + CALLABLE = 2 + VARIABLE = 3 + + +class EnvironmentValue(object): + """ + Hold a single value. We're going to cache parsed version of the file + We're going to keep track of variables which feed into this values evaluation + """ + def __init__(self, value): + self.value = value + self.var_type = ValueTypes.UNKNOWN + + if callable(self.value): + self.var_type = ValueTypes.CALLABLE + else: + self.parse_value() + + + def parse_value(self): + """ + Scan the string and break into component values + """ + + try: + if '$' not in self.value: + self._parsed = self.value + self.var_type = ValueTypes.STRING + else: + # Now we need to parse the specified string + result = _dollar_exps.sub(sub_match, args) + print(result) + pass + except TypeError: + # likely callable? either way we don't parse + self._parsed = self.value + + def parse_trial(self): + """ + Try alternate parsing methods. + :return: + """ + parts = [] + for c in self.value: + + + +class EnvironmentValues(object): + """ + A class to hold all the environment variables + """ + def __init__(self, **kw): + self._dict = {} + for k in kw: + self._dict[k] = EnvironmentValue(kw[k]) diff --git a/src/engine/SCons/EnvironmentValuesTest.py b/src/engine/SCons/EnvironmentValuesTest.py new file mode 100644 index 0000000..58ee9cf --- /dev/null +++ b/src/engine/SCons/EnvironmentValuesTest.py @@ -0,0 +1,16 @@ +import unittest + +from SCons.EnvironmentValues import EnvironmentValues + +class MyTestCase(unittest.TestCase): + def test_simple_environmentValues(self): + """Test comparing SubstitutionEnvironments + """ + + env1 = EnvironmentValues(XXX='x') + env2 = EnvironmentValues(XXX='x',XX="$X", X1="${X}", X2="$($X$)") + + + +if __name__ == '__main__': + unittest.main() diff --git a/src/engine/SCons/Subst.py b/src/engine/SCons/Subst.py index 9aa4bbc..a68b54d 100644 --- a/src/engine/SCons/Subst.py +++ b/src/engine/SCons/Subst.py @@ -338,24 +338,28 @@ SUBST_RAW = 1 SUBST_SIG = 2 _rm = re.compile(r'\$[()]') -_remove = re.compile(r'\$\([^\$]*(\$[^\)][^\$]*)*\$\)') +_rm_split = re.compile(r'(\$[()])') # Indexed by the SUBST_* constants above. -_regex_remove = [ _rm, None, _remove ] +_regex_remove = [ _rm, None, _rm_split ] def _rm_list(list): return [l for l in list if not l in ('$(', '$)')] def _remove_list(list): result = [] - do_append = result.append + depth = 0 for l in list: if l == '$(': - do_append = lambda x: None + depth += 1 elif l == '$)': - do_append = result.append - else: - do_append(l) + depth -= 1 + if depth < 0: + break + elif depth == 0: + result.append(l) + if depth != 0: + return None return result # Indexed by the SUBST_* constants above. @@ -562,12 +566,19 @@ def scons_subst(strSubst, env, mode=SUBST_RAW, target=None, source=None, gvars={ except KeyError: pass + res = result if is_String(result): # Remove $(-$) pairs and any stuff in between, # if that's appropriate. remove = _regex_remove[mode] if remove: - result = remove.sub('', result) + if mode == SUBST_SIG: + result = _list_remove[mode](remove.split(result)) + if result is None: + raise SCons.Errors.UserError("Unbalanced $(/$) in: " + res) + result = ' '.join(result) + else: + result = remove.sub('', result) if mode != SUBST_RAW: # Compress strings of white space characters into # a single space. @@ -576,6 +587,8 @@ def scons_subst(strSubst, env, mode=SUBST_RAW, target=None, source=None, gvars={ remove = _list_remove[mode] if remove: result = remove(result) + if result is None: + raise SCons.Errors.UserError("Unbalanced $(/$) in: " + str(res)) return result diff --git a/src/engine/SCons/SubstTests.py b/src/engine/SCons/SubstTests.py index c11f247..6604128 100644 --- a/src/engine/SCons/SubstTests.py +++ b/src/engine/SCons/SubstTests.py @@ -183,6 +183,9 @@ class SubstTestCase(unittest.TestCase): 'HHH' : 'III', 'FFFIII' : 'BADNEWS', + 'THING1' : "$(STUFF$)", + 'THING2' : "$THING1", + 'LITERAL' : TestLiteral("$XXX"), # Test that we can expand to and return a function. @@ -405,6 +408,11 @@ class scons_subst_TestCase(SubstTestCase): "test", "test", + "test $( $THING2 $)", + "test $( $(STUFF$) $)", + "test STUFF", + "test", + "$AAA ${AAA}A $BBBB $BBB", "a aA b", "a aA b", @@ -544,6 +552,23 @@ class scons_subst_TestCase(SubstTestCase): else: raise AssertionError("did not catch expected UserError") + def test_subst_balance_errors(self): + """Test scons_subst(): handling syntax errors""" + env = DummyEnv(self.loc) + try: + scons_subst('$(', env, mode=SUBST_SIG) + except SCons.Errors.UserError as e: + assert str(e) == "Unbalanced $(/$) in: $(", str(e) + else: + raise AssertionError("did not catch expected UserError") + + try: + scons_subst('$)', env, mode=SUBST_SIG) + except SCons.Errors.UserError as e: + assert str(e) == "Unbalanced $(/$) in: $)", str(e) + else: + raise AssertionError("did not catch expected UserError") + def test_subst_type_errors(self): """Test scons_subst(): handling type errors""" env = DummyEnv(self.loc) diff --git a/src/engine/SCons/Tool/MSCommon/common.py b/src/engine/SCons/Tool/MSCommon/common.py index a846cfc..b60cd5b 100644 --- a/src/engine/SCons/Tool/MSCommon/common.py +++ b/src/engine/SCons/Tool/MSCommon/common.py @@ -130,6 +130,13 @@ def normalize_env(env, keys, force=False): if sys32_dir not in normenv['PATH']: normenv['PATH'] = normenv['PATH'] + os.pathsep + sys32_dir + # Without Wbem in PATH, vcvarsall.bat has a "'wmic' is not recognized" + # error starting with Visual Studio 2017, although the script still + # seems to work anyway. + sys32_wbem_dir = os.path.join(sys32_dir, 'Wbem') + if sys32_wbem_dir not in normenv['PATH']: + normenv['PATH'] = normenv['PATH'] + os.pathsep + sys32_wbem_dir + debug("PATH: %s"%normenv['PATH']) return normenv diff --git a/src/engine/SCons/Tool/MSCommon/vc.py b/src/engine/SCons/Tool/MSCommon/vc.py index 4126314..7c9eab5 100644 --- a/src/engine/SCons/Tool/MSCommon/vc.py +++ b/src/engine/SCons/Tool/MSCommon/vc.py @@ -37,6 +37,7 @@ __doc__ = """Module for Visual C/C++ detection and configuration. import SCons.compat import SCons.Util +import subprocess import os import platform from string import digits as string_digits @@ -135,9 +136,11 @@ def get_host_target(env): # If you update this, update SupportedVSList in Tool/MSCommon/vs.py, and the # MSVC_VERSION documentation in Tool/msvc.xml. -_VCVER = ["14.0", "14.0Exp", "12.0", "12.0Exp", "11.0", "11.0Exp", "10.0", "10.0Exp", "9.0", "9.0Exp","8.0", "8.0Exp","7.1", "7.0", "6.0"] +_VCVER = ["14.1", "14.0", "14.0Exp", "12.0", "12.0Exp", "11.0", "11.0Exp", "10.0", "10.0Exp", "9.0", "9.0Exp","8.0", "8.0Exp","7.1", "7.0", "6.0"] _VCVER_TO_PRODUCT_DIR = { + '14.1' : [ + (SCons.Util.HKEY_LOCAL_MACHINE, r'')], # Visual Studio 2017 doesn't set this registry key anymore '14.0' : [ (SCons.Util.HKEY_LOCAL_MACHINE, r'Microsoft\VisualStudio\14.0\Setup\VC\ProductDir')], '14.0Exp' : [ @@ -222,6 +225,33 @@ def is_host_target_supported(host_target, msvc_version): return True + +def find_vc_pdir_vswhere(msvc_version): + """ + Find the MSVC product directory using vswhere.exe . + Run it asking for specified version and get MSVS install location + :param msvc_version: + :return: MSVC install dir + """ + vswhere_path = os.path.join( + 'C:\\', + 'Program Files (x86)', + 'Microsoft Visual Studio', + 'Installer', + 'vswhere.exe' + ) + vswhere_cmd = [vswhere_path, '-version', msvc_version, '-property', 'installationPath'] + + if os.path.exists(vswhere_path): + sp = subprocess.Popen(vswhere_cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + vsdir, err = sp.communicate() + vc_pdir = os.path.join(vsdir.rstrip(), 'VC') + return vc_pdir + else: + # No vswhere on system, no install info available + return None + + def find_vc_pdir(msvc_version): """Try to find the product directory for the given version. @@ -240,16 +270,22 @@ def find_vc_pdir(msvc_version): for hkroot, key in hkeys: try: comps = None - if common.is_win64(): - try: - # ordinally at win64, try Wow6432Node first. - comps = common.read_reg(root + 'Wow6432Node\\' + key, hkroot) - except SCons.Util.WinError as e: - # at Microsoft Visual Studio for Python 2.7, value is not in Wow6432Node - pass - if not comps: - # not Win64, or Microsoft Visual Studio for Python 2.7 - comps = common.read_reg(root + key, hkroot) + if not key: + comps = find_vc_pdir_vswhere(msvc_version) + if not comps: + debug('find_vc_dir(): no VC found via vswhere for version {}'.format(repr(key))) + raise SCons.Util.WinError + else: + if common.is_win64(): + try: + # ordinally at win64, try Wow6432Node first. + comps = common.read_reg(root + 'Wow6432Node\\' + key, hkroot) + except SCons.Util.WinError as e: + # at Microsoft Visual Studio for Python 2.7, value is not in Wow6432Node + pass + if not comps: + # not Win64, or Microsoft Visual Studio for Python 2.7 + comps = common.read_reg(root + key, hkroot) except SCons.Util.WinError as e: debug('find_vc_dir(): no VC registry key {}'.format(repr(key))) else: @@ -281,8 +317,10 @@ def find_batch_file(env,msvc_version,host_arch,target_arch): elif vernum < 7: pdir = os.path.join(pdir, "Bin") batfilename = os.path.join(pdir, "vcvars32.bat") - else: # >= 8 + elif 8 <= vernum <= 14: batfilename = os.path.join(pdir, "vcvarsall.bat") + else: # vernum >= 14.1 VS2017 and above + batfilename = os.path.join(pdir, "Auxiliary", "Build", "vcvarsall.bat") if not os.path.exists(batfilename): debug("Not found: %s" % batfilename) diff --git a/src/engine/SCons/Tool/MSCommon/vs.py b/src/engine/SCons/Tool/MSCommon/vs.py index cff05d5..f9382fb 100644 --- a/src/engine/SCons/Tool/MSCommon/vs.py +++ b/src/engine/SCons/Tool/MSCommon/vs.py @@ -199,6 +199,17 @@ class VisualStudio(object): # Tool/MSCommon/vc.py, and the MSVC_VERSION documentation in Tool/msvc.xml. SupportedVSList = [ + # Visual Studio 2017 + VisualStudio('14.1', + vc_version='14.1', + sdk_version='10.0A', + hkeys=[], + common_tools_var='VS150COMNTOOLS', + executable_path=r'Common7\IDE\devenv.com', + batch_file_path=r'VC\Auxiliary\Build\vsvars32.bat', + supported_arch=['x86', 'amd64', "arm"], + ), + # Visual Studio 2015 VisualStudio('14.0', vc_version='14.0', diff --git a/src/engine/SCons/Tool/__init__.py b/src/engine/SCons/Tool/__init__.py index 1ab43e7..6408f8b 100644 --- a/src/engine/SCons/Tool/__init__.py +++ b/src/engine/SCons/Tool/__init__.py @@ -1172,12 +1172,12 @@ def tool_list(platform, env): ars = ['ar', 'mslib'] else: "prefer GNU tools on all other platforms" - linkers = ['gnulink', 'mslink', 'ilink'] - c_compilers = ['gcc', 'msvc', 'intelc', 'icc', 'cc'] - cxx_compilers = ['g++', 'msvc', 'intelc', 'icc', 'cxx'] + linkers = ['gnulink', 'ilink'] + c_compilers = ['gcc', 'intelc', 'icc', 'cc'] + cxx_compilers = ['g++', 'intelc', 'icc', 'cxx'] assemblers = ['gas', 'nasm', 'masm'] fortran_compilers = ['gfortran', 'g77', 'ifort', 'ifl', 'f95', 'f90', 'f77'] - ars = ['ar', 'mslib'] + ars = ['ar',] if not str(platform) == 'win32': other_plat_tools += ['m4', 'rpm'] diff --git a/src/engine/SCons/Tool/gxx.py b/src/engine/SCons/Tool/gxx.py index c5eb579..574fd8e 100644 --- a/src/engine/SCons/Tool/gxx.py +++ b/src/engine/SCons/Tool/gxx.py @@ -41,7 +41,7 @@ import SCons.Tool import SCons.Util from . import gcc -cplusplus = __import__(__package__+'.c++', globals(), locals(), ['*']) +from . import cxx compilers = ['g++'] @@ -52,7 +52,7 @@ def generate(env): if 'CXX' not in env: env['CXX'] = env.Detect(compilers) or compilers[0] - cplusplus.generate(env) + cxx.generate(env) # platform specific settings if env['PLATFORM'] == 'aix': |