Make compilers list per subproject

Previously subprojects inherited languages already added by main
project, or any previous subproject. This change to have a list of
compilers per interpreters, which means that if a subproject does not
add 'c' language  it won't be able to compile .c files any more, even if
main project added the 'c' language.

This delays processing list of compilers until the interpreter adds the
BuildTarget into its list of targets. That way the interpreter can add
missing languages instead of duplicating that logic into BuildTarget for
the cython case.
pull/10164/head
Xavier Claessens 3 years ago committed by Xavier Claessens
parent ebbe4425e7
commit f2d21bf8a9
  1. 6
      docs/markdown/snippets/per_project_compiler.md
  2. 2
      docs/yaml/functions/executable.yaml
  3. 5
      mesonbuild/ast/introspection.py
  4. 195
      mesonbuild/build.py
  5. 37
      mesonbuild/interpreter/interpreter.py
  6. 3
      mesonbuild/modules/unstable_rust.py
  7. 2
      test cases/failing/100 no lang/test.json
  8. 3
      test cases/failing/123 missing compiler/meson.build
  9. 1
      test cases/failing/123 missing compiler/subprojects/sub/main.c
  10. 4
      test cases/failing/123 missing compiler/subprojects/sub/meson.build
  11. 7
      test cases/failing/123 missing compiler/test.json
  12. 2
      test cases/failing/54 wrong shared crate type/meson.build
  13. 2
      test cases/failing/55 wrong static crate type/meson.build
  14. 2
      test cases/unit/35 dist script/subprojects/sub/meson.build
  15. 8
      unittests/allplatformstests.py

@ -0,0 +1,6 @@
## Per-subproject languages
Subprojects does not inherit languages added by main project or other subprojects
any more. This could break subprojects that wants to compile e.g. `.c` files but
did not add `c` language, either in `project()` or `add_languages()`, and were
relying on the main project to do it for them.

@ -29,7 +29,7 @@ kwargs:
the `implib` argument. the `implib` argument.
implib: implib:
type: bool type: bool | str
since: 0.42.0 since: 0.42.0
description: | description: |
When set to true, an import library is generated for the When set to true, an import library is generated for the

