From d56a0181a1f095fcb01dd4d8039c6bb2d14d92cb Mon Sep 17 00:00:00 2001 From: James Benton Date: Mon, 6 Apr 2020 14:28:58 +0100 Subject: Add support for passing cppflags through to Visual Studio project generation. These flags can affect intellisense, for example if you are using C++17 you will have intellisense errors unless you set the appropriate CFLAGS to inform inform intellisense of the C++ version you are using. Additionally /Zc:__cplusplus is required for intellisense to recognise a provided /std:c++ switch, as it is not a usual build flag we must append it to the flags when we see /std:c++. For more information see the page on /Zc:__cplusplus: https://docs.microsoft.com/en-us/cpp/build/reference/zc-cplusplus --- src/CHANGES.txt | 8 ++++++ src/engine/SCons/Tool/msvs.py | 19 +++++++++++-- src/engine/SCons/Tool/msvs.xml | 19 +++++++++++++ src/engine/SCons/Tool/msvsTests.py | 57 +++++++++++++++++++++----------------- 4 files changed, 76 insertions(+), 27 deletions(-) diff --git a/src/CHANGES.txt b/src/CHANGES.txt index 643de92..6500a0f 100755 --- a/src/CHANGES.txt +++ b/src/CHANGES.txt @@ -115,6 +115,14 @@ RELEASE VERSION/DATE TO BE FILLED IN LATER - Docbook builder provides a fallback if lxml fails to generate a document with tostring(). + From James Benton: + - Improve Visual Studio solution/project generation code to add support + for a per-variant cppflags. Intellisense can be affected by cppflags, + this is especially important when it comes to /std:c++* which specifies + what C++ standard version to target. SCons will append /Zc:__cplusplus + to the project's cppflags when a /std:c++* flag is found as this is + required for intellisense to use the C++ standard version from cppflags. + RELEASE 3.1.2 - Mon, 17 Dec 2019 02:06:27 +0000 diff --git a/src/engine/SCons/Tool/msvs.py b/src/engine/SCons/Tool/msvs.py index 7332b49..376d814 100644 --- a/src/engine/SCons/Tool/msvs.py +++ b/src/engine/SCons/Tool/msvs.py @@ -76,6 +76,13 @@ def processIncludes(includes, env, target, source): return [env.Dir(i).abspath for i in SCons.PathList.PathList(includes).subst_path(env, target, source)] +def processFlags(flags, env): + # If /std:c++XX is in flags then we need to ensure /Zc:__cplusplus is in + # flags to tell intellisense to respect our specified standard + if any(f.startswith('/std:c++') for f in flags) and \ + not any(f == '/Zc:__cplusplus' for f in flags): + flags.append('/Zc:__cplusplus') + return [env.subst(f) for f in flags] external_makefile_guid = '{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}' @@ -508,6 +515,10 @@ class _DSPGenerator(object): cpppaths = GetKeyFromEnv(env, 'cpppaths', variants) else: cpppaths = [env.get('CPPPATH', [])] * len(variants) + if 'cppflags' in env: + cppflags = GetKeyFromEnv(env, 'cppflags', variants) + else: + cppflags = [env.get('CCFLAGS', []) + env.get('CXXFLAGS', []) + env.get('CPPFLAGS', [])] * len(variants) self.env = env @@ -550,12 +561,13 @@ class _DSPGenerator(object): for n in sourcenames: self.sources[n].sort(key=lambda a: a.lower()) - def AddConfig(self, variant, buildtarget, outdir, runfile, cmdargs, cppdefines, cpppaths, dspfile=dspfile, env=env): + def AddConfig(self, variant, buildtarget, outdir, runfile, cmdargs, cppdefines, cpppaths, cppflags, dspfile=dspfile, env=env): config = Config() config.buildtarget = buildtarget config.outdir = outdir config.cmdargs = cmdargs config.cppdefines = cppdefines + config.cppflags = cppflags config.runfile = runfile # Dir objects can't be pickled, so we need an absolute path here. @@ -573,7 +585,7 @@ class _DSPGenerator(object): print("Adding '" + self.name + ' - ' + config.variant + '|' + config.platform + "' to '" + str(dspfile) + "'") for i in range(len(variants)): - AddConfig(self, variants[i], buildtarget[i], outdir[i], runfile[i], cmdargs[i], cppdefines[i], cpppaths[i]) + AddConfig(self, variants[i], buildtarget[i], outdir[i], runfile[i], cmdargs[i], cppdefines[i], cpppaths[i], cppflags[i]) seen = set() self.platforms = [p.platform for p in self.configs.values() @@ -1144,6 +1156,7 @@ V10DSPCommandLine = """\ \t\t$(NMakeForcedIncludes) \t\t$(NMakeAssemblySearchPath) \t\t$(NMakeForcedUsingAssemblies) +\t\t%(additionaloptions)s """ V15DSPHeader = """\ @@ -1243,6 +1256,7 @@ class _GenerateV10DSP(_DSPGenerator, _GenerateV10User): cmdargs = self.configs[kind].cmdargs cpppaths = self.configs[kind].cpppaths cppdefines = self.configs[kind].cppdefines + cppflags = self.configs[kind].cppflags env_has_buildtarget = 'MSVSBUILDTARGET' in self.env if not env_has_buildtarget: @@ -1262,6 +1276,7 @@ class _GenerateV10DSP(_DSPGenerator, _GenerateV10User): # assumes they don't. preprocdefs = xmlify(';'.join(processDefines(cppdefines))) includepath = xmlify(';'.join(processIncludes(cpppaths, self.env, None, None))) + additionaloptions = xmlify(' '.join(processFlags(cppflags, self.env))) if not env_has_buildtarget: del self.env['MSVSBUILDTARGET'] diff --git a/src/engine/SCons/Tool/msvs.xml b/src/engine/SCons/Tool/msvs.xml index f6c9a39..e6aa511 100644 --- a/src/engine/SCons/Tool/msvs.xml +++ b/src/engine/SCons/Tool/msvs.xml @@ -145,6 +145,25 @@ See its __doc__ string for a discussion of the format. + cppflags + + + Compiler flags for the different variants. + If a /std:c++ flag is found then /Zc:__cplusplus is + appended to the flags if not already found, this + ensures that intellisense uses the /std:c++ switch. + The number of cppflags entries + must match the number of variant + entries, or be empty (not specified). If you give + only one, it will automatically be propagated to all + variants. If you don't give this parameter, SCons + will combine the invoking environment's + CCFLAGS, CXXFLAGS, + CPPFLAGS entries for all variants. + + + + cpppaths diff --git a/src/engine/SCons/Tool/msvsTests.py b/src/engine/SCons/Tool/msvsTests.py index 1bacc2c..e1f098f 100644 --- a/src/engine/SCons/Tool/msvsTests.py +++ b/src/engine/SCons/Tool/msvsTests.py @@ -677,6 +677,9 @@ class msvsTestCase(unittest.TestCase): list_cppdefines = [['_A', '_B', 'C'], ['_B', '_C_'], ['D'], []] list_cpppaths = [[r'C:\test1'], [r'C:\test1;C:\test2'], [self.fs.Dir('subdir')], []] + list_cppflags = [['/std:c++17', '/Zc:__cplusplus'], + ['/std:c++14', '/Zc:__cplusplus'], + ['/std:c++11', '/Zc:__cplusplus'], []] def TestParamsFromList(test_variant, test_list): """ @@ -716,36 +719,39 @@ class msvsTestCase(unittest.TestCase): tests_cmdargs = TestParamsFromList(list_variant, list_cmdargs) tests_cppdefines = TestParamsFromList(list_variant, list_cppdefines) tests_cpppaths = TestParamsFromList(list_variant, list_cpppaths) + tests_cppflags = TestParamsFromList(list_variant, list_cppflags) # Run the test for each test case for param_cmdargs, expected_cmdargs in tests_cmdargs: for param_cppdefines, expected_cppdefines in tests_cppdefines: for param_cpppaths, expected_cpppaths in tests_cpppaths: - debug('Testing %s. with :\n variant = %s \n cmdargs = "%s" \n cppdefines = "%s" \n cpppaths = "%s"' % \ - (str_function_test, list_variant, param_cmdargs, param_cppdefines, param_cpppaths)) - param_configs = [] - expected_configs = {} - for platform in ['Win32', 'x64']: - for variant in ['Debug', 'Release']: - variant_platform = '%s|%s' % (variant, platform) - runfile = '%s\\%s\\test.exe' % (platform, variant) - buildtarget = '%s\\%s\\test.exe' % (platform, variant) - outdir = '%s\\%s' % (platform, variant) - - # Create parameter list for this variant_platform - param_configs.append([variant_platform, runfile, buildtarget, outdir]) - - # Create expected dictionary result for this variant_platform - expected_configs[variant_platform] = { - 'variant': variant, - 'platform': platform, - 'runfile': runfile, - 'buildtarget': buildtarget, - 'outdir': outdir, - 'cmdargs': expected_cmdargs[variant_platform], - 'cppdefines': expected_cppdefines[variant_platform], - 'cpppaths': expected_cpppaths[variant_platform], - } + for param_cppflags, expected_cppflags in tests_cppflags: + print('Testing %s. with :\n variant = %s \n cmdargs = "%s" \n cppdefines = "%s" \n cpppaths = "%s" \n cppflags = "%s"' % \ + (str_function_test, list_variant, param_cmdargs, param_cppdefines, param_cpppaths, param_cppflags)) + param_configs = [] + expected_configs = {} + for platform in ['Win32', 'x64']: + for variant in ['Debug', 'Release']: + variant_platform = '%s|%s' % (variant, platform) + runfile = '%s\\%s\\test.exe' % (platform, variant) + buildtarget = '%s\\%s\\test.exe' % (platform, variant) + outdir = '%s\\%s' % (platform, variant) + + # Create parameter list for this variant_platform + param_configs.append([variant_platform, runfile, buildtarget, outdir]) + + # Create expected dictionary result for this variant_platform + expected_configs[variant_platform] = { + 'variant': variant, + 'platform': platform, + 'runfile': runfile, + 'buildtarget': buildtarget, + 'outdir': outdir, + 'cmdargs': expected_cmdargs[variant_platform], + 'cppdefines': expected_cppdefines[variant_platform], + 'cpppaths': expected_cpppaths[variant_platform], + 'cppflags': expected_cppflags[variant_platform], + } # Create parameter environment with final parameter dictionary param_dict = dict(list(zip(('variant', 'runfile', 'buildtarget', 'outdir'), @@ -753,6 +759,7 @@ class msvsTestCase(unittest.TestCase): param_dict['cmdargs'] = param_cmdargs param_dict['cppdefines'] = param_cppdefines param_dict['cpppaths'] = param_cpppaths + param_dict['cppflags'] = param_cppflags # Hack to be able to run the test with a 'DummyEnv' class _DummyEnv(DummyEnv): -- cgit v0.12