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: Get always args #13929

Draft
wants to merge 2 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
89 changes: 73 additions & 16 deletions mesonbuild/backend/vs2010backend.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,25 +66,37 @@ def autodetect_vs_version(build: T.Optional[build.Build], interpreter: T.Optiona
'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 i in range(len(args)):
arg = args[i]

if not arg.startswith('/O'):
continue

indexes.append(i)

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 @@ -1118,7 +1130,8 @@ def get_args_defines_and_inc_dirs(self, target, compiler, generated_files_includ

@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 +1286,7 @@ def add_non_makefile_vcxproj_elements(
target,
platform: str,
subsystem,
build_args,
build_args_,
target_args,
target_defines,
target_inc_dirs,
Expand All @@ -1282,6 +1295,30 @@ def add_non_makefile_vcxproj_elements(
compiler = self._get_cl_compiler(target)
buildtype_link_args = compiler.get_optimization_link_args(self.optimization)

build_args_copy = build_args_.copy()

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

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

def get_remaining_build_args():
result = ' '.join(build_args_copy)
# TODO: Ensure we don't use build_args_copy anymore
# build_args_copy = None
return result

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

Expand Down Expand Up @@ -1318,25 +1355,31 @@ def add_non_makefile_vcxproj_elements(
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(check_build_arg_prefix('/Z[:alnum:]') == False) # 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(check_build_arg_prefix('/RTC') == False)

# 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,6 +1392,7 @@ def add_non_makefile_vcxproj_elements(
ET.SubElement(clconf, 'ExceptionHandling').text = 'false'
else: # 'sc' or 'default'
ET.SubElement(clconf, 'ExceptionHandling').text = 'Sync'
assert(check_build_arg_prefix('/EH') == False)

if len(target_args) > 0:
target_args.append('%(AdditionalOptions)')
Expand All @@ -1363,8 +1407,9 @@ def add_non_makefile_vcxproj_elements(
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_copy, remove=True)
if '/Ox' in o_flags:
ET.SubElement(clconf, 'Optimization').text = 'Full'
elif '/O2' in o_flags:
Expand All @@ -1379,15 +1424,24 @@ def add_non_makefile_vcxproj_elements(
ET.SubElement(clconf, 'InlineFunctionExpansion').text = 'OnlyExplicitInline'
elif '/Ob2' in o_flags:
ET.SubElement(clconf, 'InlineFunctionExpansion').text = 'AnySuitable'
assert(check_build_arg_prefix('/O') == False)

# 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)

# todo
have_no_logo = check_build_arg('/nologo', remove=True)

additional = get_remaining_build_args()
if additional:
ET.SubElement(clconf, 'AdditionalOptions').text = additional

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

Expand Down Expand Up @@ -1531,8 +1585,10 @@ def add_non_makefile_vcxproj_elements(
targetmachine.text = 'MachineARM64EC'
else:
raise MesonException('Unsupported Visual Studio target machine: ' + targetplatform)
# /nologo
ET.SubElement(link, 'SuppressStartupBanner').text = 'true'

if have_no_logo:
ET.SubElement(link, 'SuppressStartupBanner').text = 'true'

# /release
if not target.get_option(OptionKey('debug')):
ET.SubElement(link, 'SetChecksum').text = 'true'
Expand Down Expand Up @@ -2089,6 +2145,7 @@ def add_regen_dependency(self, root: ET.Element) -> None:
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
5 changes: 0 additions & 5 deletions unittests/windowstests.py
Original file line number Diff line number Diff line change
Expand Up @@ -437,11 +437,6 @@ def test_modules(self):
self.build()

def test_non_utf8_fails(self):
# FIXME: VS backend does not use flags from compiler.get_always_args()
# and thus it's missing /utf-8 argument. Was that intentional? This needs
# to be revisited.
if self.backend is not Backend.ninja:
raise SkipTest(f'This test only pass with ninja backend (not {self.backend.name}).')
testdir = os.path.join(self.platform_test_dir, '18 msvc charset')
env = get_fake_env(testdir, self.builddir, self.prefix)
cc = detect_c_compiler(env, MachineChoice.HOST)
Expand Down
Loading