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

VS: Add support for /utf-8 #13929

Draft
wants to merge 7 commits into
base: master
Choose a base branch
from
Draft
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
4 changes: 4 additions & 0 deletions mesonbuild/backend/ninjabackend.py
Original file line number Diff line number Diff line change
Expand Up @@ -2927,6 +2927,10 @@ def _generate_single_compile_target_args(self, target: build.BuildTarget, compil
# Finally add the private dir for the target to the include path. This
# must override everything else and must be the final path added.
commands += compiler.get_include_args(self.get_target_private_dir(target), False)

# Add Meson per-compiler defaults (like /utf-8 for MSVC)
compiler.add_default_build_args(commands)

return commands

# Returns a dictionary, mapping from each compiler src type (e.g. 'c', 'cpp', etc.) to a list of compiler arg strings
Expand Down
134 changes: 106 additions & 28 deletions mesonbuild/backend/vs2010backend.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,25 +66,35 @@
'Please specify the exact backend to use.'.format(vs_version, vs_install_dir))


def split_o_flags_args(args: T.List[str]) -> T.List[str]:
def split_o_flags_args(args: T.List[str], remove: bool) -> T.List[str]:
"""
Splits any /O args and returns them. Does not take care of flags overriding
previous ones. Skips non-O flag arguments.
previous ones. Skips non-O flag arguments. If remove is true, found arguments
will be removed from the args list

['/Ox', '/Ob1'] returns ['/Ox', '/Ob1']
['/Oxj', '/MP'] returns ['/Ox', '/Oj']
"""
o_flags = []
for arg in args:
indexes = []
for index, arg in enumerate(args):
if not arg.startswith('/O'):
continue

indexes.append(index)

flags = list(arg[2:])
# Assume that this one can't be clumped with the others since it takes
# an argument itself
if 'b' in flags:
o_flags.append(arg)
else:
o_flags += ['/O' + f for f in flags]

if remove:
for index in reversed(indexes):
args.pop(index)

return o_flags

def generate_guid_from_path(path, path_type) -> str:
Expand Down Expand Up @@ -1013,6 +1023,10 @@
# to override all the defaults, but not the per-target compile args.
for lang in file_args.keys():
file_args[lang] += target.get_option(OptionKey(f'{lang}_args', machine=target.for_machine))
# Meson default build arguments, which must added as last so that they're added only
# if not already set in other ways
for lang in file_args.keys():
target.compilers[lang].add_default_build_args(file_args[lang])
for args in file_args.values():
# This is where Visual Studio will insert target_args, target_defines,
# etc, which are added later from external deps (see below).
Expand Down Expand Up @@ -1118,7 +1132,8 @@

@staticmethod
def get_build_args(compiler, optimization_level: str, debug: bool, sanitize: str) -> T.List[str]:
build_args = compiler.get_optimization_args(optimization_level)
build_args = compiler.get_always_args()
build_args += compiler.get_optimization_args(optimization_level)
build_args += compiler.get_debug_args(debug)
build_args += compiler.sanitizer_compile_args(sanitize)

Expand Down Expand Up @@ -1273,7 +1288,7 @@
target,
platform: str,
subsystem,
build_args,
build_args_,
target_args,
target_defines,
target_inc_dirs,
Expand All @@ -1282,6 +1297,24 @@
compiler = self._get_cl_compiler(target)
buildtype_link_args = compiler.get_optimization_link_args(self.optimization)

build_args = build_args_ + target_args

def check_build_arg(name, remove = True):
index = None
try:
index = build_args.index(name)
except ValueError:
pass
if index and remove:
build_args.pop(index)
return index is not None

def check_build_arg_prefix(prefix):
for a in build_args:
if a.startswith(prefix):
return True
return False

# Prefix to use to access the build root from the vcxproj dir
down = self.target_to_build_root(target)

Expand All @@ -1301,6 +1334,7 @@
clconf = ET.SubElement(compiles, 'ClCompile')
if True in ((dep.name == 'openmp') for dep in target.get_external_deps()):
ET.SubElement(clconf, 'OpenMPSupport').text = 'true'

# CRT type; debug or release
vscrt_type = target.get_option(OptionKey('b_vscrt'))
vscrt_val = compiler.get_crt_val(vscrt_type, self.buildtype)
Expand All @@ -1318,25 +1352,31 @@
else:
ET.SubElement(type_config, 'UseDebugLibraries').text = 'false'
ET.SubElement(clconf, 'RuntimeLibrary').text = 'MultiThreadedDLL'

# Sanitizers
if '/fsanitize=address' in build_args:
if check_build_arg('/fsanitize=address', remove=True):
ET.SubElement(type_config, 'EnableASAN').text = 'true'