@ -258,7 +258,10 @@ class IntrospectionInterpreter(AstInterpreter):
objects = [] # type: T.List[T.Any] objects = [] # type: T.List[T.Any]
empty_sources = [] # type: T.List[T.Any] empty_sources = [] # type: T.List[T.Any]
# Passing the unresolved sources list causes errors # Passing the unresolved sources list causes errors
target = targetclass(name, self.subdir, self.subproject, for_machine, empty_sources, [], objects, self.environment, kwargs_reduced) target = targetclass(name, self.subdir, self.subproject, for_machine, empty_sources, [], objects,
self.environment, self.coredata.compilers[for_machine], kwargs_reduced)
target.process_compilers()
target.process_compilers_late([])
new_target = { new_target = {
'name': target.get_basename(), 'name': target.get_basename(),

@ -40,7 +40,7 @@ from .mesonlib import (
) )
from .compilers import ( from .compilers import (
Compiler, is_object, clink_langs, sort_clink, lang_suffixes, all_languages, Compiler, is_object, clink_langs, sort_clink, lang_suffixes, all_languages,
is_known_suffix, detect_static_linker, detect_compiler_for is_known_suffix, detect_static_linker
) )
from .linkers import StaticLinker from .linkers import StaticLinker
from .interpreterbase import FeatureNew, FeatureDeprecated from .interpreterbase import FeatureNew, FeatureDeprecated
@ -725,11 +725,12 @@ class BuildTarget(Target):
def __init__(self, name: str, subdir: str, subproject: SubProject, for_machine: MachineChoice, def __init__(self, name: str, subdir: str, subproject: SubProject, for_machine: MachineChoice,
sources: T.List['SourceOutputs'], structured_sources: T.Optional[StructuredSources], sources: T.List['SourceOutputs'], structured_sources: T.Optional[StructuredSources],
objects, environment: environment.Environment, kwargs): objects, environment: environment.Environment, compilers: T.Dict[str, 'Compiler'], kwargs):
super().__init__(name, subdir, subproject, True, for_machine) super().__init__(name, subdir, subproject, True, for_machine)
unity_opt = environment.coredata.get_option(OptionKey('unity')) unity_opt = environment.coredata.get_option(OptionKey('unity'))
self.is_unity = unity_opt == 'on' or (unity_opt == 'subprojects' and subproject != '') self.is_unity = unity_opt == 'on' or (unity_opt == 'subprojects' and subproject != '')
self.environment = environment self.environment = environment
self.all_compilers = compilers
self.compilers = OrderedDict() # type: OrderedDict[str, Compiler] self.compilers = OrderedDict() # type: OrderedDict[str, Compiler]
self.objects: T.List[T.Union[str, 'File', 'ExtractedObjects']] = [] self.objects: T.List[T.Union[str, 'File', 'ExtractedObjects']] = []
self.structured_sources = structured_sources self.structured_sources = structured_sources
@ -763,14 +764,15 @@ class BuildTarget(Target):
self.process_objectlist(objects) self.process_objectlist(objects)
self.process_kwargs(kwargs, environment) self.process_kwargs(kwargs, environment)
self.check_unknown_kwargs(kwargs) self.check_unknown_kwargs(kwargs)
self.process_compilers()
if not any([self.sources, self.generated, self.objects, self.link_whole, self.structured_sources]): if not any([self.sources, self.generated, self.objects, self.link_whole, self.structured_sources]):
raise InvalidArguments(f'Build target {name} has no sources.') raise InvalidArguments(f'Build target {name} has no sources.')
self.process_compilers_late()
self.validate_sources()
self.validate_install(environment) self.validate_install(environment)
self.check_module_linking() self.check_module_linking()
def post_init(self) -> None:
''' Initialisations and checks requiring the final list of compilers to be known
'''
self.validate_sources()
if self.structured_sources and any([self.sources, self.generated]): if self.structured_sources and any([self.sources, self.generated]):
raise MesonException('cannot mix structured sources and unstructured sources') raise MesonException('cannot mix structured sources and unstructured sources')
if self.structured_sources and 'rust' not in self.compilers: if self.structured_sources and 'rust' not in self.compilers:
@ -844,15 +846,15 @@ class BuildTarget(Target):
removed = True removed = True
return removed return removed
def process_compilers_late(self): def process_compilers_late(self, extra_languages: T.List[str]):
"""Processes additional compilers after kwargs have been evaluated. """Processes additional compilers after kwargs have been evaluated.
This can add extra compilers that might be required by keyword This can add extra compilers that might be required by keyword
arguments, such as link_with or dependencies. It will also try to guess arguments, such as link_with or dependencies. It will also try to guess
which compiler to use if one hasn't been selected already. which compiler to use if one hasn't been selected already.
""" """
# Populate list of compilers for lang in extra_languages:
compilers = self.environment.coredata.compilers[self.for_machine] self.compilers[lang] = self.all_compilers[lang]
# did user override clink_langs for this target? # did user override clink_langs for this target?
link_langs = [self.link_language] if self.link_language else clink_langs link_langs = [self.link_language] if self.link_language else clink_langs
@ -860,14 +862,11 @@ class BuildTarget(Target):
# If this library is linked against another library we need to consider # If this library is linked against another library we need to consider
# the languages of those libraries as well. # the languages of those libraries as well.
if self.link_targets or self.link_whole_targets: if self.link_targets or self.link_whole_targets:
extra = set()
for t in itertools.chain(self.link_targets, self.link_whole_targets): for t in itertools.chain(self.link_targets, self.link_whole_targets):
if isinstance(t, CustomTarget) or isinstance(t, CustomTargetIndex): if isinstance(t, CustomTarget) or isinstance(t, CustomTargetIndex):
continue # We can't know anything about these. continue # We can't know anything about these.
for name, compiler in t.compilers.items(): for name, compiler in t.compilers.items():
if name in link_langs: if name in link_langs and name not in self.compilers:
extra.add((name, compiler))
for name, compiler in sorted(extra, key=lambda p: sort_clink(p[0])):
self.compilers[name] = compiler self.compilers[name] = compiler
if not self.compilers: if not self.compilers:
@ -875,21 +874,28 @@ class BuildTarget(Target):
# files of unknown origin. Just add the first clink compiler # files of unknown origin. Just add the first clink compiler
# that we have and hope that it can link these objects # that we have and hope that it can link these objects
for lang in link_langs: for lang in link_langs:
if lang in compilers: if lang in self.all_compilers:
self.compilers[lang] = compilers[lang] self.compilers[lang] = self.all_compilers[lang]
break break
def process_compilers(self) -> None: # Now that we have the final list of compilers we can sort it according
# to clink_langs and do sanity checks.
self.compilers = OrderedDict(sorted(self.compilers.items(),
key=lambda t: sort_clink(t[0])))
self.post_init()
def process_compilers(self) -> T.List[str]:
''' '''
Populate self.compilers, which is the list of compilers that this Populate self.compilers, which is the list of compilers that this
target will use for compiling all its sources. target will use for compiling all its sources.
We also add compilers that were used by extracted objects to simplify We also add compilers that were used by extracted objects to simplify
dynamic linker determination. dynamic linker determination.
Returns a list of missing languages that we can add implicitly, such as
C/C++ compiler for cython.
''' '''
missing_languages: T.List[str] = []
if not any([self.sources, self.generated, self.objects, self.structured_sources]): if not any([self.sources, self.generated, self.objects, self.structured_sources]):
return return missing_languages
# Populate list of compilers
compilers = self.environment.coredata.compilers[self.for_machine]
# Pre-existing sources # Pre-existing sources
sources: T.List['FileOrString'] = list(self.sources) sources: T.List['FileOrString'] = list(self.sources)
generated = self.generated.copy() generated = self.generated.copy()
@ -936,56 +942,33 @@ class BuildTarget(Target):
# are expected to be able to add arbitrary non-source files to the # are expected to be able to add arbitrary non-source files to the
# sources list # sources list
for s in sources: for s in sources:
for lang, compiler in compilers.items(): for lang, compiler in self.all_compilers.items():
if compiler.can_compile(s): if compiler.can_compile(s):
if lang not in self.compilers: if lang not in self.compilers:
self.compilers[lang] = compiler self.compilers[lang] = compiler
break break
else: else:
if is_known_suffix(s): if is_known_suffix(s):
raise MesonException('No {} machine compiler for "{}"'. path = pathlib.Path(str(s)).as_posix()
format(self.for_machine.get_lower_case_name(), s)) m = f'No {self.for_machine.get_lower_case_name()} machine compiler for {path!r}'
raise MesonException(m)
# Re-sort according to clink_langs
self.compilers = OrderedDict(sorted(self.compilers.items(),
key=lambda t: sort_clink(t[0])))
# If all our sources are Vala, our target also needs the C compiler but # If all our sources are Vala, our target also needs the C compiler but
# it won't get added above. # it won't get added above.
if 'vala' in self.compilers and 'c' not in self.compilers: if 'vala' in self.compilers and 'c' not in self.compilers:
self.compilers['c'] = compilers['c'] self.compilers['c'] = self.all_compilers['c']
if 'cython' in self.compilers: if 'cython' in self.compilers:
key = OptionKey('language', machine=self.for_machine, lang='cython') key = OptionKey('language', machine=self.for_machine, lang='cython')
if key in self.option_overrides: if key in self.option_overrides:
value = self.option_overrides[key] value = self.option_overrides[key]
else: else:
value = self.environment.coredata.options[key].value value = self.environment.coredata.options[key].value
try: try:
self.compilers[value] = compilers[value] self.compilers[value] = self.all_compilers[value]
except KeyError: except KeyError:
# TODO: it would be nice to not have to do this here, but we missing_languages.append(value)
# have two problems to work around:
# 1. If this is set via an override we have no way to know return missing_languages
# before now that we need a compiler for the non-default language
# 2. Because Cython itself initializes the `cython_language`
# option, we have no good place to insert that you need it
# before now, so we just have to do it here.
comp = detect_compiler_for(self.environment, value, self.for_machine)
# This is copied verbatim from the interpreter
if self.for_machine == MachineChoice.HOST or self.environment.is_cross_build():
logger_fun = mlog.log
else:
logger_fun = mlog.debug
logger_fun(comp.get_display_language(), 'compiler for the', self.for_machine.get_lower_case_name(), 'machine:',
mlog.bold(' '.join(comp.get_exelist())), comp.get_version_string())
if comp.linker is not None:
logger_fun(comp.get_display_language(), 'linker for the', self.for_machine.get_lower_case_name(), 'machine:',
mlog.bold(' '.join(comp.linker.get_exelist())), comp.linker.id, comp.linker.version)
if comp is None:
raise MesonException(f'Cannot find required compiler {value}')
self.compilers[value] = comp
def validate_sources(self): def validate_sources(self):
if not self.sources: if not self.sources:
@ -1553,14 +1536,13 @@ You probably should put it in link_with instead.''')
return langs return langs
def get_prelinker(self): def get_prelinker(self):
all_compilers = self.environment.coredata.compilers[self.for_machine]
if self.link_language: if self.link_language:
comp = all_compilers[self.link_language] comp = self.all_compilers[self.link_language]
return comp return comp
for l in clink_langs: for l in clink_langs:
if l in self.compilers: if l in self.compilers:
try: try:
prelinker = all_compilers[l] prelinker = self.all_compilers[l]
except KeyError: except KeyError:
raise MesonException( raise MesonException(
f'Could not get a prelinker linker for build target {self.name!r}. ' f'Could not get a prelinker linker for build target {self.name!r}. '
@ -1579,15 +1561,16 @@ You probably should put it in link_with instead.''')
that can link compiled C. We don't actually need to add an exception that can link compiled C. We don't actually need to add an exception
for Vala here because of that. for Vala here because of that.
''' '''
# Populate list of all compilers, not just those being used to compile
# sources in this target
all_compilers = self.environment.coredata.compilers[self.for_machine]
# If the user set the link_language, just return that. # If the user set the link_language, just return that.
if self.link_language: if self.link_language:
comp = all_compilers[self.link_language] comp = self.all_compilers[self.link_language]
return comp, comp.language_stdlib_only_link_flags(self.environment) return comp, comp.language_stdlib_only_link_flags(self.environment)
# Since dependencies could come from subprojects, they could have
# languages we don't have in self.all_compilers. Use the global list of
# all compilers here.
all_compilers = self.environment.coredata.compilers[self.for_machine]
# Languages used by dependencies # Languages used by dependencies
dep_langs = self.get_langs_used_by_deps() dep_langs = self.get_langs_used_by_deps()
# Pick a compiler based on the language priority-order # Pick a compiler based on the language priority-order
@ -1601,11 +1584,9 @@ You probably should put it in link_with instead.''')
f'Requires a linker for language "{l}", but that is not ' f'Requires a linker for language "{l}", but that is not '
'a project language.') 'a project language.')
stdlib_args: T.List[str] = [] stdlib_args: T.List[str] = []
added_languages: T.Set[str] = set()
for dl in itertools.chain(self.compilers, dep_langs): for dl in itertools.chain(self.compilers, dep_langs):
if dl != linker.language: if dl != linker.language:
stdlib_args += all_compilers[dl].language_stdlib_only_link_flags(self.environment) stdlib_args += all_compilers[dl].language_stdlib_only_link_flags(self.environment)
added_languages.add(dl)
# Type of var 'linker' is Compiler. # Type of var 'linker' is Compiler.
# Pretty hard to fix because the return value is passed everywhere # Pretty hard to fix because the return value is passed everywhere
return linker, stdlib_args return linker, stdlib_args
@ -1653,7 +1634,7 @@ You probably should put it in link_with instead.''')
''' '''
# Rustc can use msvc style linkers # Rustc can use msvc style linkers
if self.uses_rust(): if self.uses_rust():
compiler = self.environment.coredata.compilers[self.for_machine]['rust'] compiler = self.all_compilers['rust']
else: else:
compiler, _ = self.get_clink_dynamic_linker_and_stdlibs() compiler, _ = self.get_clink_dynamic_linker_and_stdlibs()
# Mixing many languages with MSVC is not supported yet so ignore stdlibs. # Mixing many languages with MSVC is not supported yet so ignore stdlibs.
@ -1833,18 +1814,38 @@ class Executable(BuildTarget):
def __init__(self, name: str, subdir: str, subproject: str, for_machine: MachineChoice, def __init__(self, name: str, subdir: str, subproject: str, for_machine: MachineChoice,
sources: T.List[File], structured_sources: T.Optional['StructuredSources'], sources: T.List[File], structured_sources: T.Optional['StructuredSources'],
objects, environment: environment.Environment, kwargs): objects, environment: environment.Environment, compilers: T.Dict[str, 'Compiler'],
kwargs):
self.typename = 'executable' self.typename = 'executable'
key = OptionKey('b_pie') key = OptionKey('b_pie')
if 'pie' not in kwargs and key in environment.coredata.options: if 'pie' not in kwargs and key in environment.coredata.options:
kwargs['pie'] = environment.coredata.options[key].value kwargs['pie'] = environment.coredata.options[key].value
super().__init__(name, subdir, subproject, for_machine, sources, structured_sources, objects, environment, kwargs) super().__init__(name, subdir, subproject, for_machine, sources, structured_sources, objects,
environment, compilers, kwargs)
# Check for export_dynamic
self.export_dynamic = kwargs.get('export_dynamic', False)
if not isinstance(self.export_dynamic, bool):
raise InvalidArguments('"export_dynamic" keyword argument must be a boolean')
self.implib = kwargs.get('implib')
if not isinstance(self.implib, (bool, str, type(None))):
raise InvalidArguments('"export_dynamic" keyword argument must be a boolean or string')
if self.implib:
self.export_dynamic = True
if self.export_dynamic and self.implib is False:
raise InvalidArguments('"implib" keyword argument must not be false for if "export_dynamic" is true')
# Only linkwithable if using export_dynamic
self.is_linkwithable = self.export_dynamic
# Remember that this exe was returned by `find_program()` through an override
self.was_returned_by_find_program = False
def post_init(self) -> None:
super().post_init()
machine = self.environment.machines[self.for_machine]
# Unless overridden, executables have no suffix or prefix. Except on # Unless overridden, executables have no suffix or prefix. Except on
# Windows and with C#/Mono executables where the suffix is 'exe' # Windows and with C#/Mono executables where the suffix is 'exe'
if not hasattr(self, 'prefix'): if not hasattr(self, 'prefix'):
self.prefix = '' self.prefix = ''
if not hasattr(self, 'suffix'): if not hasattr(self, 'suffix'):
machine = environment.machines[for_machine]
# Executable for Windows or C#/Mono # Executable for Windows or C#/Mono
if machine.is_windows() or machine.is_cygwin() or 'cs' in self.compilers: if machine.is_windows() or machine.is_cygwin() or 'cs' in self.compilers:
self.suffix = 'exe' self.suffix = 'exe'
@ -1862,7 +1863,7 @@ class Executable(BuildTarget):
'cpp' in self.compilers and self.compilers['cpp'].get_id() in ('ti', 'c2000')): 'cpp' in self.compilers and self.compilers['cpp'].get_id() in ('ti', 'c2000')):
self.suffix = 'out' self.suffix = 'out'
else: else:
self.suffix = environment.machines[for_machine].get_exe_suffix() self.suffix = machine.get_exe_suffix()
self.filename = self.name self.filename = self.name
if self.suffix: if self.suffix:
self.filename += '.' + self.suffix self.filename += '.' + self.suffix
@ -1877,25 +1878,12 @@ class Executable(BuildTarget):
# The debugging information file this target will generate # The debugging information file this target will generate
self.debug_filename = None self.debug_filename = None
# Check for export_dynamic
self.export_dynamic = False
if kwargs.get('export_dynamic'):
if not isinstance(kwargs['export_dynamic'], bool):
raise InvalidArguments('"export_dynamic" keyword argument must be a boolean')
self.export_dynamic = True
if kwargs.get('implib'):
self.export_dynamic = True
if self.export_dynamic and kwargs.get('implib') is False:
raise InvalidArguments('"implib" keyword argument must not be false for if "export_dynamic" is true')
m = environment.machines[for_machine]
# If using export_dynamic, set the import library name # If using export_dynamic, set the import library name
if self.export_dynamic: if self.export_dynamic:
implib_basename = self.name + '.exe' implib_basename = self.name + '.exe'
if not isinstance(kwargs.get('implib', False), bool): if isinstance(self.implib, str):
implib_basename = kwargs['implib'] implib_basename = self.implib
if m.is_windows() or m.is_cygwin(): if machine.is_windows() or machine.is_cygwin():
self.vs_import_filename = f'{implib_basename}.lib' self.vs_import_filename = f'{implib_basename}.lib'
self.gcc_import_filename = f'lib{implib_basename}.a' self.gcc_import_filename = f'lib{implib_basename}.a'
if self.get_using_msvc(): if self.get_using_msvc():
@ -1903,17 +1891,11 @@ class Executable(BuildTarget):
else: else:
self.import_filename = self.gcc_import_filename self.import_filename = self.gcc_import_filename
if m.is_windows() and ('cs' in self.compilers or if machine.is_windows() and ('cs' in self.compilers or
self.uses_rust() or self.uses_rust() or
self.get_using_msvc()): self.get_using_msvc()):
self.debug_filename = self.name + '.pdb' self.debug_filename = self.name + '.pdb'
# Only linkwithable if using export_dynamic
self.is_linkwithable = self.export_dynamic
# Remember that this exe was returned by `find_program()` through an override
self.was_returned_by_find_program = False
def get_default_install_dir(self, environment: environment.Environment) -> T.Tuple[str, str]: def get_default_install_dir(self, environment: environment.Environment) -> T.Tuple[str, str]:
return environment.get_bindir(), '{bindir}' return environment.get_bindir(), '{bindir}'
@ -1960,9 +1942,17 @@ class StaticLibrary(BuildTarget):
def __init__(self, name: str, subdir: str, subproject: str, for_machine: MachineChoice, def __init__(self, name: str, subdir: str, subproject: str, for_machine: MachineChoice,
sources: T.List[File], structured_sources: T.Optional['StructuredSources'], sources: T.List[File], structured_sources: T.Optional['StructuredSources'],
objects, environment: environment.Environment, kwargs): objects, environment: environment.Environment, compilers: T.Dict[str, 'Compiler'],
kwargs):
self.typename = 'static library' self.typename = 'static library'
super().__init__(name, subdir, subproject, for_machine, sources, structured_sources, objects, environment, kwargs) self.prelink = kwargs.get('prelink', False)
if not isinstance(self.prelink, bool):
raise InvalidArguments('Prelink keyword argument must be a boolean.')
super().__init__(name, subdir, subproject, for_machine, sources, structured_sources, objects,
environment, compilers, kwargs)
def post_init(self) -> None:
super().post_init()
if 'cs' in self.compilers: if 'cs' in self.compilers:
raise InvalidArguments('Static libraries not supported for C#.') raise InvalidArguments('Static libraries not supported for C#.')
if 'rust' in self.compilers: if 'rust' in self.compilers:
@ -1993,9 +1983,6 @@ class StaticLibrary(BuildTarget):
self.suffix = 'a' self.suffix = 'a'
self.filename = self.prefix + self.name + '.' + self.suffix self.filename = self.prefix + self.name + '.' + self.suffix
self.outputs = [self.filename] self.outputs = [self.filename]
self.prelink = kwargs.get('prelink', False)
if not isinstance(self.prelink, bool):
raise InvalidArguments('Prelink keyword argument must be a boolean.')
def get_link_deps_mapping(self, prefix: str, environment: environment.Environment) -> T.Mapping[str, str]: def get_link_deps_mapping(self, prefix: str, environment: environment.Environment) -> T.Mapping[str, str]:
return {} return {}
@ -2023,7 +2010,8 @@ class SharedLibrary(BuildTarget):
def __init__(self, name: str, subdir: str, subproject: str, for_machine: MachineChoice, def __init__(self, name: str, subdir: str, subproject: str, for_machine: MachineChoice,
sources: T.List[File], structured_sources: T.Optional['StructuredSources'], sources: T.List[File], structured_sources: T.Optional['StructuredSources'],
objects, environment: environment.Environment, kwargs): objects, environment: environment.Environment, compilers: T.Dict[str, 'Compiler'],
kwargs):
self.typename = 'shared library' self.typename = 'shared library'
self.soversion = None self.soversion = None
self.ltversion = None self.ltversion = None
@ -2040,7 +2028,11 @@ class SharedLibrary(BuildTarget):
self.debug_filename = None self.debug_filename = None
# Use by the pkgconfig module # Use by the pkgconfig module
self.shared_library_only = False self.shared_library_only = False
super().__init__(name, subdir, subproject, for_machine, sources, structured_sources, objects, environment, kwargs) super().__init__(name, subdir, subproject, for_machine, sources, structured_sources, objects,
environment, compilers, kwargs)
def post_init(self) -> None:
super().post_init()
if 'rust' in self.compilers: if 'rust' in self.compilers:
# If no crate type is specified, or it's the generic lib type, use dylib # If no crate type is specified, or it's the generic lib type, use dylib
if not hasattr(self, 'rust_crate_type') or self.rust_crate_type == 'lib': if not hasattr(self, 'rust_crate_type') or self.rust_crate_type == 'lib':
@ -2054,7 +2046,7 @@ class SharedLibrary(BuildTarget):
if not hasattr(self, 'suffix'): if not hasattr(self, 'suffix'):
self.suffix = None self.suffix = None
self.basic_filename_tpl = '{0.prefix}{0.name}.{0.suffix}' self.basic_filename_tpl = '{0.prefix}{0.name}.{0.suffix}'
self.determine_filenames(environment) self.determine_filenames(self.environment)
def get_link_deps_mapping(self, prefix: str, environment: environment.Environment) -> T.Mapping[str, str]: def get_link_deps_mapping(self, prefix: str, environment: environment.Environment) -> T.Mapping[str, str]:
result: T.Dict[str, str] = {} result: T.Dict[str, str] = {}
@ -2352,13 +2344,14 @@ class SharedModule(SharedLibrary):
def __init__(self, name: str, subdir: str, subproject: str, for_machine: MachineChoice, def __init__(self, name: str, subdir: str, subproject: str, for_machine: MachineChoice,
sources: T.List[File], structured_sources: T.Optional['StructuredSources'], sources: T.List[File], structured_sources: T.Optional['StructuredSources'],
objects, environment: environment.Environment, kwargs): objects, environment: environment.Environment,
compilers: T.Dict[str, 'Compiler'], kwargs):
if 'version' in kwargs: if 'version' in kwargs:
raise MesonException('Shared modules must not specify the version kwarg.') raise MesonException('Shared modules must not specify the version kwarg.')
if 'soversion' in kwargs: if 'soversion' in kwargs:
raise MesonException('Shared modules must not specify the soversion kwarg.') raise MesonException('Shared modules must not specify the soversion kwarg.')
super().__init__(name, subdir, subproject, for_machine, sources, super().__init__(name, subdir, subproject, for_machine, sources,
structured_sources, objects, environment, kwargs) structured_sources, objects, environment, compilers, kwargs)
self.typename = 'shared module' self.typename = 'shared module'
# We need to set the soname in cases where build files link the module # We need to set the soname in cases where build files link the module
# to build targets, see: https://github.com/mesonbuild/meson/issues/9492 # to build targets, see: https://github.com/mesonbuild/meson/issues/9492
@ -2680,9 +2673,11 @@ class Jar(BuildTarget):
def __init__(self, name: str, subdir: str, subproject: str, for_machine: MachineChoice, def __init__(self, name: str, subdir: str, subproject: str, for_machine: MachineChoice,
sources: T.List[File], structured_sources: T.Optional['StructuredSources'], sources: T.List[File], structured_sources: T.Optional['StructuredSources'],
objects, environment: environment.Environment, kwargs): objects, environment: environment.Environment, compilers: T.Dict[str, 'Compiler'],
kwargs):
self.typename = 'jar' self.typename = 'jar'
super().__init__(name, subdir, subproject, for_machine, sources, structured_sources, objects, environment, kwargs) super().__init__(name, subdir, subproject, for_machine, sources, structured_sources, objects,
environment, compilers, kwargs)
for s in self.sources: for s in self.sources:
if not s.endswith('.java'): if not s.endswith('.java'):
raise InvalidArguments(f'Jar source {s} is not a java file.') raise InvalidArguments(f'Jar source {s} is not a java file.')

