diff --git a/mesonbuild/backend/vs2010backend.py b/mesonbuild/backend/vs2010backend.py index 31a4c0e86..f0d125d95 100644 --- a/mesonbuild/backend/vs2010backend.py +++ b/mesonbuild/backend/vs2010backend.py @@ -23,6 +23,7 @@ from .. import dependencies from .. import mlog from .. import compilers from ..build import BuildTarget +from ..compilers import CompilerArgs from ..mesonlib import MesonException, File from ..environment import Environment @@ -426,24 +427,25 @@ class Vs2010Backend(backends.Backend): pch_out.text = '$(IntDir)$(TargetName)-%s.pch' % lang def add_additional_options(self, lang, parent_node, file_args): - if len(file_args[lang]) == 0: - # We only need per file options if they were not set per project. - return - args = file_args[lang] + ['%(AdditionalOptions)'] + args = [] + for arg in file_args[lang].to_native(): + if arg == '%(AdditionalOptions)': + args.append(arg) + else: + args.append(self.escape_additional_option(arg)) ET.SubElement(parent_node, "AdditionalOptions").text = ' '.join(args) def add_preprocessor_defines(self, lang, parent_node, file_defines): - if len(file_defines[lang]) == 0: - # We only need per file options if they were not set per project. - return - defines = file_defines[lang] + ['%(PreprocessorDefinitions)'] + defines = [] + for define in file_defines[lang]: + if define == '%(PreprocessorDefinitions)': + defines.append(define) + else: + defines.append(self.escape_preprocessor_define(define)) ET.SubElement(parent_node, "PreprocessorDefinitions").text = ';'.join(defines) def add_include_dirs(self, lang, parent_node, file_inc_dirs): - if len(file_inc_dirs[lang]) == 0: - # We only need per file options if they were not set per project. - return - dirs = file_inc_dirs[lang] + ['%(AdditionalIncludeDirectories)'] + dirs = file_inc_dirs[lang] ET.SubElement(parent_node, "AdditionalIncludeDirectories").text = ';'.join(dirs) @staticmethod @@ -668,29 +670,98 @@ class Vs2010Backend(backends.Backend): # Arguments, include dirs, defines for all files in the current target target_args = [] target_defines = [] - target_inc_dirs = ['.', self.relpath(self.get_target_private_dir(target), - self.get_target_dir(target)), - proj_to_src_dir] + generated_files_include_dirs + target_inc_dirs = [] # Arguments, include dirs, defines passed to individual files in # a target; perhaps because the args are language-specific - file_args = dict((lang, []) for lang in target.compilers) + # + # file_args is also later split out into defines and include_dirs in + # case someone passed those in there + file_args = dict((lang, CompilerArgs(comp)) for lang, comp in target.compilers.items()) file_defines = dict((lang, []) for lang in target.compilers) file_inc_dirs = dict((lang, []) for lang in target.compilers) - for l, args in self.environment.coredata.external_args.items(): + # The order in which these compile args are added must match + # generate_single_compile() and generate_basic_compiler_args() + for l, comp in target.compilers.items(): + if l in file_args: + file_args[l] += compilers.get_base_compile_args(self.environment.coredata.base_options, comp) + file_args[l] += comp.get_option_compile_args(self.environment.coredata.compiler_options) + # Add compile args added using add_project_arguments() + for l, args in self.build.projects_args.get(target.subproject, {}).items(): if l in file_args: file_args[l] += args + # Add compile args added using add_global_arguments() + # These override per-project arguments for l, args in self.build.global_args.items(): if l in file_args: file_args[l] += args - for l, args in self.build.projects_args.get(target.subproject, {}).items(): + # Compile args added from the env: CFLAGS/CXXFLAGS, etc. We want these + # to override all the defaults, but not the per-target compile args. + for l, args in self.environment.coredata.external_args.items(): if l in file_args: file_args[l] += args + 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). + args += ['%(AdditionalOptions)', '%(PreprocessorDefinitions)', '%(AdditionalIncludeDirectories)'] + # Add include dirs from the `include_directories:` kwarg on the target + # and from `include_directories:` of internal deps of the target. + # + # Target include dirs should override internal deps include dirs. + # + # Include dirs from internal deps should override include dirs from + # external deps. + # These are per-target, but we still add them as per-file because we + # need them to be looked in first. + for d in target.get_include_dirs(): + for i in d.get_incdirs(): + curdir = os.path.join(d.get_curdir(), i) + args.append('-I' + self.relpath(curdir, target.subdir)) # build dir + args.append('-I' + os.path.join(proj_to_src_root, curdir)) # src dir + for i in d.get_extra_build_dirs(): + curdir = os.path.join(d.get_curdir(), i) + args.append('-I' + self.relpath(curdir, target.subdir)) # build dir + # Add per-target compile args, f.ex, `c_args : ['/DFOO']`. We set these + # near the end since these are supposed to override everything else. for l, args in target.extra_args.items(): if l in file_args: - file_args[l] += compiler.unix_args_to_native(args) - for l, comp in target.compilers.items(): - if l in file_args: - file_args[l] += comp.get_option_compile_args(self.environment.coredata.compiler_options) + file_args[l] += args + # The highest priority includes. In order of directory search: + # target private dir, target build dir, generated sources include dirs, + # target source dir + for args in file_args.values(): + t_inc_dirs = ['.', self.relpath(self.get_target_private_dir(target), + self.get_target_dir(target))] + t_inc_dirs += generated_files_include_dirs + [proj_to_src_dir] + args += ['-I' + arg for arg in t_inc_dirs] + + # Split preprocessor defines and include directories out of the list of + # all extra arguments. The rest go into %(AdditionalOptions). + for l, args in file_args.items(): + for arg in args[:]: + if arg.startswith(('-D', '/D')) or arg == '%(PreprocessorDefinitions)': + file_args[l].remove(arg) + # Don't escape the marker + if arg == '%(PreprocessorDefinitions)': + define = arg + else: + define = arg[2:] + # De-dup + if define in file_defines[l]: + file_defines[l].remove(define) + file_defines[l].append(define) + elif arg.startswith(('-I', '/I')) or arg == '%(AdditionalIncludeDirectories)': + file_args[l].remove(arg) + # Don't escape the marker + if arg == '%(AdditionalIncludeDirectories)': + inc_dir = arg + else: + inc_dir = arg[2:] + # De-dup + if inc_dir not in file_inc_dirs[l]: + file_inc_dirs[l].append(inc_dir) + + # Split compile args needed to find external dependencies + # Link args are added while generating the link command for d in target.get_external_deps(): # Cflags required by external deps might have UNIX-specific flags, # so filter them out if needed @@ -699,50 +770,22 @@ class Vs2010Backend(backends.Backend): if arg.startswith(('-D', '/D')): define = arg[2:] # De-dup - if define not in target_defines: - target_defines.append(define) + if define in target_defines: + target_defines.remove(define) + target_defines.append(define) elif arg.startswith(('-I', '/I')): inc_dir = arg[2:] # De-dup if inc_dir not in target_inc_dirs: target_inc_dirs.append(inc_dir) else: - # De-dup - if arg not in target_args: - target_args.append(arg) - - # Split preprocessor defines and include directories out of the list of - # all extra arguments. The rest go into %(AdditionalOptions). - for l, args in file_args.items(): - file_args[l] = [] - for arg in args: - if arg.startswith(('-D', '/D')): - define = self.escape_preprocessor_define(arg[2:]) - # De-dup - if define not in file_defines[l]: - file_defines[l].append(define) - elif arg.startswith(('-I', '/I')): - inc_dir = arg[2:] - # De-dup - if inc_dir not in file_inc_dirs[l]: - file_inc_dirs[l].append(inc_dir) - else: - file_args[l].append(self.escape_additional_option(arg)) + target_args.append(arg) languages += gen_langs if len(target_args) > 0: target_args.append('%(AdditionalOptions)') ET.SubElement(clconf, "AdditionalOptions").text = ' '.join(target_args) - for d in target.include_dirs: - for i in d.incdirs: - curdir = os.path.join(d.curdir, i) - target_inc_dirs.append(self.relpath(curdir, target.subdir)) # build dir - target_inc_dirs.append(os.path.join(proj_to_src_root, curdir)) # src dir - for i in d.get_extra_build_dirs(): - curdir = os.path.join(d.curdir, i) - target_inc_dirs.append(self.relpath(curdir, target.subdir)) # build dir - target_inc_dirs.append('%(AdditionalIncludeDirectories)') ET.SubElement(clconf, 'AdditionalIncludeDirectories').text = ';'.join(target_inc_dirs) target_defines.append('%(PreprocessorDefinitions)') @@ -773,19 +816,27 @@ class Vs2010Backend(backends.Backend): resourcecompile = ET.SubElement(compiles, 'ResourceCompile') ET.SubElement(resourcecompile, 'PreprocessorDefinitions') + + # Linker options link = ET.SubElement(compiles, 'Link') - # Put all language args here, too. - extra_link_args = compiler.get_option_link_args(self.environment.coredata.compiler_options) + extra_link_args = CompilerArgs(compiler) # FIXME: Can these buildtype linker args be added as tags in the # vcxproj file (similar to buildtype compiler args) instead of in # AdditionalOptions? extra_link_args += compiler.get_buildtype_linker_args(self.buildtype) - for l in self.environment.coredata.external_link_args.values(): - extra_link_args += l if not isinstance(target, build.StaticLibrary): - extra_link_args += target.link_args if isinstance(target, build.SharedModule): extra_link_args += compiler.get_std_shared_module_link_args() + # Add link args added using add_project_link_arguments() + extra_link_args += self.build.get_project_link_args(compiler, target.subproject) + # Add link args added using add_global_link_arguments() + # These override per-project link arguments + extra_link_args += self.build.get_global_link_args(compiler) + # Link args added from the env: LDFLAGS. We want these to + # override all the defaults but not the per-target link args. + extra_link_args += self.environment.coredata.external_link_args[compiler.get_language()] + # Only non-static built targets need link args and link dependencies + extra_link_args += target.link_args # External deps must be last because target link libraries may depend on them. for dep in target.get_external_deps(): extra_link_args += dep.get_link_args() @@ -793,8 +844,13 @@ class Vs2010Backend(backends.Backend): if isinstance(d, build.StaticLibrary): for dep in d.get_external_deps(): extra_link_args += dep.get_link_args() - extra_link_args = compiler.unix_args_to_native(extra_link_args) - (additional_libpaths, additional_links, extra_link_args) = self.split_link_args(extra_link_args) + # Add link args for c_* or cpp_* build options. Currently this only + # adds c_winlibs and cpp_winlibs when building for Windows. This needs + # to be after all internal and external libraries so that unresolved + # symbols from those can be found here. This is needed when the + # *_winlibs that we want to link to are static mingw64 libraries. + extra_link_args += compiler.get_option_link_args(self.environment.coredata.compiler_options) + (additional_libpaths, additional_links, extra_link_args) = self.split_link_args(extra_link_args.to_native()) if len(extra_link_args) > 0: extra_link_args.append('%(AdditionalOptions)') ET.SubElement(link, "AdditionalOptions").text = ' '.join(extra_link_args) diff --git a/setup.cfg b/setup.cfg index 823c84dfa..b3adc5901 100644 --- a/setup.cfg +++ b/setup.cfg @@ -15,5 +15,7 @@ ignore = # E401: multiple imports on one line E401, # too many leading '#' for block comment - E266 + E266, + # module level import not at top of file + E402 max-line-length = 120