Skip to content

Commit 2384044

Browse files
richard-townsend-armtargos
authored andcommitted
tools,gyp: add support for MSVC cross-compilation
This change means that GYP can now generate two sets of projects: one exclusively for a host x64 machine and one containing a mix of x64 and Arm targets. The names of host targets are fixed up to end with _host.exe, and any actions involving them are fixed up. This allows compilation of Node on an x64 server for a Windows on Arm target. PR-URL: #32867 Reviewed-By: Christian Clauss <[email protected]> Reviewed-By: João Reis <[email protected]>
1 parent d4e8714 commit 2384044

File tree

1 file changed

+81
-38
lines changed
  • tools/gyp/pylib/gyp/generator

1 file changed

+81
-38
lines changed

tools/gyp/pylib/gyp/generator/msvs.py

+81-38
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
# letters.
4141
VALID_MSVS_GUID_CHARS = re.compile(r'^[A-F0-9\-]+$')
4242

43+
generator_supports_multiple_toolsets = gyp.common.CrossCompileRequested()
4344

4445
generator_default_variables = {
4546
'DRIVER_PREFIX': '',
@@ -51,7 +52,7 @@
5152
'STATIC_LIB_SUFFIX': '.lib',
5253
'SHARED_LIB_SUFFIX': '.dll',
5354
'INTERMEDIATE_DIR': '$(IntDir)',
54-
'SHARED_INTERMEDIATE_DIR': '$(OutDir)obj/global_intermediate',
55+
'SHARED_INTERMEDIATE_DIR': '$(OutDir)/obj/global_intermediate',
5556
'OS': 'win',
5657
'PRODUCT_DIR': '$(OutDir)',
5758
'LIB_DIR': '$(OutDir)lib',
@@ -286,7 +287,7 @@ def _ConfigTargetVersion(config_data):
286287
return config_data.get('msvs_target_version', 'Windows7')
287288

288289

289-
def _ConfigPlatform(config_data):
290+
def _ConfigPlatform(config_data, spec):
290291
return config_data.get('msvs_configuration_platform', 'Win32')
291292

292293

@@ -297,8 +298,8 @@ def _ConfigBaseName(config_name, platform_name):
297298
return config_name
298299

299300

300-
def _ConfigFullName(config_name, config_data):
301-
platform_name = _ConfigPlatform(config_data)
301+
def _ConfigFullName(config_name, config_data, spec):
302+
platform_name = _ConfigPlatform(config_data, spec)
302303
return '%s|%s' % (_ConfigBaseName(config_name, platform_name), platform_name)
303304

304305

@@ -951,7 +952,7 @@ def _GetMsbuildToolsetOfProject(proj_path, spec, version):
951952
return toolset
952953

953954

954-
def _GenerateProject(project, options, version, generator_flags):
955+
def _GenerateProject(project, options, version, generator_flags, spec):
955956
"""Generates a vcproj file.
956957
957958
Arguments:
@@ -969,7 +970,7 @@ def _GenerateProject(project, options, version, generator_flags):
969970
return []
970971

971972
if version.UsesVcxproj():
972-
return _GenerateMSBuildProject(project, options, version, generator_flags)
973+
return _GenerateMSBuildProject(project, options, version, generator_flags, spec)
973974
else:
974975
return _GenerateMSVSProject(project, options, version, generator_flags)
975976

@@ -1091,7 +1092,7 @@ def _GetUniquePlatforms(spec):
10911092
# Gather list of unique platforms.
10921093
platforms = OrderedSet()
10931094
for configuration in spec['configurations']:
1094-
platforms.add(_ConfigPlatform(spec['configurations'][configuration]))
1095+
platforms.add(_ConfigPlatform(spec['configurations'][configuration], spec))
10951096
platforms = list(platforms)
10961097
return platforms
10971098

@@ -1801,6 +1802,8 @@ def _GatherSolutionFolders(sln_projects, project_objects, flat):
18011802
# Convert into a tree of dicts on path.
18021803
for p in sln_projects:
18031804
gyp_file, target = gyp.common.ParseQualifiedTarget(p)[0:2]
1805+
if p.endswith("#host"):
1806+
target += "_host"
18041807
gyp_dir = os.path.dirname(gyp_file)
18051808
path_dict = _GetPathDict(root, gyp_dir)
18061809
path_dict[target + '.vcproj'] = project_objects[p]
@@ -1819,7 +1822,10 @@ def _GetPathOfProject(qualified_target, spec, options, msvs_version):
18191822
default_config = _GetDefaultConfiguration(spec)
18201823
proj_filename = default_config.get('msvs_existing_vcproj')
18211824
if not proj_filename:
1822-
proj_filename = (spec['target_name'] + options.suffix +
1825+
proj_filename = spec['target_name']
1826+
if spec['toolset'] == 'host':
1827+
proj_filename += "_host"
1828+
proj_filename = (proj_filename + options.suffix +
18231829
msvs_version.ProjectExtension())
18241830

18251831
build_file = gyp.common.BuildFile(qualified_target)
@@ -1838,10 +1844,12 @@ def _GetPlatformOverridesOfProject(spec):
18381844
# solution configurations for this target.
18391845
config_platform_overrides = {}
18401846
for config_name, c in spec['configurations'].items():
1841-
config_fullname = _ConfigFullName(config_name, c)
1842-
platform = c.get('msvs_target_platform', _ConfigPlatform(c))
1847+
config_fullname = _ConfigFullName(config_name, c, spec)
1848+
platform = c.get('msvs_target_platform', _ConfigPlatform(c, spec))
18431849
fixed_config_fullname = '%s|%s' % (
1844-
_ConfigBaseName(config_name, _ConfigPlatform(c)), platform)
1850+
_ConfigBaseName(config_name, _ConfigPlatform(c, spec)), platform)
1851+
if spec['toolset'] == 'host' and generator_supports_multiple_toolsets:
1852+
fixed_config_fullname = '%s|x64' % (config_name,)
18451853
config_platform_overrides[config_fullname] = fixed_config_fullname
18461854
return config_platform_overrides
18471855

@@ -1862,19 +1870,18 @@ def _CreateProjectObjects(target_list, target_dicts, options, msvs_version):
18621870
projects = {}
18631871
for qualified_target in target_list:
18641872
spec = target_dicts[qualified_target]
1865-
if spec['toolset'] != 'target':
1866-
raise GypError(
1867-
'Multiple toolsets not supported in msvs build (target %s)' %
1868-
qualified_target)
18691873
proj_path, fixpath_prefix = _GetPathOfProject(qualified_target, spec,
18701874
options, msvs_version)
18711875
guid = _GetGuidOfProject(proj_path, spec)
18721876
overrides = _GetPlatformOverridesOfProject(spec)
18731877
build_file = gyp.common.BuildFile(qualified_target)
18741878
# Create object for this project.
1879+
target_name = spec['target_name']
1880+
if spec['toolset'] == 'host':
1881+
target_name += '_host'
18751882
obj = MSVSNew.MSVSProject(
18761883
proj_path,
1877-
name=spec['target_name'],
1884+
name=target_name,
18781885
guid=guid,
18791886
spec=spec,
18801887
build_file=build_file,
@@ -2041,7 +2048,10 @@ def GenerateOutput(target_list, target_dicts, data, params):
20412048
for qualified_target in target_list:
20422049
spec = target_dicts[qualified_target]
20432050
for config_name, config in spec['configurations'].items():
2044-
configs.add(_ConfigFullName(config_name, config))
2051+
config_name = _ConfigFullName(config_name, config, spec)
2052+
configs.add(config_name)
2053+
if config_name == 'Release|arm64':
2054+
configs.add("Release|x64")
20452055
configs = list(configs)
20462056

20472057
# Figure out all the projects that will be generated and their guids
@@ -2053,11 +2063,14 @@ def GenerateOutput(target_list, target_dicts, data, params):
20532063
for project in project_objects.values():
20542064
fixpath_prefix = project.fixpath_prefix
20552065
missing_sources.extend(_GenerateProject(project, options, msvs_version,
2056-
generator_flags))
2066+
generator_flags, spec))
20572067
fixpath_prefix = None
20582068

20592069
for build_file in data:
20602070
# Validate build_file extension
2071+
target_only_configs = configs
2072+
if generator_supports_multiple_toolsets:
2073+
target_only_configs = [i for i in configs if i.endswith('arm64')]
20612074
if not build_file.endswith('.gyp'):
20622075
continue
20632076
sln_path = os.path.splitext(build_file)[0] + options.suffix + '.sln'
@@ -2072,7 +2085,7 @@ def GenerateOutput(target_list, target_dicts, data, params):
20722085
# Create solution.
20732086
sln = MSVSNew.MSVSSolution(sln_path,
20742087
entries=root_entries,
2075-
variants=configs,
2088+
variants=target_only_configs,
20762089
websiteProperties=False,
20772090
version=msvs_version)
20782091
sln.Write()
@@ -2674,21 +2687,23 @@ def _GenerateMSBuildRuleXmlFile(xml_path, msbuild_rules):
26742687
easy_xml.WriteXmlIfChanged(content, xml_path, pretty=True, win32=True)
26752688

26762689

2677-
def _GetConfigurationAndPlatform(name, settings):
2690+
def _GetConfigurationAndPlatform(name, settings, spec):
26782691
configuration = name.rsplit('_', 1)[0]
26792692
platform = settings.get('msvs_configuration_platform', 'Win32')
2693+
if spec['toolset'] == 'host' and platform == 'arm64':
2694+
platform = 'x64' # Host-only tools are always built for x64
26802695
return (configuration, platform)
26812696

26822697

2683-
def _GetConfigurationCondition(name, settings):
2698+
def _GetConfigurationCondition(name, settings, spec):
26842699
return (r"'$(Configuration)|$(Platform)'=='%s|%s'" %
2685-
_GetConfigurationAndPlatform(name, settings))
2700+
_GetConfigurationAndPlatform(name, settings, spec))
26862701

26872702

2688-
def _GetMSBuildProjectConfigurations(configurations):
2703+
def _GetMSBuildProjectConfigurations(configurations, spec):
26892704
group = ['ItemGroup', {'Label': 'ProjectConfigurations'}]
26902705
for (name, settings) in sorted(configurations.items()):
2691-
configuration, platform = _GetConfigurationAndPlatform(name, settings)
2706+
configuration, platform = _GetConfigurationAndPlatform(name, settings, spec)
26922707
designation = '%s|%s' % (configuration, platform)
26932708
group.append(
26942709
['ProjectConfiguration', {'Include': designation},
@@ -2740,7 +2755,7 @@ def _GetMSBuildGlobalProperties(spec, version, guid, gyp_file_name):
27402755
platform_name = None
27412756
msvs_windows_sdk_version = None
27422757
for configuration in spec['configurations'].values():
2743-
platform_name = platform_name or _ConfigPlatform(configuration)
2758+
platform_name = platform_name or _ConfigPlatform(configuration, spec)
27442759
msvs_windows_sdk_version = (msvs_windows_sdk_version or
27452760
_ConfigWindowsTargetPlatformVersion(configuration, version))
27462761
if platform_name and msvs_windows_sdk_version:
@@ -2762,7 +2777,7 @@ def _GetMSBuildConfigurationDetails(spec, build_file):
27622777
properties = {}
27632778
for name, settings in spec['configurations'].items():
27642779
msbuild_attributes = _GetMSBuildAttributes(spec, settings, build_file)
2765-
condition = _GetConfigurationCondition(name, settings)
2780+
condition = _GetConfigurationCondition(name, settings, spec)
27662781
character_set = msbuild_attributes.get('CharacterSet')
27672782
config_type = msbuild_attributes.get('ConfigurationType')
27682783
_AddConditionalProperty(properties, condition, 'ConfigurationType',
@@ -2790,12 +2805,12 @@ def _GetMSBuildLocalProperties(msbuild_toolset):
27902805
return properties
27912806

27922807

2793-
def _GetMSBuildPropertySheets(configurations):
2808+
def _GetMSBuildPropertySheets(configurations, spec):
27942809
user_props = r'$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props'
27952810
additional_props = {}
27962811
props_specified = False
27972812
for name, settings in sorted(configurations.items()):
2798-
configuration = _GetConfigurationCondition(name, settings)
2813+
configuration = _GetConfigurationCondition(name, settings, spec)
27992814
if 'msbuild_props' in settings:
28002815
additional_props[configuration] = _FixPaths(settings['msbuild_props'])
28012816
props_specified = True
@@ -2946,7 +2961,7 @@ def _GetMSBuildConfigurationGlobalProperties(spec, configurations, build_file):
29462961

29472962
properties = {}
29482963
for (name, configuration) in sorted(configurations.items()):
2949-
condition = _GetConfigurationCondition(name, configuration)
2964+
condition = _GetConfigurationCondition(name, configuration, spec)
29502965
attributes = _GetMSBuildAttributes(spec, configuration, build_file)
29512966
msbuild_settings = configuration['finalized_msbuild_settings']
29522967
_AddConditionalProperty(properties, condition, 'IntDir',
@@ -3055,7 +3070,9 @@ def _GetMSBuildToolSettingsSections(spec, configurations):
30553070
for (name, configuration) in sorted(configurations.items()):
30563071
msbuild_settings = configuration['finalized_msbuild_settings']
30573072
group = ['ItemDefinitionGroup',
3058-
{'Condition': _GetConfigurationCondition(name, configuration)}
3073+
{'Condition':
3074+
_GetConfigurationCondition(name, configuration, spec)
3075+
}
30593076
]
30603077
for tool_name, tool_settings in sorted(msbuild_settings.items()):
30613078
# Skip the tool named '' which is a holder of global settings handled
@@ -3277,7 +3294,9 @@ def _AddSources2(spec, sources, exclusions, grouped_sources,
32773294
extensions_excluded_from_precompile = ['.c']
32783295

32793296
if precompiled_source == source:
3280-
condition = _GetConfigurationCondition(config_name, configuration)
3297+
condition = _GetConfigurationCondition(
3298+
config_name, configuration, spec
3299+
)
32813300
detail.append(['PrecompiledHeader',
32823301
{'Condition': condition},
32833302
'Create'
@@ -3296,12 +3315,26 @@ def _AddSources2(spec, sources, exclusions, grouped_sources,
32963315
_GetUniquePlatforms(spec))
32973316
grouped_sources[group].append([element, {'Include': source}] + detail)
32983317

3299-
3300-
def _GetMSBuildProjectReferences(project):
3318+
def _GetMSBuildProjectReferences(project, spec):
3319+
current_configuration = spec['default_configuration']
33013320
references = []
33023321
if project.dependencies:
33033322
group = ['ItemGroup']
3323+
added_dependency_set = set()
33043324
for dependency in project.dependencies:
3325+
dependency_spec = dependency.spec
3326+
should_skip_dep = False
3327+
if project.spec["toolset"] == 'target':
3328+
if dependency_spec['toolset'] == 'host':
3329+
if dependency_spec['type'] == 'static_library':
3330+
should_skip_dep = True
3331+
if dependency.name.startswith('run_'):
3332+
should_skip_dep = False
3333+
if should_skip_dep:
3334+
continue
3335+
3336+
canonical_name = dependency.name.replace('_host', '')
3337+
added_dependency_set.add(canonical_name)
33053338
guid = dependency.guid
33063339
project_dir = os.path.split(project.path)[0]
33073340
relative_path = gyp.common.RelativePath(dependency.path, project_dir)
@@ -3323,7 +3356,7 @@ def _GetMSBuildProjectReferences(project):
33233356
return references
33243357

33253358

3326-
def _GenerateMSBuildProject(project, options, version, generator_flags):
3359+
def _GenerateMSBuildProject(project, options, version, generator_flags, spec):
33273360
spec = project.spec
33283361
configurations = spec['configurations']
33293362
project_dir, project_file_name = os.path.split(project.path)
@@ -3411,7 +3444,7 @@ def _GenerateMSBuildProject(project, options, version, generator_flags):
34113444
'DefaultTargets': 'Build'
34123445
}]
34133446

3414-
content += _GetMSBuildProjectConfigurations(configurations)
3447+
content += _GetMSBuildProjectConfigurations(configurations, spec)
34153448
content += _GetMSBuildGlobalProperties(spec, version, project.guid,
34163449
project_file_name)
34173450
content += import_default_section
@@ -3422,18 +3455,18 @@ def _GenerateMSBuildProject(project, options, version, generator_flags):
34223455
content += _GetMSBuildLocalProperties(project.msbuild_toolset)
34233456
content += import_cpp_props_section
34243457
content += import_masm_props_section
3425-
if spec.get('msvs_enable_marmasm'):
3458+
if spec.get('msvs_enable_marmasm') or True:
34263459
content += import_marmasm_props_section
34273460
content += _GetMSBuildExtensions(props_files_of_rules)
3428-
content += _GetMSBuildPropertySheets(configurations)
3461+
content += _GetMSBuildPropertySheets(configurations, spec)
34293462
content += macro_section
34303463
content += _GetMSBuildConfigurationGlobalProperties(spec, configurations,
34313464
project.build_file)
34323465
content += _GetMSBuildToolSettingsSections(spec, configurations)
34333466
content += _GetMSBuildSources(
34343467
spec, sources, exclusions, rule_dependencies, extension_to_rule_name,
34353468
actions_spec, sources_handled_by_action, list_excluded)
3436-
content += _GetMSBuildProjectReferences(project)
3469+
content += _GetMSBuildProjectReferences(project, spec)
34373470
content += import_cpp_targets_section
34383471
content += import_masm_targets_section
34393472
if spec.get('msvs_enable_marmasm'):
@@ -3516,15 +3549,25 @@ def _GenerateActionsForMSBuild(spec, actions_to_add):
35163549
sources_handled_by_action = OrderedSet()
35173550
actions_spec = []
35183551
for primary_input, actions in actions_to_add.items():
3552+
if generator_supports_multiple_toolsets:
3553+
primary_input = primary_input.replace(".exe", "_host.exe")
35193554
inputs = OrderedSet()
35203555
outputs = OrderedSet()
35213556
descriptions = []
35223557
commands = []
35233558
for action in actions:
3559+
def fixup_host_exe(i):
3560+
if "$(OutDir)" in i:
3561+
i = i.replace('.exe', '_host.exe')
3562+
return i
3563+
if generator_supports_multiple_toolsets:
3564+
action['inputs'] = [fixup_host_exe(i) for i in action['inputs']]
35243565
inputs.update(OrderedSet(action['inputs']))
35253566
outputs.update(OrderedSet(action['outputs']))
35263567
descriptions.append(action['description'])
35273568
cmd = action['command']
3569+
if generator_supports_multiple_toolsets:
3570+
cmd = cmd.replace('.exe', "_host.exe")
35283571
# For most actions, add 'call' so that actions that invoke batch files
35293572
# return and continue executing. msbuild_use_call provides a way to
35303573
# disable this but I have not seen any adverse effect from doing that

0 commit comments

Comments
 (0)