@ -23,7 +23,8 @@ from .. import compilers
from .. import envconfig from .. import envconfig
from ..wrap import wrap, WrapMode from ..wrap import wrap, WrapMode
from .. import mesonlib from .. import mesonlib
from ..mesonlib import MesonBugException, HoldableObject, FileMode, MachineChoice, OptionKey, listify, extract_as_list, has_path_sep from ..mesonlib import (MesonBugException, HoldableObject, FileMode, MachineChoice, OptionKey,
listify, extract_as_list, has_path_sep, PerMachine)
from ..programs import ExternalProgram, NonExistingExternalProgram from ..programs import ExternalProgram, NonExistingExternalProgram
from ..dependencies import Dependency from ..dependencies import Dependency
from ..depfile import DepFile from ..depfile import DepFile
@ -304,6 +305,7 @@ class Interpreter(InterpreterBase, HoldableObject):
self.build_func_dict() self.build_func_dict()
self.build_holder_map() self.build_holder_map()
self.user_defined_options = user_defined_options self.user_defined_options = user_defined_options
self.compilers: PerMachine[T.Dict[str, 'Compiler']] = PerMachine({}, {})
# build_def_files needs to be defined before parse_project is called # build_def_files needs to be defined before parse_project is called
# #
@ -1346,7 +1348,7 @@ external dependencies (including libraries) must go to "dependencies".''')
def add_languages_for(self, args: T.List[str], required: bool, for_machine: MachineChoice) -> bool: def add_languages_for(self, args: T.List[str], required: bool, for_machine: MachineChoice) -> bool:
args = [a.lower() for a in args] args = [a.lower() for a in args]
langs = set(self.coredata.compilers[for_machine].keys()) langs = set(self.compilers[for_machine].keys())
langs.update(args) langs.update(args)
# We'd really like to add cython's default language here, but it can't # We'd really like to add cython's default language here, but it can't
# actually be done because the cython compiler hasn't been initialized, # actually be done because the cython compiler hasn't been initialized,
@ -1360,11 +1362,11 @@ external dependencies (including libraries) must go to "dependencies".''')
success = True success = True
for lang in sorted(args, key=compilers.sort_clink): for lang in sorted(args, key=compilers.sort_clink):
clist = self.coredata.compilers[for_machine] if lang in self.compilers[for_machine]:
continue
machine_name = for_machine.get_lower_case_name() machine_name = for_machine.get_lower_case_name()
if lang in clist: comp = self.coredata.compilers[for_machine].get(lang)
comp = clist[lang] if not comp:
else:
try: try:
comp = compilers.detect_compiler_for(self.environment, lang, for_machine) comp = compilers.detect_compiler_for(self.environment, lang, for_machine)
if comp is None: if comp is None:
@ -1402,6 +1404,7 @@ external dependencies (including libraries) must go to "dependencies".''')
logger_fun(comp.get_display_language(), 'linker for the', machine_name, 'machine:', logger_fun(comp.get_display_language(), 'linker for the', machine_name, 'machine:',
mlog.bold(' '.join(comp.linker.get_exelist())), comp.linker.id, comp.linker.version) mlog.bold(' '.join(comp.linker.get_exelist())), comp.linker.id, comp.linker.version)
self.build.ensure_static_linker(comp) self.build.ensure_static_linker(comp)
self.compilers[for_machine][lang] = comp
return success return success
@ -2830,6 +2833,13 @@ Try setting b_lundef to false instead.'''.format(self.coredata.options[OptionKey
idname = tobj.get_id() idname = tobj.get_id()
if idname in self.build.targets: if idname in self.build.targets:
raise InvalidCode(f'Tried to create target "{name}", but a target of that name already exists.') raise InvalidCode(f'Tried to create target "{name}", but a target of that name already exists.')
if isinstance(tobj, build.BuildTarget):
missing_languages = tobj.process_compilers()
self.add_languages(missing_languages, True, tobj.for_machine)
tobj.process_compilers_late(missing_languages)
self.add_stdlib_info(tobj)
self.build.targets[idname] = tobj self.build.targets[idname] = tobj
if idname not in self.coredata.target_guids: if idname not in self.coredata.target_guids:
self.coredata.target_guids[idname] = str(uuid.uuid4()).upper() self.coredata.target_guids[idname] = str(uuid.uuid4()).upper()
@ -2947,10 +2957,10 @@ Try setting b_lundef to false instead.'''.format(self.coredata.options[OptionKey
outputs.update(o) outputs.update(o)
kwargs['include_directories'] = self.extract_incdirs(kwargs) kwargs['include_directories'] = self.extract_incdirs(kwargs)
target = targetclass(name, self.subdir, self.subproject, for_machine, srcs, struct, objs, self.environment, kwargs) target = targetclass(name, self.subdir, self.subproject, for_machine, srcs, struct, objs,
self.environment, self.compilers[for_machine], kwargs)
target.project_version = self.project_version target.project_version = self.project_version
self.add_stdlib_info(target)
self.add_target(name, target) self.add_target(name, target)
self.project_args_frozen = True self.project_args_frozen = True
return target return target
@ -2972,17 +2982,8 @@ This will become a hard error in the future.''', location=self.current_node)
cleaned_items.append(i) cleaned_items.append(i)
kwargs['d_import_dirs'] = cleaned_items kwargs['d_import_dirs'] = cleaned_items
def get_used_languages(self, target):
result = set()
for i in target.sources:
for lang, c in self.coredata.compilers[target.for_machine].items():
if c.can_compile(i):
result.add(lang)
break
return result
def add_stdlib_info(self, target): def add_stdlib_info(self, target):
for l in self.get_used_languages(target): for l in target.compilers.keys():
dep = self.build.stdlibs[target.for_machine].get(l, None) dep = self.build.stdlibs[target.for_machine].get(l, None)
if dep: if dep:
target.add_deps(dep) target.add_deps(dep)

@ -151,7 +151,8 @@ class RustModule(ExtensionModule):
new_target = Executable( new_target = Executable(
name, base_target.subdir, state.subproject, base_target.for_machine, name, base_target.subdir, state.subproject, base_target.for_machine,
base_target.sources, base_target.structured_sources, base_target.sources, base_target.structured_sources,
base_target.objects, base_target.environment, new_target_kwargs base_target.objects, base_target.environment, base_target.compilers,
new_target_kwargs
) )
test = self.interpreter.make_test( test = self.interpreter.make_test(

@ -1,7 +1,7 @@
{ {
"stdout": [ "stdout": [
{ {
"line": "test cases/failing/100 no lang/meson.build:2:0: ERROR: No host machine compiler for \"main.c\"" "line": "test cases/failing/100 no lang/meson.build:2:0: ERROR: No host machine compiler for 'main.c'"
} }
] ]
} }

@ -0,0 +1,3 @@
project('main project', 'c')
subproject('sub')

@ -0,0 +1 @@
int main(int argc, char *argv[]) { return 0; }

@ -0,0 +1,4 @@
project('sub')
# Should fail because we did not add C language, even if parent project did.
executable('app', 'main.c')

@ -0,0 +1,7 @@
{
"stdout": [
{
"line": "test cases/failing/123 missing compiler/subprojects/sub/meson.build:4:0: ERROR: No host machine compiler for 'subprojects/sub/main.c'"
}
]
}

@ -4,4 +4,4 @@ if not add_languages('rust', required: false)
error('MESON_SKIP_TEST test requires rust compiler') error('MESON_SKIP_TEST test requires rust compiler')
endif endif
shared_library('test', 'foo.rs', rust_crate_type : 'staticlib') shared_library('mytest', 'foo.rs', rust_crate_type : 'staticlib')

@ -4,4 +4,4 @@ if not add_languages('rust', required: false)
error('MESON_SKIP_TEST test requires rust compiler') error('MESON_SKIP_TEST test requires rust compiler')
endif endif
static_library('test', 'foo.rs', rust_crate_type : 'cdylib') static_library('mytest', 'foo.rs', rust_crate_type : 'cdylib')

@ -1,4 +1,4 @@
project('sub') project('sub', 'c')
if get_option('broken_dist_script') if get_option('broken_dist_script')
# Make sure we can add a dist script in a subproject, but it won't be run # Make sure we can add a dist script in a subproject, but it won't be run

@ -3998,10 +3998,14 @@ class AllPlatformTests(BasePlatformTests):
env = get_fake_env(testdir, self.builddir, self.prefix) env = get_fake_env(testdir, self.builddir, self.prefix)
def output_name(name, type_): def output_name(name, type_):
return type_(name=name, subdir=None, subproject=None, target = type_(name=name, subdir=None, subproject=None,
for_machine=MachineChoice.HOST, sources=[], for_machine=MachineChoice.HOST, sources=[],
structured_sources=None, structured_sources=None,
objects=[], environment=env, kwargs={}).filename objects=[], environment=env, compilers=env.coredata.compilers[MachineChoice.HOST],
kwargs={})
target.process_compilers()
target.process_compilers_late([])
return target.filename
shared_lib_name = lambda name: output_name(name, SharedLibrary) shared_lib_name = lambda name: output_name(name, SharedLibrary)
static_lib_name = lambda name: output_name(name, StaticLibrary) static_lib_name = lambda name: output_name(name, StaticLibrary)

Loading…
Cancel
Save