diff options
Diffstat (limited to 'mesonbuild/build.py')
-rw-r--r-- | mesonbuild/build.py | 144 |
1 files changed, 106 insertions, 38 deletions
diff --git a/mesonbuild/build.py b/mesonbuild/build.py index 79759ee..106386c 100644 --- a/mesonbuild/build.py +++ b/mesonbuild/build.py @@ -19,6 +19,7 @@ from . import mlog import copy, os, re from .mesonlib import File, flatten, MesonException, stringlistify, classify_unity_sources from .environment import for_windows, for_darwin +from .compilers import is_object, clike_langs, lang_suffixes known_basic_kwargs = {'install' : True, 'c_pch' : True, @@ -62,17 +63,6 @@ known_lib_kwargs.update({'version' : True, # Only for shared libs 'pic' : True, # Only for static libs }) -def compilers_are_msvc(compilers): - """ - Check if all the listed compilers are MSVC. Used by Executable, - StaticLibrary, and SharedLibrary for deciding when to use MSVC-specific - file naming. - """ - for compiler in compilers.values(): - if compiler.get_id() != 'msvc': - return False - return True - class InvalidArguments(MesonException): pass @@ -88,8 +78,8 @@ class Build: self.environment = environment self.projects = {} self.targets = {} - self.compilers = [] - self.cross_compilers = [] + self.compilers = {} + self.cross_compilers = {} self.global_args = {} self.projects_args = {} self.global_link_args = {} @@ -109,26 +99,19 @@ class Build: self.dep_manifest = {} self.cross_stdlibs = {} - def has_language(self, language): - for i in self.compilers: - if i.get_language() == language: - return True - return False - def add_compiler(self, compiler): if self.static_linker is None and compiler.needs_static_linker(): self.static_linker = self.environment.detect_static_linker(compiler) - if self.has_language(compiler.get_language()): - return - self.compilers.append(compiler) + lang = compiler.get_language() + if lang not in self.compilers: + self.compilers[lang] = compiler def add_cross_compiler(self, compiler): if len(self.cross_compilers) == 0: self.static_cross_linker = self.environment.detect_static_linker(compiler) - for i in self.cross_compilers: - if i.get_language() == compiler.get_language(): - return - self.cross_compilers.append(compiler) + lang = compiler.get_language() + if lang not in self.cross_compilers: + self.cross_compilers[lang] = compiler def get_project(self): return self.projects[''] @@ -204,6 +187,10 @@ class ExtractedObjects(): if is_unity: self.check_unity_compatible() + def __repr__(self): + r = '<{0} {1!r}: {2}>' + return r.format(self.__class__.__name__, self.target.name, self.srclist) + def check_unity_compatible(self): # Figure out if the extracted object list is compatible with a Unity # build. When we're doing a Unified build, we go through the sources, @@ -290,7 +277,14 @@ class BuildTarget(): self.extra_args = {} self.generated = [] self.extra_files = [] + # Sources can be: + # 1. Pre-existing source files in the source tree + # 2. Pre-existing sources generated by configure_file in the build tree + # 3. Sources files generated by another target or a Generator self.process_sourcelist(sources) + # Objects can be: + # 1. Pre-existing objects provided by the user with the `objects:` kwarg + # 2. Compiled objects created by and extracted from another target self.process_objectlist(objects) self.process_kwargs(kwargs, environment) self.check_unknown_kwargs(kwargs) @@ -333,7 +327,7 @@ class BuildTarget(): for s in objects: if hasattr(s, 'held_object'): s = s.held_object - if isinstance(s, (str, ExtractedObjects)): + if isinstance(s, (str, File, ExtractedObjects)): self.objects.append(s) elif isinstance(s, (GeneratedList, CustomTarget)): msg = 'Generated files are not allowed in the \'objects\' kwarg ' + \ @@ -380,19 +374,56 @@ class BuildTarget(): return removed def process_compilers(self): - if len(self.sources) + len(self.generated) == 0: + ''' + Populate self.compilers, which is the list of compilers that this + target will use for compiling all its sources. + We also add compilers that were used by extracted objects to simplify + dynamic linker determination. + ''' + if len(self.sources) + len(self.generated) + len(self.objects) == 0: return - sources = list(self.sources) - for gensrc in self.generated: - sources += gensrc.get_outputs() # Populate list of compilers if self.is_cross: compilers = self.environment.coredata.cross_compilers else: compilers = self.environment.coredata.compilers - for lang, compiler in compilers.items(): - if self.can_compile_sources(compiler, sources): - self.compilers[lang] = compiler + # Pre-existing sources + sources = list(self.sources) + # All generated sources + for gensrc in self.generated: + for s in gensrc.get_outputs(): + # Generated objects can't be compiled, so don't use them for + # compiler detection. If our target only has generated objects, + # we will fall back to using the first c-like compiler we find, + # which is what we need. + if not is_object(s): + sources.append(s) + # Sources that were used to create our extracted objects + for o in self.objects: + if not isinstance(o, ExtractedObjects): + continue + for s in o.srclist: + # Don't add Vala sources since that will pull in the Vala + # compiler even though we will never use it since we are + # dealing with compiled C code. + if not s.endswith(lang_suffixes['vala']): + sources.append(s) + if sources: + # Add compilers based on the above sources + for lang, compiler in compilers.items(): + # We try to be conservative because sometimes people add files + # in the list of sources that we can't determine the type based + # just on the suffix. + if self.can_compile_sources(compiler, sources): + self.compilers[lang] = compiler + else: + # No source files, target consists of only object files of unknown + # origin. Just add the first clike compiler that we have and hope + # that it can link these objects + for lang in clike_langs: + if lang in compilers: + self.compilers[lang] = compilers[lang] + break # If all our sources are Vala, our target also needs the C compiler but # it won't get added above. if 'vala' in self.compilers and 'c' not in self.compilers: @@ -766,6 +797,43 @@ class BuildTarget(): def get_aliaslist(self): return [] + def get_clike_dynamic_linker(self): + ''' + We use the order of languages in `clike_langs` to determine which + linker to use in case the target has sources compiled with multiple + compilers. All languages other than those in this list have their own + linker. + Note that Vala outputs C code, so Vala sources can use any linker + that can link compiled C. We don't actually need to add an exception + for Vala here because of that. + ''' + for l in clike_langs: + if l in self.compilers: + return self.compilers[l] + + def get_using_msvc(self): + ''' + Check if the dynamic linker is MSVC. Used by Executable, StaticLibrary, + and SharedLibrary for deciding when to use MSVC-specific file naming + and debug filenames. + + If at least some code is built with MSVC and the final library is + linked with MSVC, we can be sure that some debug info will be + generated. We only check the dynamic linker here because the static + linker is guaranteed to be of the same type. + + Interesting cases: + 1. The Vala compiler outputs C code to be compiled by whatever + C compiler we're using, so all objects will still be created by the + MSVC compiler. + 2. If the target contains only objects, process_compilers guesses and + picks the first compiler that smells right. + ''' + linker = self.get_clike_dynamic_linker() + if linker and linker.get_id() == 'msvc': + return True + return False + class Generator(): def __init__(self, args, kwargs): @@ -890,7 +958,7 @@ class Executable(BuildTarget): self.filename += '.' + self.suffix # See determine_debug_filenames() in build.SharedLibrary buildtype = environment.coredata.get_builtin_option('buildtype') - if compilers_are_msvc(self.compilers) and buildtype.startswith('debug'): + if self.get_using_msvc() and buildtype.startswith('debug'): self.debug_filename = self.prefix + self.name + '.pdb' def type_suffix(self): @@ -921,7 +989,7 @@ class StaticLibrary(BuildTarget): self.filename = self.prefix + self.name + '.' + self.suffix # See determine_debug_filenames() in build.SharedLibrary buildtype = environment.coredata.get_builtin_option('buildtype') - if compilers_are_msvc(self.compilers) and buildtype.startswith('debug'): + if self.get_using_msvc() and buildtype.startswith('debug'): self.debug_filename = self.prefix + self.name + '.pdb' def type_suffix(self): @@ -997,7 +1065,7 @@ class SharedLibrary(BuildTarget): suffix = 'dll' self.vs_import_filename = '{0}.lib'.format(self.name) self.gcc_import_filename = 'lib{0}.dll.a'.format(self.name) - if compilers_are_msvc(self.compilers): + if self.get_using_msvc(): # Shared library is of the form foo.dll prefix = '' # Import library is called foo.lib @@ -1044,7 +1112,7 @@ class SharedLibrary(BuildTarget): determine_filenames() above. """ buildtype = env.coredata.get_builtin_option('buildtype') - if compilers_are_msvc(self.compilers) and buildtype.startswith('debug'): + if self.get_using_msvc() and buildtype.startswith('debug'): # Currently we only implement separate debug symbol files for MSVC # since the toolchain does it for us. Other toolchains embed the # debugging symbols in the file itself by default. |