diff options
Diffstat (limited to 'mesonbuild/backend')
-rw-r--r-- | mesonbuild/backend/backends.py | 70 | ||||
-rw-r--r-- | mesonbuild/backend/ninjabackend.py | 252 | ||||
-rw-r--r-- | mesonbuild/backend/vs2010backend.py | 189 |
3 files changed, 335 insertions, 176 deletions
diff --git a/mesonbuild/backend/backends.py b/mesonbuild/backend/backends.py index 6f8a50e..4988f28 100644 --- a/mesonbuild/backend/backends.py +++ b/mesonbuild/backend/backends.py @@ -21,6 +21,7 @@ from .. import compilers import json import subprocess from ..mesonlib import MesonException, get_compiler_for_source, classify_unity_sources +from ..compilers import CompilerArgs class CleanTrees: ''' @@ -338,32 +339,59 @@ class Backend: return extra_args def generate_basic_compiler_args(self, target, compiler, no_warn_args=False): - commands = [] + # Create an empty commands list, and start adding arguments from + # various sources in the order in which they must override each other + # starting from hard-coded defaults followed by build options and so on. + commands = CompilerArgs(compiler) + # First, the trivial ones that are impossible to override. + # + # Add -nostdinc/-nostdinc++ if needed; can't be overriden commands += self.get_cross_stdlib_args(target, compiler) + # Add things like /NOLOGO or -pipe; usually can't be overriden commands += compiler.get_always_args() + # Only add warning-flags by default if the buildtype enables it, and if + # we weren't explicitly asked to not emit warnings (for Vala, f.ex) if no_warn_args: commands += compiler.get_no_warn_args() elif self.environment.coredata.get_builtin_option('buildtype') != 'plain': commands += compiler.get_warn_args(self.environment.coredata.get_builtin_option('warning_level')) + # Add -Werror if werror=true is set in the build options set on the + # command-line or default_options inside project(). This only sets the + # action to be done for warnings if/when they are emitted, so it's ok + # to set it after get_no_warn_args() or get_warn_args(). + if self.environment.coredata.get_builtin_option('werror'): + commands += compiler.get_werror_args() + # Add compile args for c_* or cpp_* build options set on the + # command-line or default_options inside project(). commands += compiler.get_option_compile_args(self.environment.coredata.compiler_options) - commands += self.build.get_global_args(compiler) + # Add buildtype args: optimization level, debugging, etc. + commands += compiler.get_buildtype_args(self.environment.coredata.get_builtin_option('buildtype')) + # Add compile args added using add_project_arguments() commands += self.build.get_project_args(compiler, target.subproject) + # Add compile args added using add_global_arguments() + # These override per-project arguments + commands += self.build.get_global_args(compiler) + # Compile args added from the env: CFLAGS/CXXFLAGS, etc. We want these + # to override all the defaults, but not the per-target compile args. commands += self.environment.coredata.external_args[compiler.get_language()] - commands += self.escape_extra_args(compiler, target.get_extra_args(compiler.get_language())) - commands += compiler.get_buildtype_args(self.environment.coredata.get_builtin_option('buildtype')) - if self.environment.coredata.get_builtin_option('werror'): - commands += compiler.get_werror_args() + # Always set -fPIC for shared libraries if isinstance(target, build.SharedLibrary): commands += compiler.get_pic_args() + # Set -fPIC for static libraries by default unless explicitly disabled if isinstance(target, build.StaticLibrary) and target.pic: commands += compiler.get_pic_args() + # Add compile args needed to find external dependencies + # Link args are added while generating the link command for dep in target.get_external_deps(): - # Cflags required by external deps might have UNIX-specific flags, - # so filter them out if needed - commands += compiler.unix_compile_flags_to_native(dep.get_compile_args()) + commands += dep.get_compile_args() + # Qt needs -fPIC for executables + # XXX: We should move to -fPIC for all executables if isinstance(target, build.Executable): - commands += dep.get_exe_args() - + commands += dep.get_exe_args(compiler) + # For 'automagic' deps: Boost and GTest. Also dependency('threads'). + # pkg-config puts the thread flags itself via `Cflags:` + if dep.need_threads(): + commands += compiler.thread_flags() # Fortran requires extra include directives. if compiler.language == 'fortran': for lt in target.link_targets: @@ -664,23 +692,3 @@ class Backend: for s in self.build.postconf_scripts: cmd = s['exe'] + s['args'] subprocess.check_call(cmd, env=child_env) - - # Subprojects of subprojects may cause the same dep args to be used - # multiple times. Remove duplicates here. Note that we can't dedup - # libraries based on name alone, because "-lfoo -lbar -lfoo" is - # a completely valid (though pathological) sequence and removing the - # latter may fail. Usually only applies to static libs, though. - def dedup_arguments(self, commands): - includes = {} - final_commands = [] - previous = '-fsuch_arguments=woof' - for c in commands: - if c.startswith(('-I', '-L', '/LIBPATH')): - if c in includes: - continue - includes[c] = True - if previous == c: - continue - previous = c - final_commands.append(c) - return final_commands diff --git a/mesonbuild/backend/ninjabackend.py b/mesonbuild/backend/ninjabackend.py index 628718f..9444087 100644 --- a/mesonbuild/backend/ninjabackend.py +++ b/mesonbuild/backend/ninjabackend.py @@ -19,6 +19,7 @@ from .. import build from .. import mlog from .. import dependencies from .. import compilers +from ..compilers import CompilerArgs from ..mesonlib import File, MesonException, get_compiler_for_source, Popen_safe from .backends import CleanTrees, InstallData from ..build import InvalidArguments @@ -1725,7 +1726,7 @@ rule FORTRAN_DEP_HACK def generate_llvm_ir_compile(self, target, outfile, src): compiler = get_compiler_for_source(target.compilers.values(), src) - commands = [] + commands = CompilerArgs(compiler) # Compiler args for compiling this target commands += compilers.get_base_compile_args(self.environment.coredata.base_options, compiler) @@ -1748,11 +1749,40 @@ rule FORTRAN_DEP_HACK # Write the Ninja build command compiler_name = 'llvm_ir{}_COMPILER'.format('_CROSS' if target.is_cross else '') element = NinjaBuildElement(self.all_outputs, rel_obj, compiler_name, rel_src) - commands = self.dedup_arguments(commands) + # Convert from GCC-style link argument naming to the naming used by the + # current compiler. + commands = commands.to_native() element.add_item('ARGS', commands) element.write(outfile) return rel_obj + def get_source_dir_include_args(self, target, compiler): + curdir = target.get_subdir() + tmppath = os.path.normpath(os.path.join(self.build_to_src, curdir)) + return compiler.get_include_args(tmppath, False) + + def get_build_dir_include_args(self, target, compiler): + curdir = target.get_subdir() + if curdir == '': + curdir = '.' + return compiler.get_include_args(curdir, False) + + def get_custom_target_dir_include_args(self, target, compiler): + custom_target_include_dirs = [] + for i in target.get_generated_sources(): + # Generator output goes into the target private dir which is + # already in the include paths list. Only custom targets have their + # own target build dir. + if not isinstance(i, build.CustomTarget): + continue + idir = self.get_target_dir(i) + if idir not in custom_target_include_dirs: + custom_target_include_dirs.append(idir) + incs = [] + for i in custom_target_include_dirs: + incs += compiler.get_include_args(i, False) + return incs + def generate_single_compile(self, target, outfile, src, is_generated=False, header_deps=[], order_deps=[]): """ Compiles C/C++, ObjC/ObjC++, Fortran, and D sources @@ -1763,30 +1793,40 @@ rule FORTRAN_DEP_HACK raise AssertionError('BUG: sources should not contain headers {!r}'.format(src.fname)) extra_orderdeps = [] compiler = get_compiler_for_source(target.compilers.values(), src) - commands = [] - # The first thing is implicit include directories: source, build and private. - commands += compiler.get_include_args(self.get_target_private_dir(target), False) - # Compiler args for compiling this target + + # Create an empty commands list, and start adding arguments from + # various sources in the order in which they must override each other + commands = CompilerArgs(compiler) + # Add compiler args for compiling this target derived from 'base' build + # options passed on the command-line, in default_options, etc. + # These have the lowest priority. commands += compilers.get_base_compile_args(self.environment.coredata.base_options, compiler) - # Add the root source and build directories as include dirs - curdir = target.get_subdir() - tmppath = os.path.normpath(os.path.join(self.build_to_src, curdir)) - src_inc = compiler.get_include_args(tmppath, False) - if curdir == '': - curdir = '.' - build_inc = compiler.get_include_args(curdir, False) - commands += build_inc + src_inc - # -I args work differently than other ones. In them the first found - # directory is used whereas for other flags (such as -ffoo -fno-foo) the - # latest one is used. Therefore put the internal include directories - # here before generating the "basic compiler args" so they override args - # coming from e.g. pkg-config. + # The code generated by valac is usually crap and has tons of unused + # variables and such, so disable warnings for Vala C sources. + no_warn_args = (is_generated == 'vala') + # Add compiler args and include paths from several sources; defaults, + # build options, external dependencies, etc. + commands += self.generate_basic_compiler_args(target, compiler, no_warn_args) + # 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. for i in target.get_include_dirs(): basedir = i.get_curdir() for d in i.get_incdirs(): - expdir = os.path.join(basedir, d) + # Avoid superfluous '/.' at the end of paths when d is '.' + if d not in ('', '.'): + expdir = os.path.join(basedir, d) + else: + expdir = basedir srctreedir = os.path.join(self.build_to_src, expdir) + # Add source subdir first so that the build subdir overrides it + sargs = compiler.get_include_args(srctreedir, i.is_system) + commands += sargs # There may be include dirs where a build directory has not been # created for some source dir. For example if someone does this: # @@ -1797,20 +1837,32 @@ rule FORTRAN_DEP_HACK bargs = compiler.get_include_args(expdir, i.is_system) else: bargs = [] - sargs = compiler.get_include_args(srctreedir, i.is_system) commands += bargs - commands += sargs for d in i.get_extra_build_dirs(): commands += compiler.get_include_args(d, i.is_system) - commands += self.generate_basic_compiler_args(target, compiler, - # The code generated by valac is usually crap - # and has tons of unused variables and such, - # so disable warnings for Vala C sources. - no_warn_args=(is_generated == 'vala')) - for d in target.external_deps: - if d.need_threads(): - commands += compiler.thread_flags() - break + # Add per-target compile args, f.ex, `c_args : ['-DFOO']`. We set these + # near the end since these are supposed to override everything else. + commands += self.escape_extra_args(compiler, + target.get_extra_args(compiler.get_language())) + # Add source dir and build dir. Project-specific and target-specific + # include paths must override per-target compile args, include paths + # from external dependencies, internal dependencies, and from + # per-target `include_directories:` + # + # We prefer headers in the build dir and the custom target dir over the + # source dir since, for instance, the user might have an + # srcdir == builddir Autotools build in their source tree. Many + # projects that are moving to Meson have both Meson and Autotools in + # parallel as part of the transition. + commands += self.get_source_dir_include_args(target, compiler) + commands += self.get_custom_target_dir_include_args(target, compiler) + commands += self.get_build_dir_include_args(target, compiler) + # 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) + + # FIXME: This file handling is atrocious and broken. We need to + # replace it with File objects used consistently everywhere. if isinstance(src, RawFilename): rel_src = src.fname if os.path.isabs(src.fname): @@ -1835,7 +1887,13 @@ rule FORTRAN_DEP_HACK rel_obj = os.path.join(self.get_target_private_dir(target), obj_basename) rel_obj += '.' + self.environment.get_object_suffix() dep_file = compiler.depfile_for_object(rel_obj) + + # Add MSVC debug file generation compile flags: /Fd /FS + commands += self.get_compile_debugfile_args(compiler, target, rel_obj) + + # PCH handling if self.environment.coredata.base_options.get('b_pch', False): + commands += self.get_pch_include_args(compiler, target) pchlist = target.get_pch(compiler.language) else: pchlist = [] @@ -1848,19 +1906,7 @@ rule FORTRAN_DEP_HACK i = os.path.join(self.get_target_private_dir(target), compiler.get_pch_name(pchlist[0])) arr.append(i) pch_dep = arr - custom_target_include_dirs = [] - for i in target.get_generated_sources(): - if not isinstance(i, build.CustomTarget): - continue - idir = self.get_target_dir(i) - if idir not in custom_target_include_dirs: - custom_target_include_dirs.append(idir) - for i in custom_target_include_dirs: - commands += compiler.get_include_args(i, False) - if self.environment.coredata.base_options.get('b_pch', False): - commands += self.get_pch_include_args(compiler, target) - commands += self.get_compile_debugfile_args(compiler, target, rel_obj) crstr = '' if target.is_cross: crstr = '_CROSS' @@ -1895,7 +1941,9 @@ rule FORTRAN_DEP_HACK element.add_orderdep(d) element.add_orderdep(pch_dep) element.add_orderdep(extra_orderdeps) - commands = self.dedup_arguments(commands) + # Convert from GCC-style link argument naming to the naming used by the + # current compiler. + commands = commands.to_native() for i in self.get_fortran_orderdeps(target, compiler): element.add_orderdep(i) element.add_item('DEPFILE', dep_file) @@ -1995,47 +2043,23 @@ rule FORTRAN_DEP_HACK return [] return linker.get_no_stdlib_link_args() - def generate_link(self, target, outfile, outname, obj_list, linker, extra_args=[]): - if isinstance(target, build.StaticLibrary): - linker_base = 'STATIC' - else: - linker_base = linker.get_language() # Fixme. - if isinstance(target, build.SharedLibrary): - self.generate_shsym(outfile, target) - crstr = '' - if target.is_cross: - crstr = '_CROSS' - linker_rule = linker_base + crstr + '_LINKER' + def get_target_type_link_args(self, target, linker): abspath = os.path.join(self.environment.get_build_dir(), target.subdir) commands = [] - if not isinstance(target, build.StaticLibrary): - commands += self.build.get_project_link_args(linker, target.subproject) - commands += self.build.get_global_link_args(linker) - commands += self.get_cross_stdlib_link_args(target, linker) - commands += linker.get_linker_always_args() - if not isinstance(target, build.StaticLibrary): - commands += compilers.get_base_link_args(self.environment.coredata.base_options, - linker, - isinstance(target, build.SharedModule)) - commands += linker.get_buildtype_linker_args(self.environment.coredata.get_builtin_option('buildtype')) - commands += linker.get_option_link_args(self.environment.coredata.compiler_options) - commands += self.get_link_debugfile_args(linker, target, outname) - if not(isinstance(target, build.StaticLibrary)): - commands += self.environment.coredata.external_link_args[linker.get_language()] if isinstance(target, build.Executable): + # Currently only used with the Swift compiler to add '-emit-executable' commands += linker.get_std_exe_link_args() elif isinstance(target, build.SharedLibrary): if isinstance(target, build.SharedModule): commands += linker.get_std_shared_module_link_args() else: commands += linker.get_std_shared_lib_link_args() + # All shared libraries are PIC commands += linker.get_pic_args() - if hasattr(target, 'soversion'): - soversion = target.soversion - else: - soversion = None + # Add -Wl,-soname arguments on Linux, -install_name on OS X commands += linker.get_soname_args(target.prefix, target.name, target.suffix, - abspath, soversion, isinstance(target, build.SharedModule)) + abspath, target.soversion, + isinstance(target, build.SharedModule)) # This is only visited when using the Visual Studio toolchain if target.vs_module_defs and hasattr(linker, 'gen_vs_module_defs_args'): commands += linker.gen_vs_module_defs_args(target.vs_module_defs.rel_to_builddir(self.build_to_src)) @@ -2046,17 +2070,75 @@ rule FORTRAN_DEP_HACK commands += linker.get_std_link_args() else: raise RuntimeError('Unknown build target type.') - # Link arguments of static libraries are not put in the command line of - # the library. They are instead appended to the command line where - # the static library is used. + return commands + + def generate_link(self, target, outfile, outname, obj_list, linker, extra_args=[]): + if isinstance(target, build.StaticLibrary): + linker_base = 'STATIC' + else: + linker_base = linker.get_language() # Fixme. + if isinstance(target, build.SharedLibrary): + self.generate_shsym(outfile, target) + crstr = '' + if target.is_cross: + crstr = '_CROSS' + linker_rule = linker_base + crstr + '_LINKER' + + # Create an empty commands list, and start adding link arguments from + # various sources in the order in which they must override each other + # starting from hard-coded defaults followed by build options and so on. + # + # Once all the linker options have been passed, we will start passing + # libraries and library paths from internal and external sources. + commands = CompilerArgs(linker) + # First, the trivial ones that are impossible to override. + # + # Add linker args for linking this target derived from 'base' build + # options passed on the command-line, in default_options, etc. + # These have the lowest priority. + if not isinstance(target, build.StaticLibrary): + commands += compilers.get_base_link_args(self.environment.coredata.base_options, + linker, + isinstance(target, build.SharedModule)) + # Add -nostdlib if needed; can't be overriden + commands += self.get_cross_stdlib_link_args(target, linker) + # Add things like /NOLOGO; usually can't be overriden + commands += linker.get_linker_always_args() + # Add buildtype linker args: optimization level, etc. + commands += linker.get_buildtype_linker_args(self.environment.coredata.get_builtin_option('buildtype')) + # Add /DEBUG and the pdb filename when using MSVC + commands += self.get_link_debugfile_args(linker, target, outname) + # Add link args specific to this BuildTarget type, such as soname args, + # PIC, import library generation, etc. + commands += self.get_target_type_link_args(target, linker) + if not isinstance(target, build.StaticLibrary): + # Add link args added using add_project_link_arguments() + commands += self.build.get_project_link_args(linker, target.subproject) + # Add link args added using add_global_link_arguments() + # These override per-project link arguments + commands += self.build.get_global_link_args(linker) + # Link args added from the env: LDFLAGS. We want these to + # override all the defaults but not the per-target link args. + commands += self.environment.coredata.external_link_args[linker.get_language()] + + # Now we will add libraries and library paths from various sources + + # Add link args to link to all internal libraries (link_with:) and + # internal dependencies needed by this target. if linker_base == 'STATIC': + # Link arguments of static libraries are not put in the command + # line of the library. They are instead appended to the command + # line where the static library is used. dependencies = [] else: dependencies = target.get_dependencies() commands += self.build_target_link_arguments(linker, dependencies) + # For 'automagic' deps: Boost and GTest. Also dependency('threads'). + # pkg-config puts the thread flags itself via `Cflags:` for d in target.external_deps: if d.need_threads(): commands += linker.thread_link_flags() + # Only non-static built targets need link args and link dependencies if not isinstance(target, build.StaticLibrary): commands += target.link_args # External deps must be last because target link libraries may depend on them. @@ -2066,12 +2148,24 @@ rule FORTRAN_DEP_HACK if isinstance(d, build.StaticLibrary): for dep in d.get_external_deps(): commands += dep.get_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. + commands += linker.get_option_link_args(self.environment.coredata.compiler_options) + # Set runtime-paths so we can run executables without needing to set + # LD_LIBRARY_PATH, etc in the environment. Doesn't work on Windows. commands += linker.build_rpath_args(self.environment.get_build_dir(), - self.determine_rpath_dirs(target), target.install_rpath) + self.determine_rpath_dirs(target), + target.install_rpath) + # Add libraries generated by custom targets custom_target_libraries = self.get_custom_target_provided_libraries(target) commands += extra_args commands += custom_target_libraries - commands = linker.unix_link_flags_to_native(self.dedup_arguments(commands)) + # Convert from GCC-style link argument naming to the naming used by the + # current compiler. + commands = commands.to_native() dep_targets = [self.get_dependency_filename(t) for t in dependencies] dep_targets += [os.path.join(self.environment.source_dir, target.subdir, t) for t in target.link_depends] diff --git a/mesonbuild/backend/vs2010backend.py b/mesonbuild/backend/vs2010backend.py index 61f755b..3b79a9c 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,90 +670,132 @@ 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_compile_flags_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 - d_compile_args = compiler.unix_compile_flags_to_native(d.get_compile_args()) + d_compile_args = compiler.unix_args_to_native(d.get_compile_args()) for arg in d_compile_args: 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)') ET.SubElement(clconf, 'PreprocessorDefinitions').text = ';'.join(target_defines) - rebuild = ET.SubElement(clconf, 'MinimalRebuild') - rebuild.text = 'true' - funclink = ET.SubElement(clconf, 'FunctionLevelLinking') - funclink.text = 'true' + ET.SubElement(clconf, 'MinimalRebuild').text = 'true' + ET.SubElement(clconf, 'FunctionLevelLinking').text = 'true' pch_node = ET.SubElement(clconf, 'PrecompiledHeader') + if self.environment.coredata.get_builtin_option('werror'): + ET.SubElement(clconf, 'TreatWarningAsError').text = 'true' + # Note: SuppressStartupBanner is /NOLOGO and is 'true' by default pch_sources = {} for lang in ['c', 'cpp']: pch = target.get_pch(lang) @@ -773,19 +817,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 +845,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_link_flags_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) |