# Debug format
if '/ZI' in build_args:
if check_build_arg('/ZI', remove=True):
ET.SubElement(clconf, 'DebugInformationFormat').text = 'EditAndContinue'
elif '/Zi' in build_args:
elif check_build_arg('/Zi', remove=True):
ET.SubElement(clconf, 'DebugInformationFormat').text = 'ProgramDatabase'
elif '/Z7' in build_args:
elif check_build_arg('/Z7', remove=True):
ET.SubElement(clconf, 'DebugInformationFormat').text = 'OldStyle'
else:
ET.SubElement(clconf, 'DebugInformationFormat').text = 'None'
assert not check_build_arg_prefix('/Z[:alnum:]') # TODO

# Runtime checks
if '/RTC1' in build_args:
if check_build_arg('/RTC1', remove=True):
ET.SubElement(clconf, 'BasicRuntimeChecks').text = 'EnableFastChecks'
elif '/RTCu' in build_args:
elif check_build_arg('/RTCu', remove=True):
ET.SubElement(clconf, 'BasicRuntimeChecks').text = 'UninitializedLocalUsageCheck'
elif '/RTCs' in build_args:
elif check_build_arg('/RTCs', remove=True):
ET.SubElement(clconf, 'BasicRuntimeChecks').text = 'StackFrameRuntimeCheck'
assert not check_build_arg_prefix('/RTC')

# Exception handling has to be set in the xml in addition to the "AdditionalOptions" because otherwise
# cl will give warning D9025: overriding '/Ehs' with cpp_eh value
if 'cpp' in target.compilers:
Expand All @@ -1349,22 +1389,26 @@
ET.SubElement(clconf, 'ExceptionHandling').text = 'false'
else: # 'sc' or 'default'
ET.SubElement(clconf, 'ExceptionHandling').text = 'Sync'
assert not check_build_arg_prefix('/EH')

if len(target_inc_dirs) > 0:
ET.SubElement(clconf, 'AdditionalIncludeDirectories').text = ';'.join(target_inc_dirs)

if len(target_defines) > 0:
target_defines.append('%(PreprocessorDefinitions)')
ET.SubElement(clconf, 'PreprocessorDefinitions').text = ';'.join(target_defines)

if len(target_args) > 0:
target_args.append('%(AdditionalOptions)')
ET.SubElement(clconf, "AdditionalOptions").text = ' '.join(target_args)
ET.SubElement(clconf, 'AdditionalIncludeDirectories').text = ';'.join(target_inc_dirs)
target_defines.append('%(PreprocessorDefinitions)')
ET.SubElement(clconf, 'PreprocessorDefinitions').text = ';'.join(target_defines)
ET.SubElement(clconf, 'FunctionLevelLinking').text = 'true'

# Warning level
warning_level = T.cast('str', target.get_option(OptionKey('warning_level')))
warning_level = 'EnableAllWarnings' if warning_level == 'everything' else 'Level' + str(1 + int(warning_level))
ET.SubElement(clconf, 'WarningLevel').text = warning_level
if target.get_option(OptionKey('werror')):
ET.SubElement(clconf, 'TreatWarningAsError').text = 'true'

# Optimization flags
o_flags = split_o_flags_args(build_args)
o_flags = split_o_flags_args(build_args, remove=True)
if '/Ox' in o_flags:
ET.SubElement(clconf, 'Optimization').text = 'Full'
elif '/O2' in o_flags:
Expand All @@ -1379,22 +1423,52 @@
ET.SubElement(clconf, 'InlineFunctionExpansion').text = 'OnlyExplicitInline'
elif '/Ob2' in o_flags:
ET.SubElement(clconf, 'InlineFunctionExpansion').text = 'AnySuitable'
assert not check_build_arg_prefix('/O')

# Size-preserving flags
if '/Os' in o_flags or '/O1' in o_flags:
ET.SubElement(clconf, 'FavorSizeOrSpeed').text = 'Size'
# Note: setting FavorSizeOrSpeed with clang-cl conflicts with /Od and can make debugging difficult, so don't.
elif '/Od' not in o_flags:
ET.SubElement(clconf, 'FavorSizeOrSpeed').text = 'Speed'
# Note: SuppressStartupBanner is /NOLOGO and is 'true' by default

self.generate_lang_standard_info(file_args, clconf)

# SuppressStartupBanner is a boolean prop and defaults to true
if not check_build_arg('/nologo', remove=True):
ET.SubElement(clconf, 'SuppressStartupBanner').text = 'false'

# Anything else goes in AdditionalOptions
if len(build_args) > 0:
build_args.append('%(AdditionalOptions)')
ET.SubElement(clconf, "AdditionalOptions").text = ' '.join(build_args)

