Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add cross-compilation support for Windows on Arm #32867

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions common.gypi
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,11 @@
],
'msvs_settings': {
'VCCLCompilerTool': {
'conditions': [
['target_arch=="arm64"', {
'FloatingPointModel': 1 # /fp:strict
}]
],
'EnableFunctionLevelLinking': 'true',
'EnableIntrinsicFunctions': 'true',
'FavorSizeOrSpeed': 1, # /Ot, favor speed over size
Expand Down
2 changes: 2 additions & 0 deletions configure.py
Original file line number Diff line number Diff line change
Expand Up @@ -1041,6 +1041,8 @@ def configure_node(o):
cross_compiling = (options.cross_compiling
if options.cross_compiling is not None
else target_arch != host_arch)
if cross_compiling:
os.environ['GYP_CROSSCOMPILE'] = "1"
if options.unused_without_snapshot:
warn('building --without-snapshot is no longer possible')

Expand Down
119 changes: 81 additions & 38 deletions tools/gyp/pylib/gyp/generator/msvs.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
# letters.
VALID_MSVS_GUID_CHARS = re.compile(r'^[A-F0-9\-]+$')

generator_supports_multiple_toolsets = gyp.common.CrossCompileRequested()

generator_default_variables = {
'DRIVER_PREFIX': '',
Expand All @@ -51,7 +52,7 @@
'STATIC_LIB_SUFFIX': '.lib',
'SHARED_LIB_SUFFIX': '.dll',
'INTERMEDIATE_DIR': '$(IntDir)',
'SHARED_INTERMEDIATE_DIR': '$(OutDir)obj/global_intermediate',
'SHARED_INTERMEDIATE_DIR': '$(OutDir)/obj/global_intermediate',
'OS': 'win',
'PRODUCT_DIR': '$(OutDir)',
'LIB_DIR': '$(OutDir)lib',
Expand Down Expand Up @@ -286,7 +287,7 @@ def _ConfigTargetVersion(config_data):
return config_data.get('msvs_target_version', 'Windows7')


def _ConfigPlatform(config_data):
def _ConfigPlatform(config_data, spec):
return config_data.get('msvs_configuration_platform', 'Win32')


Expand All @@ -297,8 +298,8 @@ def _ConfigBaseName(config_name, platform_name):
return config_name


def _ConfigFullName(config_name, config_data):
platform_name = _ConfigPlatform(config_data)
def _ConfigFullName(config_name, config_data, spec):
platform_name = _ConfigPlatform(config_data, spec)
return '%s|%s' % (_ConfigBaseName(config_name, platform_name), platform_name)


Expand Down Expand Up @@ -951,7 +952,7 @@ def _GetMsbuildToolsetOfProject(proj_path, spec, version):
return toolset


def _GenerateProject(project, options, version, generator_flags):
def _GenerateProject(project, options, version, generator_flags, spec):
"""Generates a vcproj file.

Arguments:
Expand All @@ -969,7 +970,7 @@ def _GenerateProject(project, options, version, generator_flags):
return []

if version.UsesVcxproj():
return _GenerateMSBuildProject(project, options, version, generator_flags)
return _GenerateMSBuildProject(project, options, version, generator_flags, spec)
else:
return _GenerateMSVSProject(project, options, version, generator_flags)

Expand Down Expand Up @@ -1091,7 +1092,7 @@ def _GetUniquePlatforms(spec):
# Gather list of unique platforms.
platforms = OrderedSet()
for configuration in spec['configurations']:
platforms.add(_ConfigPlatform(spec['configurations'][configuration]))
platforms.add(_ConfigPlatform(spec['configurations'][configuration], spec))
platforms = list(platforms)
return platforms

Expand Down Expand Up @@ -1801,6 +1802,8 @@ def _GatherSolutionFolders(sln_projects, project_objects, flat):
# Convert into a tree of dicts on path.
for p in sln_projects:
gyp_file, target = gyp.common.ParseQualifiedTarget(p)[0:2]
if p.endswith("#host"):
target += "_host"
gyp_dir = os.path.dirname(gyp_file)
path_dict = _GetPathDict(root, gyp_dir)
path_dict[target + '.vcproj'] = project_objects[p]
Expand All @@ -1819,7 +1822,10 @@ def _GetPathOfProject(qualified_target, spec, options, msvs_version):
default_config = _GetDefaultConfiguration(spec)
proj_filename = default_config.get('msvs_existing_vcproj')
if not proj_filename:
proj_filename = (spec['target_name'] + options.suffix +
proj_filename = spec['target_name']
if spec['toolset'] == 'host':
proj_filename += "_host"
proj_filename = (proj_filename + options.suffix +
msvs_version.ProjectExtension())

build_file = gyp.common.BuildFile(qualified_target)
Expand All @@ -1838,10 +1844,12 @@ def _GetPlatformOverridesOfProject(spec):
# solution configurations for this target.
config_platform_overrides = {}
for config_name, c in spec['configurations'].items():
config_fullname = _ConfigFullName(config_name, c)
platform = c.get('msvs_target_platform', _ConfigPlatform(c))
config_fullname = _ConfigFullName(config_name, c, spec)
platform = c.get('msvs_target_platform', _ConfigPlatform(c, spec))
fixed_config_fullname = '%s|%s' % (
_ConfigBaseName(config_name, _ConfigPlatform(c)), platform)
_ConfigBaseName(config_name, _ConfigPlatform(c, spec)), platform)
if spec['toolset'] == 'host' and generator_supports_multiple_toolsets:
fixed_config_fullname = '%s|x64' % (config_name,)
config_platform_overrides[config_fullname] = fixed_config_fullname
return config_platform_overrides

Expand All @@ -1862,19 +1870,18 @@ def _CreateProjectObjects(target_list, target_dicts, options, msvs_version):
projects = {}
for qualified_target in target_list:
spec = target_dicts[qualified_target]
if spec['toolset'] != 'target':
raise GypError(
'Multiple toolsets not supported in msvs build (target %s)' %
qualified_target)
proj_path, fixpath_prefix = _GetPathOfProject(qualified_target, spec,
options, msvs_version)
guid = _GetGuidOfProject(proj_path, spec)
overrides = _GetPlatformOverridesOfProject(spec)
build_file = gyp.common.BuildFile(qualified_target)
# Create object for this project.
target_name = spec['target_name']
if spec['toolset'] == 'host':
target_name += '_host'
obj = MSVSNew.MSVSProject(
proj_path,
name=spec['target_name'],
name=target_name,
guid=guid,
spec=spec,
build_file=build_file,
Expand Down Expand Up @@ -2041,7 +2048,10 @@ def GenerateOutput(target_list, target_dicts, data, params):
for qualified_target in target_list:
spec = target_dicts[qualified_target]
for config_name, config in spec['configurations'].items():
configs.add(_ConfigFullName(config_name, config))
config_name = _ConfigFullName(config_name, config, spec)
configs.add(config_name)
if config_name == 'Release|arm64':
configs.add("Release|x64")
configs = list(configs)

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

for build_file in data:
# Validate build_file extension
target_only_configs = configs
if generator_supports_multiple_toolsets:
target_only_configs = [i for i in configs if i.endswith('arm64')]
if not build_file.endswith('.gyp'):
continue
sln_path = os.path.splitext(build_file)[0] + options.suffix + '.sln'
Expand All @@ -2072,7 +2085,7 @@ def GenerateOutput(target_list, target_dicts, data, params):
# Create solution.
sln = MSVSNew.MSVSSolution(sln_path,
entries=root_entries,
variants=configs,
variants=target_only_configs,
websiteProperties=False,
version=msvs_version)
sln.Write()
Expand Down Expand Up @@ -2674,21 +2687,23 @@ def _GenerateMSBuildRuleXmlFile(xml_path, msbuild_rules):
easy_xml.WriteXmlIfChanged(content, xml_path, pretty=True, win32=True)


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


def _GetConfigurationCondition(name, settings):
def _GetConfigurationCondition(name, settings, spec):
return (r"'$(Configuration)|$(Platform)'=='%s|%s'" %
_GetConfigurationAndPlatform(name, settings))
_GetConfigurationAndPlatform(name, settings, spec))


def _GetMSBuildProjectConfigurations(configurations):
def _GetMSBuildProjectConfigurations(configurations, spec):
group = ['ItemGroup', {'Label': 'ProjectConfigurations'}]
for (name, settings) in sorted(configurations.items()):
configuration, platform = _GetConfigurationAndPlatform(name, settings)
configuration, platform = _GetConfigurationAndPlatform(name, settings, spec)
designation = '%s|%s' % (configuration, platform)
group.append(
['ProjectConfiguration', {'Include': designation},
Expand Down Expand Up @@ -2740,7 +2755,7 @@ def _GetMSBuildGlobalProperties(spec, version, guid, gyp_file_name):
platform_name = None
msvs_windows_sdk_version = None
for configuration in spec['configurations'].values():
platform_name = platform_name or _ConfigPlatform(configuration)
platform_name = platform_name or _ConfigPlatform(configuration, spec)
msvs_windows_sdk_version = (msvs_windows_sdk_version or
_ConfigWindowsTargetPlatformVersion(configuration, version))
if platform_name and msvs_windows_sdk_version:
Expand All @@ -2762,7 +2777,7 @@ def _GetMSBuildConfigurationDetails(spec, build_file):
properties = {}
for name, settings in spec['configurations'].items():
msbuild_attributes = _GetMSBuildAttributes(spec, settings, build_file)
condition = _GetConfigurationCondition(name, settings)
condition = _GetConfigurationCondition(name, settings, spec)
character_set = msbuild_attributes.get('CharacterSet')
config_type = msbuild_attributes.get('ConfigurationType')
_AddConditionalProperty(properties, condition, 'ConfigurationType',
Expand Down Expand Up @@ -2790,12 +2805,12 @@ def _GetMSBuildLocalProperties(msbuild_toolset):
return properties


def _GetMSBuildPropertySheets(configurations):
def _GetMSBuildPropertySheets(configurations, spec):
user_props = r'$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props'
additional_props = {}
props_specified = False
for name, settings in sorted(configurations.items()):
configuration = _GetConfigurationCondition(name, settings)
configuration = _GetConfigurationCondition(name, settings, spec)
if 'msbuild_props' in settings:
additional_props[configuration] = _FixPaths(settings['msbuild_props'])
props_specified = True
Expand Down Expand Up @@ -2946,7 +2961,7 @@ def _GetMSBuildConfigurationGlobalProperties(spec, configurations, build_file):

properties = {}
for (name, configuration) in sorted(configurations.items()):
condition = _GetConfigurationCondition(name, configuration)
condition = _GetConfigurationCondition(name, configuration, spec)
attributes = _GetMSBuildAttributes(spec, configuration, build_file)
msbuild_settings = configuration['finalized_msbuild_settings']
_AddConditionalProperty(properties, condition, 'IntDir',
Expand Down Expand Up @@ -3055,7 +3070,9 @@ def _GetMSBuildToolSettingsSections(spec, configurations):
for (name, configuration) in sorted(configurations.items()):
msbuild_settings = configuration['finalized_msbuild_settings']
group = ['ItemDefinitionGroup',
{'Condition': _GetConfigurationCondition(name, configuration)}
{'Condition':
_GetConfigurationCondition(name, configuration, spec)
}
]
for tool_name, tool_settings in sorted(msbuild_settings.items()):
# Skip the tool named '' which is a holder of global settings handled
Expand Down Expand Up @@ -3277,7 +3294,9 @@ def _AddSources2(spec, sources, exclusions, grouped_sources,
extensions_excluded_from_precompile = ['.c']

if precompiled_source == source:
condition = _GetConfigurationCondition(config_name, configuration)
condition = _GetConfigurationCondition(
config_name, configuration, spec
)
detail.append(['PrecompiledHeader',
{'Condition': condition},
'Create'
Expand All @@ -3296,12 +3315,26 @@ def _AddSources2(spec, sources, exclusions, grouped_sources,
_GetUniquePlatforms(spec))
grouped_sources[group].append([element, {'Include': source}] + detail)


def _GetMSBuildProjectReferences(project):
def _GetMSBuildProjectReferences(project, spec):
current_configuration = spec['default_configuration']
references = []
if project.dependencies:
group = ['ItemGroup']
added_dependency_set = set()
for dependency in project.dependencies:
dependency_spec = dependency.spec
should_skip_dep = False
if project.spec["toolset"] == 'target':
if dependency_spec['toolset'] == 'host':
if dependency_spec['type'] == 'static_library':
should_skip_dep = True
if dependency.name.startswith('run_'):
should_skip_dep = False
if should_skip_dep:
continue

canonical_name = dependency.name.replace('_host', '')
added_dependency_set.add(canonical_name)
guid = dependency.guid
project_dir = os.path.split(project.path)[0]
relative_path = gyp.common.RelativePath(dependency.path, project_dir)
Expand All @@ -3323,7 +3356,7 @@ def _GetMSBuildProjectReferences(project):
return references


def _GenerateMSBuildProject(project, options, version, generator_flags):
def _GenerateMSBuildProject(project, options, version, generator_flags, spec):
spec = project.spec
configurations = spec['configurations']
project_dir, project_file_name = os.path.split(project.path)
Expand Down Expand Up @@ -3411,7 +3444,7 @@ def _GenerateMSBuildProject(project, options, version, generator_flags):
'DefaultTargets': 'Build'
}]

content += _GetMSBuildProjectConfigurations(configurations)
content += _GetMSBuildProjectConfigurations(configurations, spec)
content += _GetMSBuildGlobalProperties(spec, version, project.guid,
project_file_name)
content += import_default_section
Expand All @@ -3422,18 +3455,18 @@ def _GenerateMSBuildProject(project, options, version, generator_flags):
content += _GetMSBuildLocalProperties(project.msbuild_toolset)
content += import_cpp_props_section
content += import_masm_props_section
if spec.get('msvs_enable_marmasm'):
if spec.get('msvs_enable_marmasm') or True:
content += import_marmasm_props_section
content += _GetMSBuildExtensions(props_files_of_rules)
content += _GetMSBuildPropertySheets(configurations)
content += _GetMSBuildPropertySheets(configurations, spec)
content += macro_section
content += _GetMSBuildConfigurationGlobalProperties(spec, configurations,
project.build_file)
content += _GetMSBuildToolSettingsSections(spec, configurations)
content += _GetMSBuildSources(
spec, sources, exclusions, rule_dependencies, extension_to_rule_name,
actions_spec, sources_handled_by_action, list_excluded)
content += _GetMSBuildProjectReferences(project)
content += _GetMSBuildProjectReferences(project, spec)
content += import_cpp_targets_section
content += import_masm_targets_section
if spec.get('msvs_enable_marmasm'):
Expand Down Expand Up @@ -3516,15 +3549,25 @@ def _GenerateActionsForMSBuild(spec, actions_to_add):
sources_handled_by_action = OrderedSet()
actions_spec = []
for primary_input, actions in actions_to_add.items():
if generator_supports_multiple_toolsets:
primary_input = primary_input.replace(".exe", "_host.exe")
inputs = OrderedSet()
outputs = OrderedSet()
descriptions = []
commands = []
for action in actions:
def fixup_host_exe(i):
if "$(OutDir)" in i:
i = i.replace('.exe', '_host.exe')
return i
if generator_supports_multiple_toolsets:
action['inputs'] = [fixup_host_exe(i) for i in action['inputs']]
inputs.update(OrderedSet(action['inputs']))
outputs.update(OrderedSet(action['outputs']))
descriptions.append(action['description'])
cmd = action['command']
if generator_supports_multiple_toolsets:
cmd = cmd.replace('.exe', "_host.exe")
# For most actions, add 'call' so that actions that invoke batch files
# return and continue executing. msbuild_use_call provides a way to
# disable this but I have not seen any adverse effect from doing that
Expand Down
Loading