Skip to content

Commit eb296f6

Browse files
refackdpranke
authored andcommitted
[win] Add support for MS VS2017 (via Registry)
1) Be aware of Microsoft Visual Studio 2017 (ver 15.0 / toolset v141) 2) Try to detect from registry (not official but working) 3) Add compatible_sdks attribute to Version for setup and early fail 4) Add GYP_BUILD_TOOL env var for easy testing BUG=683729 BUG=700524 Change-Id: I2f65d2bc393e00dae2baa9ee04a828ba1ad28476 Reviewed-on: https://chromium-review.googlesource.com/453201 Reviewed-by: Bruce Dawson <[email protected]>
1 parent 95da766 commit eb296f6

4 files changed

Lines changed: 130 additions & 42 deletions

File tree

AUTHORS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,3 +12,4 @@ David J. Sankel <[email protected]>
1212
Eric N. Vander Weele <[email protected]>
1313
Tom Freudenberg <[email protected]>
1414
Julien Brianceau <[email protected]>
15+
Refael Ackermann <[email protected]>

pylib/gyp/MSVSVersion.py

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ class VisualStudioVersion(object):
1818

1919
def __init__(self, short_name, description,
2020
solution_version, project_version, flat_sln, uses_vcxproj,
21-
path, sdk_based, default_toolset=None):
21+
path, sdk_based, default_toolset=None, compatible_sdks=None):
2222
self.short_name = short_name
2323
self.description = description
2424
self.solution_version = solution_version
@@ -28,6 +28,9 @@ def __init__(self, short_name, description,
2828
self.path = path
2929
self.sdk_based = sdk_based
3030
self.default_toolset = default_toolset
31+
compatible_sdks = compatible_sdks or []
32+
compatible_sdks.sort(key=lambda v: float(v.replace('v', '')), reverse=True)
33+
self.compatible_sdks = compatible_sdks
3134

3235
def ShortName(self):
3336
return self.short_name
@@ -236,6 +239,16 @@ def _CreateVersion(name, path, sdk_based=False):
236239
if path:
237240
path = os.path.normpath(path)
238241
versions = {
242+
'2017': VisualStudioVersion('2017',
243+
'Visual Studio 2017',
244+
solution_version='12.00',
245+
project_version='15.0',
246+
flat_sln=False,
247+
uses_vcxproj=True,
248+
path=path,
249+
sdk_based=sdk_based,
250+
default_toolset='v141',
251+
compatible_sdks=['v8.1', 'v10.0']),
239252
'2015': VisualStudioVersion('2015',
240253
'Visual Studio 2015',
241254
solution_version='12.00',
@@ -348,14 +361,14 @@ def _DetectVisualStudioVersions(versions_to_check, force_express):
348361
A list of visual studio versions installed in descending order of
349362
usage preference.
350363
Base this on the registry and a quick check if devenv.exe exists.
351-
Only versions 8-10 are considered.
352364
Possibilities are:
353365
2005(e) - Visual Studio 2005 (8)
354366
2008(e) - Visual Studio 2008 (9)
355367
2010(e) - Visual Studio 2010 (10)
356368
2012(e) - Visual Studio 2012 (11)
357369
2013(e) - Visual Studio 2013 (12)
358370
2015 - Visual Studio 2015 (14)
371+
2017 - Visual Studio 2017 (15)
359372
Where (e) is e for express editions of MSVS and blank otherwise.
360373
"""
361374
version_to_year = {
@@ -365,6 +378,7 @@ def _DetectVisualStudioVersions(versions_to_check, force_express):
365378
'11.0': '2012',
366379
'12.0': '2013',
367380
'14.0': '2015',
381+
'15.0': '2017'
368382
}
369383
versions = []
370384
for version in versions_to_check:
@@ -395,13 +409,18 @@ def _DetectVisualStudioVersions(versions_to_check, force_express):
395409

396410
# The old method above does not work when only SDK is installed.
397411
keys = [r'HKLM\Software\Microsoft\VisualStudio\SxS\VC7',
398-
r'HKLM\Software\Wow6432Node\Microsoft\VisualStudio\SxS\VC7']
412+
r'HKLM\Software\Wow6432Node\Microsoft\VisualStudio\SxS\VC7',
413+
r'HKLM\Software\Microsoft\VisualStudio\SxS\VS7',
414+
r'HKLM\Software\Wow6432Node\Microsoft\VisualStudio\SxS\VS7']
399415
for index in range(len(keys)):
400416
path = _RegistryGetValue(keys[index], version)
401417
if not path:
402418
continue
403419
path = _ConvertToCygpath(path)
404-
if version != '14.0': # There is no Express edition for 2015.
420+
if version == '15.0':
421+
if os.path.exists(path):
422+
versions.append(_CreateVersion('2017', path))
423+
elif version != '14.0': # There is no Express edition for 2015.
405424
versions.append(_CreateVersion(version_to_year[version] + 'e',
406425
os.path.join(path, '..'), sdk_based=True))
407426

@@ -420,7 +439,7 @@ def SelectVisualStudioVersion(version='auto', allow_fallback=True):
420439
if version == 'auto':
421440
version = os.environ.get('GYP_MSVS_VERSION', 'auto')
422441
version_map = {
423-
'auto': ('14.0', '12.0', '10.0', '9.0', '8.0', '11.0'),
442+
'auto': ('15.0', '14.0', '12.0', '10.0', '9.0', '8.0', '11.0'),
424443
'2005': ('8.0',),
425444
'2005e': ('8.0',),
426445
'2008': ('9.0',),
@@ -432,6 +451,7 @@ def SelectVisualStudioVersion(version='auto', allow_fallback=True):
432451
'2013': ('12.0',),
433452
'2013e': ('12.0',),
434453
'2015': ('14.0',),
454+
'2017': ('15.0',),
435455
}
436456
override_path = os.environ.get('GYP_MSVS_OVERRIDE_PATH')
437457
if override_path:

pylib/gyp/generator/msvs.py

Lines changed: 34 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -294,19 +294,21 @@ def _ConfigFullName(config_name, config_data):
294294
return '%s|%s' % (_ConfigBaseName(config_name, platform_name), platform_name)
295295

296296

297-
def _ConfigWindowsTargetPlatformVersion(config_data):
298-
ver = config_data.get('msvs_windows_sdk_version')
299-
300-
for key in [r'HKLM\Software\Microsoft\Microsoft SDKs\Windows\%s',
301-
r'HKLM\Software\Wow6432Node\Microsoft\Microsoft SDKs\Windows\%s']:
302-
sdk_dir = MSVSVersion._RegistryGetValue(key % ver, 'InstallationFolder')
303-
if not sdk_dir:
304-
continue
305-
version = MSVSVersion._RegistryGetValue(key % ver, 'ProductVersion') or ''
306-
# Find a matching entry in sdk_dir\include.
307-
names = sorted([x for x in os.listdir(r'%s\include' % sdk_dir)
308-
if x.startswith(version)], reverse=True)
309-
return names[0]
297+
def _ConfigWindowsTargetPlatformVersion(config_data, version):
298+
config_ver = config_data.get('msvs_windows_sdk_version')
299+
vers = [config_ver] if config_ver else version.compatible_sdks
300+
for ver in vers:
301+
for key in [
302+
r'HKLM\Software\Microsoft\Microsoft SDKs\Windows\%s',
303+
r'HKLM\Software\Wow6432Node\Microsoft\Microsoft SDKs\Windows\%s']:
304+
sdk_dir = MSVSVersion._RegistryGetValue(key % ver, 'InstallationFolder')
305+
if not sdk_dir:
306+
continue
307+
version = MSVSVersion._RegistryGetValue(key % ver, 'ProductVersion') or ''
308+
# Find a matching entry in sdk_dir\include.
309+
names = sorted([x for x in os.listdir(r'%s\include' % sdk_dir)
310+
if x.startswith(version)], reverse=True)
311+
return names[0]
310312

311313

312314
def _BuildCommandLineForRuleRaw(spec, cmd, cygwin_shell, has_input_path,
@@ -2664,7 +2666,7 @@ def _GetMSBuildProjectConfigurations(configurations):
26642666
return [group]
26652667

26662668

2667-
def _GetMSBuildGlobalProperties(spec, guid, gyp_file_name):
2669+
def _GetMSBuildGlobalProperties(spec, version, guid, gyp_file_name):
26682670
namespace = os.path.splitext(gyp_file_name)[0]
26692671
properties = [
26702672
['PropertyGroup', {'Label': 'Globals'},
@@ -2709,15 +2711,18 @@ def _GetMSBuildGlobalProperties(spec, guid, gyp_file_name):
27092711
for configuration in spec['configurations'].itervalues():
27102712
platform_name = platform_name or _ConfigPlatform(configuration)
27112713
msvs_windows_sdk_version = (msvs_windows_sdk_version or
2712-
_ConfigWindowsTargetPlatformVersion(configuration))
2714+
_ConfigWindowsTargetPlatformVersion(configuration, version))
27132715
if platform_name and msvs_windows_sdk_version:
27142716
break
2715-
2716-
if platform_name == 'ARM':
2717-
properties[0].append(['WindowsSDKDesktopARMSupport', 'true'])
27182717
if msvs_windows_sdk_version:
27192718
properties[0].append(['WindowsTargetPlatformVersion',
27202719
str(msvs_windows_sdk_version)])
2720+
elif version.compatible_sdks:
2721+
raise GypError('%s requires any SDK of %o version, but non were found' %
2722+
(version.description, version.compatible_sdks))
2723+
2724+
if platform_name == 'ARM':
2725+
properties[0].append(['WindowsSDKDesktopARMSupport', 'true'])
27212726

27222727
return properties
27232728

@@ -2862,6 +2867,9 @@ def _GetMSBuildAttributes(spec, config, build_file):
28622867
product_name = spec.get('product_name', '$(ProjectName)')
28632868
target_name = prefix + product_name
28642869
msbuild_attributes['TargetName'] = target_name
2870+
if 'TargetExt' not in msbuild_attributes and 'product_extension' in spec:
2871+
ext = spec.get('product_extension')
2872+
msbuild_attributes['TargetExt'] = '.' + ext
28652873

28662874
if spec.get('msvs_external_builder'):
28672875
external_out_dir = spec.get('msvs_external_builder_out_dir', '.')
@@ -2916,6 +2924,9 @@ def _GetMSBuildConfigurationGlobalProperties(spec, configurations, build_file):
29162924
attributes['OutputDirectory'])
29172925
_AddConditionalProperty(properties, condition, 'TargetName',
29182926
attributes['TargetName'])
2927+
if 'TargetExt' in attributes:
2928+
_AddConditionalProperty(properties, condition, 'TargetExt',
2929+
attributes['TargetExt'])
29192930

29202931
if attributes.get('TargetPath'):
29212932
_AddConditionalProperty(properties, condition, 'TargetPath',
@@ -3268,6 +3279,9 @@ def _GetMSBuildProjectReferences(project):
32683279
['ReferenceOutputAssembly', 'false']
32693280
]
32703281
for config in dependency.spec.get('configurations', {}).itervalues():
3282+
if config.get('msvs_use_library_dependency_inputs', 0):
3283+
project_ref.append(['UseLibraryDependencyInputs', 'true'])
3284+
break
32713285
# If it's disabled in any config, turn it off in the reference.
32723286
if config.get('msvs_2010_disable_uldi_when_referenced', 0):
32733287
project_ref.append(['UseLibraryDependencyInputs', 'false'])
@@ -3360,7 +3374,8 @@ def _GenerateMSBuildProject(project, options, version, generator_flags):
33603374
}]
33613375

33623376
content += _GetMSBuildProjectConfigurations(configurations)
3363-
content += _GetMSBuildGlobalProperties(spec, project.guid, project_file_name)
3377+
content += _GetMSBuildGlobalProperties(spec, version, project.guid,
3378+
project_file_name)
33643379
content += import_default_section
33653380
content += _GetMSBuildConfigurationDetails(spec, project.build_file)
33663381
if spec.get('msvs_enable_winphone'):

test/lib/TestGyp.py

Lines changed: 70 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -304,7 +304,7 @@ def run_gyp(self, gyp_file, *args, **kw):
304304
# TODO: --depth=. works around Chromium-specific tree climbing.
305305
depth = kw.pop('depth', '.')
306306
run_args = ['--depth='+depth]
307-
run_args.extend(['--format='+f for f in self.formats]);
307+
run_args.extend(['--format='+f for f in self.formats])
308308
run_args.append(gyp_file)
309309
if self.no_parallel:
310310
run_args += ['--no-parallel']
@@ -313,8 +313,9 @@ def run_gyp(self, gyp_file, *args, **kw):
313313
run_args.extend(self.extra_args)
314314
# Default xcode_ninja_target_pattern to ^.*$ to fix xcode-ninja tests
315315
xcode_ninja_target_pattern = kw.pop('xcode_ninja_target_pattern', '.*')
316-
run_args.extend(
317-
['-G', 'xcode_ninja_target_pattern=%s' % xcode_ninja_target_pattern])
316+
if self is TestGypXcodeNinja:
317+
run_args.extend(
318+
['-G', 'xcode_ninja_target_pattern=%s' % xcode_ninja_target_pattern])
318319
run_args.extend(args)
319320
return self.run(program=self.gyp, arguments=run_args, **kw)
320321

@@ -703,10 +704,15 @@ def FindVisualStudioInstallation():
703704
search %PATH% and %PATHEXT% for a devenv.{exe,bat,...} executable.
704705
Failing that, we search for likely deployment paths.
705706
"""
707+
override_build_tool = os.environ.get('GYP_BUILD_TOOL')
708+
if override_build_tool:
709+
return override_build_tool, True, override_build_tool
710+
706711
possible_roots = ['%s:\\Program Files%s' % (chr(drive), suffix)
707712
for drive in range(ord('C'), ord('Z') + 1)
708713
for suffix in ['', ' (x86)']]
709714
possible_paths = {
715+
'2017': r'Microsoft Visual Studio\2017',
710716
'2015': r'Microsoft Visual Studio 14.0\Common7\IDE\devenv.com',
711717
'2013': r'Microsoft Visual Studio 12.0\Common7\IDE\devenv.com',
712718
'2012': r'Microsoft Visual Studio 11.0\Common7\IDE\devenv.com',
@@ -721,6 +727,36 @@ def FindVisualStudioInstallation():
721727
msvs_version = flag.split('=')[-1]
722728
msvs_version = os.environ.get('GYP_MSVS_VERSION', msvs_version)
723729

730+
if msvs_version in ['2017', 'auto']:
731+
msbuild_exes = []
732+
try:
733+
path = possible_paths['2017']
734+
for r in possible_roots:
735+
build_tool = os.path.join(r, path)
736+
if os.path.exists(build_tool):
737+
break;
738+
else:
739+
build_tool = None
740+
if not build_tool:
741+
args1 = ['reg', 'query',
742+
'HKLM\Software\Microsoft\VisualStudio\SxS\VS7',
743+
'/v', '15.0', '/reg:32']
744+
build_tool = subprocess.check_output(args1)\
745+
.strip().split('\r\n').pop().split(' ').pop()
746+
if build_tool:
747+
args2 = ['cmd.exe', '/d', '/c',
748+
'cd', '/d', build_tool,
749+
'&', 'dir', '/b', '/s', 'msbuild.exe']
750+
msbuild_exes = subprocess.check_output(args2).strip().split('\r\n')
751+
if len(msbuild_exes):
752+
msbuild_Path = os.path.join(build_tool, msbuild_exes[0])
753+
if os.path.exists(msbuild_Path):
754+
os.environ['GYP_MSVS_VERSION'] = '2017'
755+
os.environ['GYP_BUILD_TOOL'] = msbuild_Path
756+
return msbuild_Path, True, msbuild_Path
757+
except Exception as e:
758+
pass
759+
724760
if msvs_version in possible_paths:
725761
# Check that the path to the specified GYP_MSVS_VERSION exists.
726762
path = possible_paths[msvs_version]
@@ -865,22 +901,38 @@ def build(self, gyp_file, target=None, rebuild=False, clean=False, **kw):
865901
Runs a Visual Studio build using the configuration generated
866902
from the specified gyp_file.
867903
"""
868-
configuration = self.configuration_buildname()
869-
if clean:
870-
build = '/Clean'
871-
elif rebuild:
872-
build = '/Rebuild'
904+
if '15.0' in self.build_tool:
905+
configuration = '/p:Configuration=' + (
906+
self.configuration or self.configuration_buildname())
907+
build = '/t'
908+
if target not in (None, self.ALL, self.DEFAULT):
909+
build += ':' + target
910+
if clean:
911+
build += ':Clean'
912+
elif rebuild:
913+
build += ':Rebuild'
914+
elif ':' not in build:
915+
build += ':Build'
916+
arguments = kw.get('arguments', [])[:]
917+
arguments.extend([gyp_file.replace('.gyp', '.sln'),
918+
build, configuration])
873919
else:
874-
build = '/Build'
875-
arguments = kw.get('arguments', [])[:]
876-
arguments.extend([gyp_file.replace('.gyp', '.sln'),
877-
build, configuration])
878-
# Note: the Visual Studio generator doesn't add an explicit 'all'
879-
# target, so we just treat it the same as the default.
880-
if target not in (None, self.ALL, self.DEFAULT):
881-
arguments.extend(['/Project', target])
882-
if self.configuration:
883-
arguments.extend(['/ProjectConfig', self.configuration])
920+
configuration = self.configuration_buildname()
921+
if clean:
922+
build = '/Clean'
923+
elif rebuild:
924+
build = '/Rebuild'
925+
else:
926+
build = '/Build'
927+
arguments = kw.get('arguments', [])[:]
928+
arguments.extend([gyp_file.replace('.gyp', '.sln'),
929+
build, configuration])
930+
# Note: the Visual Studio generator doesn't add an explicit 'all'
931+
# target, so we just treat it the same as the default.
932+
if target not in (None, self.ALL, self.DEFAULT):
933+
arguments.extend(['/Project', target])
934+
if self.configuration:
935+
arguments.extend(['/ProjectConfig', self.configuration])
884936
kw['arguments'] = arguments
885937
return self.run(program=self.build_tool, **kw)
886938
def up_to_date(self, gyp_file, target=None, **kw):

0 commit comments

Comments
 (0)