resourcecompile = ET.SubElement(compiles, 'ResourceCompile')
ET.SubElement(resourcecompile, 'PreprocessorDefinitions')

# Linker options
link = ET.SubElement(compiles, 'Link')
extra_link_args = compiler.compiler_args()
extra_link_args = compiler.get_linker_always_args()
extra_link_args += compiler.compiler_args()
extra_link_args += compiler.get_optimization_link_args(self.optimization)

# Note that cl.exe arguments are case sensitive, but link.exe arguments are not.
def check_link_arg(name, remove = True):
index = None
for i, value in enumerate(extra_link_args):
if value.upper() == name.upper():
index = i
break
if index and remove:
extra_link_args.pop(index)
return index is not None

def check_link_arg_prefix(prefix):

Check notice

Code scanning / CodeQL

Unused local variable Note

Variable check_link_arg_prefix is not used.
for a in extra_link_args:
if a.upper().startswith(prefix.upper()):
return True
return False

# Generate Debug info
if self.debug:
self.generate_debug_information(link)
Expand Down Expand Up @@ -1489,9 +1563,6 @@
for lib in self.get_custom_target_provided_libraries(target):
additional_links.append(self.relpath(lib, self.get_target_dir(target)))

if len(extra_link_args) > 0:
extra_link_args.append('%(AdditionalOptions)')
ET.SubElement(link, "AdditionalOptions").text = ' '.join(extra_link_args)
if len(additional_libpaths) > 0:
additional_libpaths.insert(0, '%(AdditionalLibraryDirectories)')
ET.SubElement(link, 'AdditionalLibraryDirectories').text = ';'.join(additional_libpaths)
Expand Down Expand Up @@ -1531,12 +1602,18 @@
targetmachine.text = 'MachineARM64EC'
else:
raise MesonException('Unsupported Visual Studio target machine: ' + targetplatform)
# /nologo
ET.SubElement(link, 'SuppressStartupBanner').text = 'true'
# /release
if not target.get_option(OptionKey('debug')):

if not check_link_arg('/NOLOGO', remove=True):
ET.SubElement(link, 'SuppressStartupBanner').text = 'false'

if check_link_arg('/RELEASE', remove=True):
ET.SubElement(link, 'SetChecksum').text = 'true'

# Remaining arguments in AdditionalOptions
if len(extra_link_args) > 0:
extra_link_args.append('%(AdditionalOptions)')
ET.SubElement(link, "AdditionalOptions").text = ' '.join(extra_link_args)

# Visual studio doesn't simply allow the src files of a project to be added with the 'Condition=...' attribute,
# to allow us to point to the different debug/debugoptimized/release sets of generated src files for each of
# the solution's configurations. Similarly, 'ItemGroup' also doesn't support 'Condition'. So, without knowing
Expand Down Expand Up @@ -2089,6 +2166,7 @@
self.add_project_reference(root, regen_vcxproj, self.environment.coredata.regen_guid)

def generate_lang_standard_info(self, file_args: T.Dict[str, CompilerArgs], clconf: ET.Element) -> None:
# virtual method implemented in vs20XXbackend subclass
pass

# Returns if a target generates a manifest or not.
Expand Down
1 change: 1 addition & 0 deletions mesonbuild/backend/xcodebackend.py
Original file line number Diff line number Diff line change
Expand Up @@ -832,6 +832,7 @@ def generate_pbx_build_rule(self, objects_dict: PbxDict) -> None:
input='"$SCRIPT_INPUT_FILE"',
depfile='"$(dirname "$SCRIPT_OUTPUT_FILE_0")/$(basename "$SCRIPT_OUTPUT_FILE_0" .o).d"',
extras=['$OTHER_INPUT_FILE_FLAGS'])
compiler.add_default_build_args(commands)
buildrule.add_item('script', self.to_shell_script(commands))
objects_dict.add_item(idval, buildrule, 'PBXBuildRule')

Expand Down
9 changes: 9 additions & 0 deletions mesonbuild/compilers/compilers.py
Original file line number Diff line number Diff line change
Expand Up @@ -782,6 +782,15 @@ def compiler_args(self, args: T.Optional[T.Iterable[str]] = None) -> CompilerArg
"""Return an appropriate CompilerArgs instance for this class."""
return CompilerArgs(self, args)

def add_default_build_args(self, args: CompilerArgs) -> None:
"""Append Meson default compiler arguments.

Because Meson default arguments can be overriden by the user, this method
must be called after building the complete arguments list. This method
checks all the previously added arguments.
"""
pass

@contextlib.contextmanager
def compile(self, code: 'mesonlib.FileOrString',
extra_args: T.Union[None, CompilerArgs, T.List[str]] = None,
Expand Down
Loading
Loading