1# Copyright 2012-2017 The Meson development team
2
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6
7#     http://www.apache.org/licenses/LICENSE-2.0
8
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14
15from collections import OrderedDict
16from functools import lru_cache
17import copy
18import hashlib
19import itertools, pathlib
20import os
21import pickle
22import re
23import textwrap
24import typing as T
25
26from . import environment
27from . import dependencies
28from . import mlog
29from . import programs
30from .mesonlib import (
31    HoldableObject, SecondLevelHolder,
32    File, MesonException, MachineChoice, PerMachine, OrderedSet, listify,
33    extract_as_list, typeslistify, stringlistify, classify_unity_sources,
34    get_filenames_templates_dict, substitute_values, has_path_sep,
35    OptionKey, PerMachineDefaultable,
36    MesonBugException, FileOrString,
37)
38from .compilers import (
39    Compiler, is_object, clink_langs, sort_clink, lang_suffixes,
40    is_known_suffix, detect_static_linker
41)
42from .linkers import StaticLinker
43from .interpreterbase import FeatureNew
44
45if T.TYPE_CHECKING:
46    from ._typing import ImmutableListProtocol, ImmutableSetProtocol
47    from .interpreter.interpreter import Test, SourceOutputs, Interpreter
48    from .mesonlib import FileMode, FileOrString
49    from .modules import ModuleState
50    from .backend.backends import Backend
51
52pch_kwargs = {'c_pch', 'cpp_pch'}
53
54lang_arg_kwargs = {
55    'c_args',
56    'cpp_args',
57    'cuda_args',
58    'd_args',
59    'd_import_dirs',
60    'd_unittest',
61    'd_module_versions',
62    'd_debug',
63    'fortran_args',
64    'java_args',
65    'objc_args',
66    'objcpp_args',
67    'rust_args',
68    'vala_args',
69    'cs_args',
70    'cython_args',
71}
72
73vala_kwargs = {'vala_header', 'vala_gir', 'vala_vapi'}
74rust_kwargs = {'rust_crate_type'}
75cs_kwargs = {'resources', 'cs_args'}
76
77buildtarget_kwargs = {
78    'build_by_default',
79    'build_rpath',
80    'dependencies',
81    'extra_files',
82    'gui_app',
83    'link_with',
84    'link_whole',
85    'link_args',
86    'link_depends',
87    'implicit_include_directories',
88    'include_directories',
89    'install',
90    'install_rpath',
91    'install_dir',
92    'install_mode',
93    'name_prefix',
94    'name_suffix',
95    'native',
96    'objects',
97    'override_options',
98    'sources',
99    'gnu_symbol_visibility',
100    'link_language',
101    'win_subsystem',
102}
103
104known_build_target_kwargs = (
105    buildtarget_kwargs |
106    lang_arg_kwargs |
107    pch_kwargs |
108    vala_kwargs |
109    rust_kwargs |
110    cs_kwargs)
111
112known_exe_kwargs = known_build_target_kwargs | {'implib', 'export_dynamic', 'pie'}
113known_shlib_kwargs = known_build_target_kwargs | {'version', 'soversion', 'vs_module_defs', 'darwin_versions'}
114known_shmod_kwargs = known_build_target_kwargs | {'vs_module_defs'}
115known_stlib_kwargs = known_build_target_kwargs | {'pic', 'prelink'}
116known_jar_kwargs = known_exe_kwargs | {'main_class'}
117
118@lru_cache(maxsize=None)
119def get_target_macos_dylib_install_name(ld) -> str:
120    name = ['@rpath/', ld.prefix, ld.name]
121    if ld.soversion is not None:
122        name.append('.' + ld.soversion)
123    name.append('.dylib')
124    return ''.join(name)
125
126class InvalidArguments(MesonException):
127    pass
128
129class DependencyOverride(HoldableObject):
130    def __init__(self, dep, node, explicit=True):
131        self.dep = dep
132        self.node = node
133        self.explicit = explicit
134
135class Headers(HoldableObject):
136
137    def __init__(self, sources: T.List[File], install_subdir: T.Optional[str],
138                 install_dir: T.Optional[str], install_mode: 'FileMode',
139                 subproject: str):
140        self.sources = sources
141        self.install_subdir = install_subdir
142        self.custom_install_dir = install_dir
143        self.custom_install_mode = install_mode
144        self.subproject = subproject
145
146    # TODO: we really don't need any of these methods, but they're preserved to
147    # keep APIs relying on them working.
148
149    def set_install_subdir(self, subdir: str) -> None:
150        self.install_subdir = subdir
151
152    def get_install_subdir(self) -> T.Optional[str]:
153        return self.install_subdir
154
155    def get_sources(self) -> T.List[File]:
156        return self.sources
157
158    def get_custom_install_dir(self) -> T.Optional[str]:
159        return self.custom_install_dir
160
161    def get_custom_install_mode(self) -> 'FileMode':
162        return self.custom_install_mode
163
164
165class Man(HoldableObject):
166
167    def __init__(self, sources: T.List[File], install_dir: T.Optional[str],
168                 install_mode: 'FileMode', subproject: str,
169                 locale: T.Optional[str]):
170        self.sources = sources
171        self.custom_install_dir = install_dir
172        self.custom_install_mode = install_mode
173        self.subproject = subproject
174        self.locale = locale
175
176    def get_custom_install_dir(self) -> T.Optional[str]:
177        return self.custom_install_dir
178
179    def get_custom_install_mode(self) -> 'FileMode':
180        return self.custom_install_mode
181
182    def get_sources(self) -> T.List['File']:
183        return self.sources
184
185
186class InstallDir(HoldableObject):
187
188    def __init__(self, src_subdir: str, inst_subdir: str, install_dir: str,
189                 install_mode: 'FileMode',
190                 exclude: T.Tuple[T.Set[str], T.Set[str]],
191                 strip_directory: bool, subproject: str,
192                 from_source_dir: bool = True):
193        self.source_subdir = src_subdir
194        self.installable_subdir = inst_subdir
195        self.install_dir = install_dir
196        self.install_mode = install_mode
197        self.exclude = exclude
198        self.strip_directory = strip_directory
199        self.from_source_dir = from_source_dir
200        self.subproject = subproject
201
202
203class Build:
204    """A class that holds the status of one build including
205    all dependencies and so on.
206    """
207
208    def __init__(self, environment: environment.Environment):
209        self.project_name = 'name of master project'
210        self.project_version = None
211        self.environment = environment
212        self.projects = {}
213        self.targets: T.MutableMapping[str, 'Target'] = OrderedDict()
214        self.run_target_names: T.Set[T.Tuple[str, str]] = set()
215        self.global_args: PerMachine[T.Dict[str, T.List[str]]] = PerMachine({}, {})
216        self.global_link_args: PerMachine[T.Dict[str, T.List[str]]] = PerMachine({}, {})
217        self.projects_args: PerMachine[T.Dict[str, T.Dict[str, T.List[str]]]] = PerMachine({}, {})
218        self.projects_link_args: PerMachine[T.Dict[str, T.Dict[str, T.List[str]]]] = PerMachine({}, {})
219        self.tests: T.List['Test'] = []
220        self.benchmarks: T.List['Test'] = []
221        self.headers: T.List[Headers] = []
222        self.man: T.List[Man] = []
223        self.data: T.List[Data] = []
224        self.static_linker: PerMachine[StaticLinker] = PerMachine(None, None)
225        self.subprojects = {}
226        self.subproject_dir = ''
227        self.install_scripts = []
228        self.postconf_scripts = []
229        self.dist_scripts = []
230        self.install_dirs: T.List[InstallDir] = []
231        self.dep_manifest_name = None
232        self.dep_manifest = {}
233        self.stdlibs = PerMachine({}, {})
234        self.test_setups: T.Dict[str, TestSetup] = {}
235        self.test_setup_default_name = None
236        self.find_overrides = {}
237        self.searched_programs = set() # The list of all programs that have been searched for.
238
239        # If we are doing a cross build we need two caches, if we're doing a
240        # build == host compilation the both caches should point to the same place.
241        self.dependency_overrides: PerMachine[T.Dict[T.Tuple, DependencyOverride]] = PerMachineDefaultable.default(
242            environment.is_cross_build(), {}, {})
243        self.devenv: T.List[EnvironmentVariables] = []
244
245    def get_build_targets(self):
246        build_targets = OrderedDict()
247        for name, t in self.targets.items():
248            if isinstance(t, BuildTarget):
249                build_targets[name] = t
250        return build_targets
251
252    def get_custom_targets(self):
253        custom_targets = OrderedDict()
254        for name, t in self.targets.items():
255            if isinstance(t, CustomTarget):
256                custom_targets[name] = t
257        return custom_targets
258
259    def copy(self):
260        other = Build(self.environment)
261        for k, v in self.__dict__.items():
262            if isinstance(v, (list, dict, set, OrderedDict)):
263                other.__dict__[k] = v.copy()
264            else:
265                other.__dict__[k] = v
266        return other
267
268    def merge(self, other):
269        for k, v in other.__dict__.items():
270            self.__dict__[k] = v
271
272    def ensure_static_linker(self, compiler):
273        if self.static_linker[compiler.for_machine] is None and compiler.needs_static_linker():
274            self.static_linker[compiler.for_machine] = detect_static_linker(self.environment, compiler)
275
276    def get_project(self):
277        return self.projects['']
278
279    def get_subproject_dir(self):
280        return self.subproject_dir
281
282    def get_targets(self) -> T.Dict[str, 'Target']:
283        return self.targets
284
285    def get_tests(self) -> T.List['Test']:
286        return self.tests
287
288    def get_benchmarks(self) -> T.List['Test']:
289        return self.benchmarks
290
291    def get_headers(self):
292        return self.headers
293
294    def get_man(self):
295        return self.man
296
297    def get_data(self):
298        return self.data
299
300    def get_install_subdirs(self):
301        return self.install_dirs
302
303    def get_global_args(self, compiler: 'Compiler', for_machine: 'MachineChoice') -> T.List[str]:
304        d = self.global_args[for_machine]
305        return d.get(compiler.get_language(), [])
306
307    def get_project_args(self, compiler: 'Compiler', project: str, for_machine: 'MachineChoice') -> T.List[str]:
308        d = self.projects_args[for_machine]
309        args = d.get(project)
310        if not args:
311            return []
312        return args.get(compiler.get_language(), [])
313
314    def get_global_link_args(self, compiler: 'Compiler', for_machine: 'MachineChoice') -> T.List[str]:
315        d = self.global_link_args[for_machine]
316        return d.get(compiler.get_language(), [])
317
318    def get_project_link_args(self, compiler: 'Compiler', project: str, for_machine: 'MachineChoice') -> T.List[str]:
319        d = self.projects_link_args[for_machine]
320
321        link_args = d.get(project)
322        if not link_args:
323            return []
324
325        return link_args.get(compiler.get_language(), [])
326
327class IncludeDirs(HoldableObject):
328
329    """Internal representation of an include_directories call."""
330
331    def __init__(self, curdir: str, dirs: T.List[str], is_system: bool, extra_build_dirs: T.Optional[T.List[str]] = None):
332        self.curdir = curdir
333        self.incdirs = dirs
334        self.is_system = is_system
335
336        # Interpreter has validated that all given directories
337        # actually exist.
338        self.extra_build_dirs: T.List[str] = extra_build_dirs or []
339
340    def __repr__(self) -> str:
341        r = '<{} {}/{}>'
342        return r.format(self.__class__.__name__, self.curdir, self.incdirs)
343
344    def get_curdir(self) -> str:
345        return self.curdir
346
347    def get_incdirs(self) -> T.List[str]:
348        return self.incdirs
349
350    def get_extra_build_dirs(self) -> T.List[str]:
351        return self.extra_build_dirs
352
353    def to_string_list(self, sourcedir: str) -> T.List[str]:
354        """Convert IncludeDirs object to a list of strings."""
355        strlist: T.List[str] = []
356        for idir in self.incdirs:
357            strlist.append(os.path.join(sourcedir, self.curdir, idir))
358        return strlist
359
360class ExtractedObjects(HoldableObject):
361    '''
362    Holds a list of sources for which the objects must be extracted
363    '''
364    def __init__(self, target, srclist=None, genlist=None, objlist=None, recursive=True):
365        self.target = target
366        self.recursive = recursive
367        self.srclist = srclist if srclist is not None else []
368        self.genlist = genlist if genlist is not None else []
369        self.objlist = objlist if objlist is not None else []
370        if self.target.is_unity:
371            self.check_unity_compatible()
372
373    def __repr__(self):
374        r = '<{0} {1!r}: {2}>'
375        return r.format(self.__class__.__name__, self.target.name, self.srclist)
376
377    @staticmethod
378    def get_sources(sources, generated_sources):
379        # Merge sources and generated sources
380        sources = list(sources)
381        for gensrc in generated_sources:
382            for s in gensrc.get_outputs():
383                # We cannot know the path where this source will be generated,
384                # but all we need here is the file extension to determine the
385                # compiler.
386                sources.append(s)
387
388        # Filter out headers and all non-source files
389        return [s for s in sources if environment.is_source(s) and not environment.is_header(s)]
390
391    def classify_all_sources(self, sources, generated_sources):
392        sources = self.get_sources(sources, generated_sources)
393        return classify_unity_sources(self.target.compilers.values(), sources)
394
395    def check_unity_compatible(self):
396        # Figure out if the extracted object list is compatible with a Unity
397        # build. When we're doing a Unified build, we go through the sources,
398        # and create a single source file from each subset of the sources that
399        # can be compiled with a specific compiler. Then we create one object
400        # from each unified source file. So for each compiler we can either
401        # extra all its sources or none.
402        cmpsrcs = self.classify_all_sources(self.target.sources, self.target.generated)
403        extracted_cmpsrcs = self.classify_all_sources(self.srclist, self.genlist)
404
405        for comp, srcs in extracted_cmpsrcs.items():
406            if set(srcs) != set(cmpsrcs[comp]):
407                raise MesonException('Single object files can not be extracted '
408                                     'in Unity builds. You can only extract all '
409                                     'the object files for each compiler at once.')
410
411    def get_outputs(self, backend):
412        return [
413            backend.object_filename_from_source(self.target, source)
414            for source in self.get_sources(self.srclist, self.genlist)
415        ]
416
417class EnvironmentVariables(HoldableObject):
418    def __init__(self) -> None:
419        self.envvars = []
420        # The set of all env vars we have operations for. Only used for self.has_name()
421        self.varnames = set()
422
423    def __repr__(self):
424        repr_str = "<{0}: {1}>"
425        return repr_str.format(self.__class__.__name__, self.envvars)
426
427    def has_name(self, name: str) -> bool:
428        return name in self.varnames
429
430    def set(self, name: str, values: T.List[str], separator: str = os.pathsep) -> None:
431        self.varnames.add(name)
432        self.envvars.append((self._set, name, values, separator))
433
434    def append(self, name: str, values: T.List[str], separator: str = os.pathsep) -> None:
435        self.varnames.add(name)
436        self.envvars.append((self._append, name, values, separator))
437
438    def prepend(self, name: str, values: T.List[str], separator: str = os.pathsep) -> None:
439        self.varnames.add(name)
440        self.envvars.append((self._prepend, name, values, separator))
441
442    def _set(self, env: T.Dict[str, str], name: str, values: T.List[str], separator: str) -> str:
443        return separator.join(values)
444
445    def _append(self, env: T.Dict[str, str], name: str, values: T.List[str], separator: str) -> str:
446        curr = env.get(name)
447        return separator.join(values if curr is None else [curr] + values)
448
449    def _prepend(self, env: T.Dict[str, str], name: str, values: T.List[str], separator: str) -> str:
450        curr = env.get(name)
451        return separator.join(values if curr is None else values + [curr])
452
453    def get_env(self, full_env: T.Dict[str, str]) -> T.Dict[str, str]:
454        env = full_env.copy()
455        for method, name, values, separator in self.envvars:
456            env[name] = method(env, name, values, separator)
457        return env
458
459class Target(HoldableObject):
460
461    # TODO: should Target be an abc.ABCMeta?
462
463    def __init__(self, name: str, subdir: str, subproject: str, build_by_default: bool, for_machine: MachineChoice):
464        if has_path_sep(name):
465            # Fix failing test 53 when this becomes an error.
466            mlog.warning(textwrap.dedent(f'''\
467                Target "{name}" has a path separator in its name.
468                This is not supported, it can cause unexpected failures and will become
469                a hard error in the future.\
470            '''))
471        self.name = name
472        self.subdir = subdir
473        self.subproject = subproject
474        self.build_by_default = build_by_default
475        self.for_machine = for_machine
476        self.install = False
477        self.build_always_stale = False
478        self.option_overrides_base: T.Dict[OptionKey, str] = {}
479        self.option_overrides_compiler: T.Dict[OptionKey, str] = {}
480        self.extra_files = []  # type: T.List[File]
481        if not hasattr(self, 'typename'):
482            raise RuntimeError(f'Target type is not set for target class "{type(self).__name__}". This is a bug')
483
484    def __lt__(self, other: object) -> bool:
485        if not hasattr(other, 'get_id') and not callable(other.get_id):
486            return NotImplemented
487        return self.get_id() < other.get_id()
488
489    def __le__(self, other: object) -> bool:
490        if not hasattr(other, 'get_id') and not callable(other.get_id):
491            return NotImplemented
492        return self.get_id() <= other.get_id()
493
494    def __gt__(self, other: object) -> bool:
495        if not hasattr(other, 'get_id') and not callable(other.get_id):
496            return NotImplemented
497        return self.get_id() > other.get_id()
498
499    def __ge__(self, other: object) -> bool:
500        if not hasattr(other, 'get_id') and not callable(other.get_id):
501            return NotImplemented
502        return self.get_id() >= other.get_id()
503
504    def get_default_install_dir(self, env: environment.Environment) -> str:
505        raise NotImplementedError
506
507    def get_install_dir(self, environment: environment.Environment) -> T.Tuple[T.Any, bool]:
508        # Find the installation directory.
509        default_install_dir = self.get_default_install_dir(environment)
510        outdirs = self.get_custom_install_dir()
511        if outdirs[0] is not None and outdirs[0] != default_install_dir and outdirs[0] is not True:
512            # Either the value is set to a non-default value, or is set to
513            # False (which means we want this specific output out of many
514            # outputs to not be installed).
515            custom_install_dir = True
516        else:
517            custom_install_dir = False
518            outdirs[0] = default_install_dir
519        return outdirs, custom_install_dir
520
521    def get_basename(self) -> str:
522        return self.name
523
524    def get_subdir(self) -> str:
525        return self.subdir
526
527    def get_typename(self) -> str:
528        return self.typename
529
530    @staticmethod
531    def _get_id_hash(target_id):
532        # We don't really need cryptographic security here.
533        # Small-digest hash function with unlikely collision is good enough.
534        h = hashlib.sha256()
535        h.update(target_id.encode(encoding='utf-8', errors='replace'))
536        # This ID should be case-insensitive and should work in Visual Studio,
537        # e.g. it should not start with leading '-'.
538        return h.hexdigest()[:7]
539
540    @staticmethod
541    def construct_id_from_path(subdir: str, name: str, type_suffix: str) -> str:
542        """Construct target ID from subdir, name and type suffix.
543
544        This helper function is made public mostly for tests."""
545        # This ID must also be a valid file name on all OSs.
546        # It should also avoid shell metacharacters for obvious
547        # reasons. '@' is not used as often as '_' in source code names.
548        # In case of collisions consider using checksums.
549        # FIXME replace with assert when slash in names is prohibited
550        name_part = name.replace('/', '@').replace('\\', '@')
551        assert not has_path_sep(type_suffix)
552        my_id = name_part + type_suffix
553        if subdir:
554            subdir_part = Target._get_id_hash(subdir)
555            # preserve myid for better debuggability
556            return subdir_part + '@@' + my_id
557        return my_id
558
559    def get_id(self) -> str:
560        return self.construct_id_from_path(
561            self.subdir, self.name, self.type_suffix())
562
563    def process_kwargs_base(self, kwargs: T.Dict[str, T.Any]) -> None:
564        if 'build_by_default' in kwargs:
565            self.build_by_default = kwargs['build_by_default']
566            if not isinstance(self.build_by_default, bool):
567                raise InvalidArguments('build_by_default must be a boolean value.')
568        elif kwargs.get('install', False):
569            # For backward compatibility, if build_by_default is not explicitly
570            # set, use the value of 'install' if it's enabled.
571            self.build_by_default = True
572
573        option_overrides = self.parse_overrides(kwargs)
574
575        for k, v in option_overrides.items():
576            if k.lang:
577                self.option_overrides_compiler[k.evolve(machine=self.for_machine)] = v
578                continue
579            self.option_overrides_base[k] = v
580
581    @staticmethod
582    def parse_overrides(kwargs: T.Dict[str, T.Any]) -> T.Dict[OptionKey, str]:
583        result: T.Dict[OptionKey, str] = {}
584        overrides = stringlistify(kwargs.get('override_options', []))
585        for o in overrides:
586            if '=' not in o:
587                raise InvalidArguments('Overrides must be of form "key=value"')
588            k, v = o.split('=', 1)
589            key = OptionKey.from_string(k.strip())
590            v = v.strip()
591            result[key] = v
592        return result
593
594    def is_linkable_target(self) -> bool:
595        return False
596
597    def get_outputs(self) -> T.List[str]:
598        return []
599
600    def should_install(self) -> bool:
601        return False
602
603class BuildTarget(Target):
604    known_kwargs = known_build_target_kwargs
605
606    def __init__(self, name: str, subdir: str, subproject: str, for_machine: MachineChoice,
607                 sources: T.List['SourceOutputs'], objects, environment: environment.Environment, kwargs):
608        super().__init__(name, subdir, subproject, True, for_machine)
609        unity_opt = environment.coredata.get_option(OptionKey('unity'))
610        self.is_unity = unity_opt == 'on' or (unity_opt == 'subprojects' and subproject != '')
611        self.environment = environment
612        self.compilers = OrderedDict() # type: OrderedDict[str, Compiler]
613        self.objects = []
614        self.external_deps = []
615        self.include_dirs = []
616        self.link_language = kwargs.get('link_language')
617        self.link_targets: T.List[BuildTarget] = []
618        self.link_whole_targets = []
619        self.link_depends = []
620        self.added_deps = set()
621        self.name_prefix_set = False
622        self.name_suffix_set = False
623        self.filename = 'no_name'
624        # The list of all files outputted by this target. Useful in cases such
625        # as Vala which generates .vapi and .h besides the compiled output.
626        self.outputs = [self.filename]
627        self.need_install = False
628        self.pch = {}
629        self.extra_args: T.Dict[str, T.List['FileOrString']] = {}
630        self.sources: T.List[File] = []
631        self.generated: T.List[T.Union[GeneratedList, CustomTarget, CustomTargetIndex]] = []
632        self.d_features = {}
633        self.pic = False
634        self.pie = False
635        # Track build_rpath entries so we can remove them at install time
636        self.rpath_dirs_to_remove: T.Set[bytes] = set()
637        self.process_sourcelist(sources)
638        # Objects can be:
639        # 1. Pre-existing objects provided by the user with the `objects:` kwarg
640        # 2. Compiled objects created by and extracted from another target
641        self.process_objectlist(objects)
642        self.process_kwargs(kwargs, environment)
643        self.check_unknown_kwargs(kwargs)
644        self.process_compilers()
645        if not any([self.sources, self.generated, self.objects, self.link_whole]):
646            raise InvalidArguments(f'Build target {name} has no sources.')
647        self.process_compilers_late()
648        self.validate_sources()
649        self.validate_install(environment)
650        self.check_module_linking()
651
652    def __repr__(self):
653        repr_str = "<{0} {1}: {2}>"
654        return repr_str.format(self.__class__.__name__, self.get_id(), self.filename)
655
656    def __str__(self):
657        return f"{self.name}"
658
659    def validate_install(self, environment):
660        if self.for_machine is MachineChoice.BUILD and self.need_install:
661            if environment.is_cross_build():
662                raise InvalidArguments('Tried to install a target for the build machine in a cross build.')
663            else:
664                mlog.warning('Installing target build for the build machine. This will fail in a cross build.')
665
666    def check_unknown_kwargs(self, kwargs):
667        # Override this method in derived classes that have more
668        # keywords.
669        self.check_unknown_kwargs_int(kwargs, self.known_kwargs)
670
671    def check_unknown_kwargs_int(self, kwargs, known_kwargs):
672        unknowns = []
673        for k in kwargs:
674            if k not in known_kwargs:
675                unknowns.append(k)
676        if len(unknowns) > 0:
677            mlog.warning('Unknown keyword argument(s) in target {}: {}.'.format(self.name, ', '.join(unknowns)))
678
679    def process_objectlist(self, objects):
680        assert(isinstance(objects, list))
681        for s in objects:
682            if isinstance(s, (str, File, ExtractedObjects)):
683                self.objects.append(s)
684            elif isinstance(s, (GeneratedList, CustomTarget)):
685                msg = 'Generated files are not allowed in the \'objects\' kwarg ' + \
686                    f'for target {self.name!r}.\nIt is meant only for ' + \
687                    'pre-built object files that are shipped with the\nsource ' + \
688                    'tree. Try adding it in the list of sources.'
689                raise InvalidArguments(msg)
690            else:
691                raise InvalidArguments(f'Bad object of type {type(s).__name__!r} in target {self.name!r}.')
692
693    def process_sourcelist(self, sources: T.List['SourceOutputs']) -> None:
694        """Split sources into generated and static sources.
695
696        Sources can be:
697        1. Pre-existing source files in the source tree (static)
698        2. Pre-existing sources generated by configure_file in the build tree.
699           (static as they are only regenerated if meson itself is regenerated)
700        3. Sources files generated by another target or a Generator (generated)
701        """
702        added_sources: T.Set[File] = set() # If the same source is defined multiple times, use it only once.
703        for s in sources:
704            if isinstance(s, File):
705                if s not in added_sources:
706                    self.sources.append(s)
707                    added_sources.add(s)
708            elif isinstance(s, (CustomTarget, CustomTargetIndex, GeneratedList)):
709                self.generated.append(s)
710
711    @staticmethod
712    def can_compile_remove_sources(compiler: 'Compiler', sources: T.List['FileOrString']) -> bool:
713        removed = False
714        for s in sources[:]:
715            if compiler.can_compile(s):
716                sources.remove(s)
717                removed = True
718        return removed
719
720    def process_compilers_late(self):
721        """Processes additional compilers after kwargs have been evaluated.
722
723        This can add extra compilers that might be required by keyword
724        arguments, such as link_with or dependencies. It will also try to guess
725        which compiler to use if one hasn't been selected already.
726        """
727        # Populate list of compilers
728        compilers = self.environment.coredata.compilers[self.for_machine]
729
730        # did user override clink_langs for this target?
731        link_langs = [self.link_language] if self.link_language else clink_langs
732
733        # If this library is linked against another library we need to consider
734        # the languages of those libraries as well.
735        if self.link_targets or self.link_whole_targets:
736            extra = set()
737            for t in itertools.chain(self.link_targets, self.link_whole_targets):
738                if isinstance(t, CustomTarget) or isinstance(t, CustomTargetIndex):
739                    continue # We can't know anything about these.
740                for name, compiler in t.compilers.items():
741                    if name in link_langs:
742                        extra.add((name, compiler))
743            for name, compiler in sorted(extra, key=lambda p: sort_clink(p[0])):
744                self.compilers[name] = compiler
745
746        if not self.compilers:
747            # No source files or parent targets, target consists of only object
748            # files of unknown origin. Just add the first clink compiler
749            # that we have and hope that it can link these objects
750            for lang in link_langs:
751                if lang in compilers:
752                    self.compilers[lang] = compilers[lang]
753                    break
754
755    def process_compilers(self):
756        '''
757        Populate self.compilers, which is the list of compilers that this
758        target will use for compiling all its sources.
759        We also add compilers that were used by extracted objects to simplify
760        dynamic linker determination.
761        '''
762        if not self.sources and not self.generated and not self.objects:
763            return
764        # Populate list of compilers
765        compilers = self.environment.coredata.compilers[self.for_machine]
766        # Pre-existing sources
767        sources = list(self.sources)
768        # All generated sources
769        for gensrc in self.generated:
770            for s in gensrc.get_outputs():
771                # Generated objects can't be compiled, so don't use them for
772                # compiler detection. If our target only has generated objects,
773                # we will fall back to using the first c-like compiler we find,
774                # which is what we need.
775                if not is_object(s):
776                    sources.append(s)
777        for d in self.external_deps:
778            for s in d.sources:
779                if isinstance(s, (str, File)):
780                    sources.append(s)
781
782        # Sources that were used to create our extracted objects
783        for o in self.objects:
784            if not isinstance(o, ExtractedObjects):
785                continue
786            for s in o.srclist:
787                # Don't add Vala sources since that will pull in the Vala
788                # compiler even though we will never use it since we are
789                # dealing with compiled C code.
790                if not s.endswith(lang_suffixes['vala']):
791                    sources.append(s)
792        if sources:
793            # For each source, try to add one compiler that can compile it.
794            #
795            # If it has a suffix that belongs to a known language, we must have
796            # a compiler for that language.
797            #
798            # Otherwise, it's ok if no compilers can compile it, because users
799            # are expected to be able to add arbitrary non-source files to the
800            # sources list
801            for s in sources:
802                for lang, compiler in compilers.items():
803                    if compiler.can_compile(s):
804                        if lang not in self.compilers:
805                            self.compilers[lang] = compiler
806                        break
807                else:
808                    if is_known_suffix(s):
809                        raise MesonException('No {} machine compiler for "{}"'.
810                                             format(self.for_machine.get_lower_case_name(), s))
811
812            # Re-sort according to clink_langs
813            self.compilers = OrderedDict(sorted(self.compilers.items(),
814                                                key=lambda t: sort_clink(t[0])))
815
816        # If all our sources are Vala, our target also needs the C compiler but
817        # it won't get added above.
818        if ('vala' in self.compilers or 'cython' in self.compilers) and 'c' not in self.compilers:
819            self.compilers['c'] = compilers['c']
820
821    def validate_sources(self):
822        if not self.sources:
823            return
824        for lang in ('cs', 'java'):
825            if lang in self.compilers:
826                check_sources = list(self.sources)
827                compiler = self.compilers[lang]
828                if not self.can_compile_remove_sources(compiler, check_sources):
829                    raise InvalidArguments(f'No {lang} sources found in target {self.name!r}')
830                if check_sources:
831                    m = '{0} targets can only contain {0} files:\n'.format(lang.capitalize())
832                    m += '\n'.join([repr(c) for c in check_sources])
833                    raise InvalidArguments(m)
834                # CSharp and Java targets can't contain any other file types
835                assert(len(self.compilers) == 1)
836                return
837
838    def process_link_depends(self, sources, environment):
839        """Process the link_depends keyword argument.
840
841        This is designed to handle strings, Files, and the output of Custom
842        Targets. Notably it doesn't handle generator() returned objects, since
843        adding them as a link depends would inherently cause them to be
844        generated twice, since the output needs to be passed to the ld_args and
845        link_depends.
846        """
847        sources = listify(sources)
848        for s in sources:
849            if isinstance(s, File):
850                self.link_depends.append(s)
851            elif isinstance(s, str):
852                self.link_depends.append(
853                    File.from_source_file(environment.source_dir, self.subdir, s))
854            elif hasattr(s, 'get_outputs'):
855                self.link_depends.extend(
856                    [File.from_built_file(s.get_subdir(), p) for p in s.get_outputs()])
857            else:
858                raise InvalidArguments(
859                    'Link_depends arguments must be strings, Files, '
860                    'or a Custom Target, or lists thereof.')
861
862    def get_original_kwargs(self):
863        return self.kwargs
864
865    def copy_kwargs(self, kwargs):
866        self.kwargs = copy.copy(kwargs)
867        for k, v in self.kwargs.items():
868            if isinstance(v, list):
869                self.kwargs[k] = listify(v, flatten=True)
870        for t in ['dependencies', 'link_with', 'include_directories', 'sources']:
871            if t in self.kwargs:
872                self.kwargs[t] = listify(self.kwargs[t], flatten=True)
873
874    def extract_objects(self, srclist: T.List[FileOrString]) -> ExtractedObjects:
875        obj_src = []
876        sources_set = set(self.sources)
877        for src in srclist:
878            if isinstance(src, str):
879                src = File(False, self.subdir, src)
880            elif isinstance(src, File):
881                FeatureNew.single_use('File argument for extract_objects', '0.50.0', self.subproject)
882            else:
883                raise MesonException(f'Object extraction arguments must be strings or Files (got {type(src).__name__}).')
884            # FIXME: It could be a generated source
885            if src not in sources_set:
886                raise MesonException(f'Tried to extract unknown source {src}.')
887            obj_src.append(src)
888        return ExtractedObjects(self, obj_src)
889
890    def extract_all_objects(self, recursive: bool = True) -> ExtractedObjects:
891        return ExtractedObjects(self, self.sources, self.generated, self.objects,
892                                recursive)
893
894    def get_all_link_deps(self):
895        return self.get_transitive_link_deps()
896
897    @lru_cache(maxsize=None)
898    def get_transitive_link_deps(self) -> 'ImmutableListProtocol[Target]':
899        result: T.List[Target] = []
900        for i in self.link_targets:
901            result += i.get_all_link_deps()
902        return result
903
904    def get_link_deps_mapping(self, prefix: str, environment: environment.Environment) -> T.Mapping[str, str]:
905        return self.get_transitive_link_deps_mapping(prefix, environment)
906
907    @lru_cache(maxsize=None)
908    def get_transitive_link_deps_mapping(self, prefix: str, environment: environment.Environment) -> T.Mapping[str, str]:
909        result: T.Dict[str, str] = {}
910        for i in self.link_targets:
911            mapping = i.get_link_deps_mapping(prefix, environment)
912            #we are merging two dictionaries, while keeping the earlier one dominant
913            result_tmp = mapping.copy()
914            result_tmp.update(result)
915            result = result_tmp
916        return result
917
918    @lru_cache(maxsize=None)
919    def get_link_dep_subdirs(self) -> 'ImmutableSetProtocol[str]':
920        result: OrderedSet[str] = OrderedSet()
921        for i in self.link_targets:
922            if not isinstance(i, StaticLibrary):
923                result.add(i.get_subdir())
924            result.update(i.get_link_dep_subdirs())
925        return result
926
927    def get_default_install_dir(self, environment: environment.Environment) -> str:
928        return environment.get_libdir()
929
930    def get_custom_install_dir(self):
931        return self.install_dir
932
933    def get_custom_install_mode(self):
934        return self.install_mode
935
936    def process_kwargs(self, kwargs, environment):
937        self.process_kwargs_base(kwargs)
938        self.copy_kwargs(kwargs)
939        kwargs.get('modules', [])
940        self.need_install = kwargs.get('install', self.need_install)
941        llist = extract_as_list(kwargs, 'link_with')
942        for linktarget in llist:
943            if isinstance(linktarget, dependencies.ExternalLibrary):
944                raise MesonException(textwrap.dedent('''\
945                    An external library was used in link_with keyword argument, which
946                    is reserved for libraries built as part of this project. External
947                    libraries must be passed using the dependencies keyword argument
948                    instead, because they are conceptually "external dependencies",
949                    just like those detected with the dependency() function.\
950                '''))
951            self.link(linktarget)
952        lwhole = extract_as_list(kwargs, 'link_whole')
953        for linktarget in lwhole:
954            self.link_whole(linktarget)
955
956        c_pchlist, cpp_pchlist, clist, cpplist, cudalist, cslist, valalist,  objclist, objcpplist, fortranlist, rustlist \
957            = [extract_as_list(kwargs, c) for c in ['c_pch', 'cpp_pch', 'c_args', 'cpp_args', 'cuda_args', 'cs_args', 'vala_args', 'objc_args', 'objcpp_args', 'fortran_args', 'rust_args']]
958
959        self.add_pch('c', c_pchlist)
960        self.add_pch('cpp', cpp_pchlist)
961        compiler_args = {'c': clist, 'cpp': cpplist, 'cuda': cudalist, 'cs': cslist, 'vala': valalist, 'objc': objclist, 'objcpp': objcpplist,
962                         'fortran': fortranlist, 'rust': rustlist
963                         }
964        for key, value in compiler_args.items():
965            self.add_compiler_args(key, value)
966
967        if not isinstance(self, Executable) or 'export_dynamic' in kwargs:
968            self.vala_header = kwargs.get('vala_header', self.name + '.h')
969            self.vala_vapi = kwargs.get('vala_vapi', self.name + '.vapi')
970            self.vala_gir = kwargs.get('vala_gir', None)
971
972        dlist = stringlistify(kwargs.get('d_args', []))
973        self.add_compiler_args('d', dlist)
974        dfeatures = dict()
975        dfeature_unittest = kwargs.get('d_unittest', False)
976        if dfeature_unittest:
977            dfeatures['unittest'] = dfeature_unittest
978        dfeature_versions = kwargs.get('d_module_versions', [])
979        if dfeature_versions:
980            dfeatures['versions'] = dfeature_versions
981        dfeature_debug = kwargs.get('d_debug', [])
982        if dfeature_debug:
983            dfeatures['debug'] = dfeature_debug
984        if 'd_import_dirs' in kwargs:
985            dfeature_import_dirs = extract_as_list(kwargs, 'd_import_dirs')
986            for d in dfeature_import_dirs:
987                if not isinstance(d, IncludeDirs):
988                    raise InvalidArguments('Arguments to d_import_dirs must be include_directories.')
989            dfeatures['import_dirs'] = dfeature_import_dirs
990        if dfeatures:
991            self.d_features = dfeatures
992
993        self.link_args = extract_as_list(kwargs, 'link_args')
994        for i in self.link_args:
995            if not isinstance(i, str):
996                raise InvalidArguments('Link_args arguments must be strings.')
997        for l in self.link_args:
998            if '-Wl,-rpath' in l or l.startswith('-rpath'):
999                mlog.warning(textwrap.dedent('''\
1000                    Please do not define rpath with a linker argument, use install_rpath
1001                    or build_rpath properties instead.
1002                    This will become a hard error in a future Meson release.\
1003                '''))
1004        self.process_link_depends(kwargs.get('link_depends', []), environment)
1005        # Target-specific include dirs must be added BEFORE include dirs from
1006        # internal deps (added inside self.add_deps()) to override them.
1007        inclist = extract_as_list(kwargs, 'include_directories')
1008        self.add_include_dirs(inclist)
1009        # Add dependencies (which also have include_directories)
1010        deplist = extract_as_list(kwargs, 'dependencies')
1011        self.add_deps(deplist)
1012        # If an item in this list is False, the output corresponding to
1013        # the list index of that item will not be installed
1014        self.install_dir = typeslistify(kwargs.get('install_dir', [None]),
1015                                        (str, bool))
1016        self.install_mode = kwargs.get('install_mode', None)
1017        main_class = kwargs.get('main_class', '')
1018        if not isinstance(main_class, str):
1019            raise InvalidArguments('Main class must be a string')
1020        self.main_class = main_class
1021        if isinstance(self, Executable):
1022            # This kwarg is deprecated. The value of "none" means that the kwarg
1023            # was not specified and win_subsystem should be used instead.
1024            self.gui_app = None
1025            if 'gui_app' in kwargs:
1026                if 'win_subsystem' in kwargs:
1027                    raise InvalidArguments('Can specify only gui_app or win_subsystem for a target, not both.')
1028                self.gui_app = kwargs['gui_app']
1029                if not isinstance(self.gui_app, bool):
1030                    raise InvalidArguments('Argument gui_app must be boolean.')
1031            self.win_subsystem = self.validate_win_subsystem(kwargs.get('win_subsystem', 'console'))
1032        elif 'gui_app' in kwargs:
1033            raise InvalidArguments('Argument gui_app can only be used on executables.')
1034        elif 'win_subsystem' in kwargs:
1035            raise InvalidArguments('Argument win_subsystem can only be used on executables.')
1036        extra_files = extract_as_list(kwargs, 'extra_files')
1037        for i in extra_files:
1038            assert(isinstance(i, File))
1039            trial = os.path.join(environment.get_source_dir(), i.subdir, i.fname)
1040            if not(os.path.isfile(trial)):
1041                raise InvalidArguments(f'Tried to add non-existing extra file {i}.')
1042        self.extra_files = extra_files
1043        self.install_rpath: str = kwargs.get('install_rpath', '')
1044        if not isinstance(self.install_rpath, str):
1045            raise InvalidArguments('Install_rpath is not a string.')
1046        self.build_rpath = kwargs.get('build_rpath', '')
1047        if not isinstance(self.build_rpath, str):
1048            raise InvalidArguments('Build_rpath is not a string.')
1049        resources = extract_as_list(kwargs, 'resources')
1050        for r in resources:
1051            if not isinstance(r, str):
1052                raise InvalidArguments('Resource argument is not a string.')
1053            trial = os.path.join(environment.get_source_dir(), self.subdir, r)
1054            if not os.path.isfile(trial):
1055                raise InvalidArguments(f'Tried to add non-existing resource {r}.')
1056        self.resources = resources
1057        if 'name_prefix' in kwargs:
1058            name_prefix = kwargs['name_prefix']
1059            if isinstance(name_prefix, list):
1060                if name_prefix:
1061                    raise InvalidArguments('name_prefix array must be empty to signify default.')
1062            else:
1063                if not isinstance(name_prefix, str):
1064                    raise InvalidArguments('name_prefix must be a string.')
1065                self.prefix = name_prefix
1066                self.name_prefix_set = True
1067        if 'name_suffix' in kwargs:
1068            name_suffix = kwargs['name_suffix']
1069            if isinstance(name_suffix, list):
1070                if name_suffix:
1071                    raise InvalidArguments('name_suffix array must be empty to signify default.')
1072            else:
1073                if not isinstance(name_suffix, str):
1074                    raise InvalidArguments('name_suffix must be a string.')
1075                if name_suffix == '':
1076                    raise InvalidArguments('name_suffix should not be an empty string. '
1077                                           'If you want meson to use the default behaviour '
1078                                           'for each platform pass `[]` (empty array)')
1079                self.suffix = name_suffix
1080                self.name_suffix_set = True
1081        if isinstance(self, StaticLibrary):
1082            # You can't disable PIC on OS X. The compiler ignores -fno-PIC.
1083            # PIC is always on for Windows (all code is position-independent
1084            # since library loading is done differently)
1085            m = self.environment.machines[self.for_machine]
1086            if m.is_darwin() or m.is_windows():
1087                self.pic = True
1088            else:
1089                self.pic = self._extract_pic_pie(kwargs, 'pic', environment, 'b_staticpic')
1090        if isinstance(self, Executable) or (isinstance(self, StaticLibrary) and not self.pic):
1091            # Executables must be PIE on Android
1092            if self.environment.machines[self.for_machine].is_android():
1093                self.pie = True
1094            else:
1095                self.pie = self._extract_pic_pie(kwargs, 'pie', environment, 'b_pie')
1096        self.implicit_include_directories = kwargs.get('implicit_include_directories', True)
1097        if not isinstance(self.implicit_include_directories, bool):
1098            raise InvalidArguments('Implicit_include_directories must be a boolean.')
1099        self.gnu_symbol_visibility = kwargs.get('gnu_symbol_visibility', '')
1100        if not isinstance(self.gnu_symbol_visibility, str):
1101            raise InvalidArguments('GNU symbol visibility must be a string.')
1102        if self.gnu_symbol_visibility != '':
1103            permitted = ['default', 'internal', 'hidden', 'protected', 'inlineshidden']
1104            if self.gnu_symbol_visibility not in permitted:
1105                raise InvalidArguments('GNU symbol visibility arg {} not one of: {}'.format(self.symbol_visibility, ', '.join(permitted)))
1106
1107    def validate_win_subsystem(self, value: str) -> str:
1108        value = value.lower()
1109        if re.fullmatch(r'(boot_application|console|efi_application|efi_boot_service_driver|efi_rom|efi_runtime_driver|native|posix|windows)(,\d+(\.\d+)?)?', value) is None:
1110            raise InvalidArguments(f'Invalid value for win_subsystem: {value}.')
1111        return value
1112
1113    def _extract_pic_pie(self, kwargs, arg: str, environment, option: str):
1114        # Check if we have -fPIC, -fpic, -fPIE, or -fpie in cflags
1115        all_flags = self.extra_args['c'] + self.extra_args['cpp']
1116        if '-f' + arg.lower() in all_flags or '-f' + arg.upper() in all_flags:
1117            mlog.warning(f"Use the '{arg}' kwarg instead of passing '-f{arg}' manually to {self.name!r}")
1118            return True
1119
1120        k = OptionKey(option)
1121        if arg in kwargs:
1122            val = kwargs[arg]
1123        elif k in environment.coredata.options:
1124            val = environment.coredata.options[k].value
1125        else:
1126            val = False
1127
1128        if not isinstance(val, bool):
1129            raise InvalidArguments(f'Argument {arg} to {self.name!r} must be boolean')
1130        return val
1131
1132    def get_filename(self):
1133        return self.filename
1134
1135    def get_outputs(self) -> T.List[str]:
1136        return self.outputs
1137
1138    def get_extra_args(self, language):
1139        return self.extra_args.get(language, [])
1140
1141    def get_dependencies(self, exclude=None):
1142        transitive_deps = []
1143        if exclude is None:
1144            exclude = []
1145        for t in itertools.chain(self.link_targets, self.link_whole_targets):
1146            if t in transitive_deps or t in exclude:
1147                continue
1148            transitive_deps.append(t)
1149            if isinstance(t, StaticLibrary):
1150                transitive_deps += t.get_dependencies(transitive_deps + exclude)
1151        return transitive_deps
1152
1153    def get_source_subdir(self):
1154        return self.subdir
1155
1156    def get_sources(self):
1157        return self.sources
1158
1159    def get_objects(self):
1160        return self.objects
1161
1162    def get_generated_sources(self):
1163        return self.generated
1164
1165    def should_install(self) -> bool:
1166        return self.need_install
1167
1168    def has_pch(self):
1169        return len(self.pch) > 0
1170
1171    def get_pch(self, language):
1172        try:
1173            return self.pch[language]
1174        except KeyError:
1175            return[]
1176
1177    def get_include_dirs(self):
1178        return self.include_dirs
1179
1180    def add_deps(self, deps):
1181        deps = listify(deps)
1182        for dep in deps:
1183            if dep in self.added_deps:
1184                continue
1185            if isinstance(dep, dependencies.InternalDependency):
1186                # Those parts that are internal.
1187                self.process_sourcelist(dep.sources)
1188                self.add_include_dirs(dep.include_directories, dep.get_include_type())
1189                for l in dep.libraries:
1190                    self.link(l)
1191                for l in dep.whole_libraries:
1192                    self.link_whole(l)
1193                if dep.get_compile_args() or dep.get_link_args():
1194                    # Those parts that are external.
1195                    extpart = dependencies.InternalDependency('undefined',
1196                                                              [],
1197                                                              dep.get_compile_args(),
1198                                                              dep.get_link_args(),
1199                                                              [], [], [], [], {})
1200                    self.external_deps.append(extpart)
1201                # Deps of deps.
1202                self.add_deps(dep.ext_deps)
1203            elif isinstance(dep, dependencies.Dependency):
1204                if dep not in self.external_deps:
1205                    self.external_deps.append(dep)
1206                    self.process_sourcelist(dep.get_sources())
1207                self.add_deps(dep.ext_deps)
1208            elif isinstance(dep, BuildTarget):
1209                raise InvalidArguments('''Tried to use a build target as a dependency.
1210You probably should put it in link_with instead.''')
1211            else:
1212                # This is a bit of a hack. We do not want Build to know anything
1213                # about the interpreter so we can't import it and use isinstance.
1214                # This should be reliable enough.
1215                if hasattr(dep, 'project_args_frozen') or hasattr(dep, 'global_args_frozen'):
1216                    raise InvalidArguments('Tried to use subproject object as a dependency.\n'
1217                                           'You probably wanted to use a dependency declared in it instead.\n'
1218                                           'Access it by calling get_variable() on the subproject object.')
1219                raise InvalidArguments(f'Argument is of an unacceptable type {type(dep).__name__!r}.\nMust be '
1220                                       'either an external dependency (returned by find_library() or '
1221                                       'dependency()) or an internal dependency (returned by '
1222                                       'declare_dependency()).')
1223            self.added_deps.add(dep)
1224
1225    def get_external_deps(self):
1226        return self.external_deps
1227
1228    def is_internal(self):
1229        return isinstance(self, StaticLibrary) and not self.need_install
1230
1231    def link(self, target):
1232        for t in listify(target):
1233            if isinstance(self, StaticLibrary) and self.need_install:
1234                if isinstance(t, (CustomTarget, CustomTargetIndex)):
1235                    if not t.should_install():
1236                        mlog.warning(f'Try to link an installed static library target {self.name} with a'
1237                                      'custom target that is not installed, this might cause problems'
1238                                      'when you try to use this static library')
1239                elif t.is_internal():
1240                    # When we're a static library and we link_with to an
1241                    # internal/convenience library, promote to link_whole.
1242                    return self.link_whole(t)
1243            if not isinstance(t, (Target, CustomTargetIndex)):
1244                raise InvalidArguments(f'{t!r} is not a target.')
1245            if not t.is_linkable_target():
1246                raise InvalidArguments(f"Link target '{t!s}' is not linkable.")
1247            if isinstance(self, SharedLibrary) and isinstance(t, StaticLibrary) and not t.pic:
1248                msg = f"Can't link non-PIC static library {t.name!r} into shared library {self.name!r}. "
1249                msg += "Use the 'pic' option to static_library to build with PIC."
1250                raise InvalidArguments(msg)
1251            if self.for_machine is not t.for_machine:
1252                msg = f'Tried to mix libraries for machines {self.for_machine} and {t.for_machine} in target {self.name!r}'
1253                if self.environment.is_cross_build():
1254                    raise InvalidArguments(msg + ' This is not possible in a cross build.')
1255                else:
1256                    mlog.warning(msg + ' This will fail in cross build.')
1257            self.link_targets.append(t)
1258
1259    def link_whole(self, target):
1260        for t in listify(target):
1261            if isinstance(t, (CustomTarget, CustomTargetIndex)):
1262                if not t.is_linkable_target():
1263                    raise InvalidArguments(f'Custom target {t!r} is not linkable.')
1264                if not t.get_filename().endswith('.a'):
1265                    raise InvalidArguments('Can only link_whole custom targets that are .a archives.')
1266                if isinstance(self, StaticLibrary):
1267                    # FIXME: We could extract the .a archive to get object files
1268                    raise InvalidArguments('Cannot link_whole a custom target into a static library')
1269            elif not isinstance(t, StaticLibrary):
1270                raise InvalidArguments(f'{t!r} is not a static library.')
1271            elif isinstance(self, SharedLibrary) and not t.pic:
1272                msg = f"Can't link non-PIC static library {t.name!r} into shared library {self.name!r}. "
1273                msg += "Use the 'pic' option to static_library to build with PIC."
1274                raise InvalidArguments(msg)
1275            if self.for_machine is not t.for_machine:
1276                msg = f'Tried to mix libraries for machines {self.for_machine} and {t.for_machine} in target {self.name!r}'
1277                if self.environment.is_cross_build():
1278                    raise InvalidArguments(msg + ' This is not possible in a cross build.')
1279                else:
1280                    mlog.warning(msg + ' This will fail in cross build.')
1281            if isinstance(self, StaticLibrary):
1282                # When we're a static library and we link_whole: to another static
1283                # library, we need to add that target's objects to ourselves.
1284                self.objects += t.extract_all_objects_recurse()
1285            self.link_whole_targets.append(t)
1286
1287    def extract_all_objects_recurse(self):
1288        objs = [self.extract_all_objects()]
1289        for t in self.link_targets:
1290            if t.is_internal():
1291                objs += t.extract_all_objects_recurse()
1292        return objs
1293
1294    def add_pch(self, language, pchlist):
1295        if not pchlist:
1296            return
1297        elif len(pchlist) == 1:
1298            if not environment.is_header(pchlist[0]):
1299                raise InvalidArguments(f'PCH argument {pchlist[0]} is not a header.')
1300        elif len(pchlist) == 2:
1301            if environment.is_header(pchlist[0]):
1302                if not environment.is_source(pchlist[1]):
1303                    raise InvalidArguments('PCH definition must contain one header and at most one source.')
1304            elif environment.is_source(pchlist[0]):
1305                if not environment.is_header(pchlist[1]):
1306                    raise InvalidArguments('PCH definition must contain one header and at most one source.')
1307                pchlist = [pchlist[1], pchlist[0]]
1308            else:
1309                raise InvalidArguments(f'PCH argument {pchlist[0]} is of unknown type.')
1310
1311            if (os.path.dirname(pchlist[0]) != os.path.dirname(pchlist[1])):
1312                raise InvalidArguments('PCH files must be stored in the same folder.')
1313
1314            mlog.warning('PCH source files are deprecated, only a single header file should be used.')
1315        elif len(pchlist) > 2:
1316            raise InvalidArguments('PCH definition may have a maximum of 2 files.')
1317        for f in pchlist:
1318            if not isinstance(f, str):
1319                raise MesonException('PCH arguments must be strings.')
1320            if not os.path.isfile(os.path.join(self.environment.source_dir, self.subdir, f)):
1321                raise MesonException(f'File {f} does not exist.')
1322        self.pch[language] = pchlist
1323
1324    def add_include_dirs(self, args, set_is_system: T.Optional[str] = None):
1325        ids = []
1326        for a in args:
1327            if not isinstance(a, IncludeDirs):
1328                raise InvalidArguments('Include directory to be added is not an include directory object.')
1329            ids.append(a)
1330        if set_is_system is None:
1331            set_is_system = 'preserve'
1332        if set_is_system != 'preserve':
1333            is_system = set_is_system == 'system'
1334            ids = [IncludeDirs(x.get_curdir(), x.get_incdirs(), is_system, x.get_extra_build_dirs()) for x in ids]
1335        self.include_dirs += ids
1336
1337    def add_compiler_args(self, language: str, args: T.List['FileOrString']) -> None:
1338        args = listify(args)
1339        for a in args:
1340            if not isinstance(a, (str, File)):
1341                raise InvalidArguments('A non-string passed to compiler args.')
1342        if language in self.extra_args:
1343            self.extra_args[language] += args
1344        else:
1345            self.extra_args[language] = args
1346
1347    def get_aliases(self) -> T.Dict[str, str]:
1348        return {}
1349
1350    def get_langs_used_by_deps(self) -> T.List[str]:
1351        '''
1352        Sometimes you want to link to a C++ library that exports C API, which
1353        means the linker must link in the C++ stdlib, and we must use a C++
1354        compiler for linking. The same is also applicable for objc/objc++, etc,
1355        so we can keep using clink_langs for the priority order.
1356
1357        See: https://github.com/mesonbuild/meson/issues/1653
1358        '''
1359        langs = [] # type: T.List[str]
1360
1361        # Check if any of the external libraries were written in this language
1362        for dep in self.external_deps:
1363            if dep.language is None:
1364                continue
1365            if dep.language not in langs:
1366                langs.append(dep.language)
1367        # Check if any of the internal libraries this target links to were
1368        # written in this language
1369        for link_target in itertools.chain(self.link_targets, self.link_whole_targets):
1370            if isinstance(link_target, (CustomTarget, CustomTargetIndex)):
1371                continue
1372            for language in link_target.compilers:
1373                if language not in langs:
1374                    langs.append(language)
1375
1376        return langs
1377
1378    def get_prelinker(self):
1379        all_compilers = self.environment.coredata.compilers[self.for_machine]
1380        if self.link_language:
1381            comp = all_compilers[self.link_language]
1382            return comp
1383        for l in clink_langs:
1384            if l in self.compilers:
1385                try:
1386                    prelinker = all_compilers[l]
1387                except KeyError:
1388                    raise MesonException(
1389                        f'Could not get a prelinker linker for build target {self.name!r}. '
1390                        f'Requires a compiler for language "{l}", but that is not '
1391                        'a project language.')
1392                return prelinker
1393        raise MesonException(f'Could not determine prelinker for {self.name!r}.')
1394
1395    def get_clink_dynamic_linker_and_stdlibs(self):
1396        '''
1397        We use the order of languages in `clink_langs` to determine which
1398        linker to use in case the target has sources compiled with multiple
1399        compilers. All languages other than those in this list have their own
1400        linker.
1401        Note that Vala outputs C code, so Vala sources can use any linker
1402        that can link compiled C. We don't actually need to add an exception
1403        for Vala here because of that.
1404        '''
1405        # Populate list of all compilers, not just those being used to compile
1406        # sources in this target
1407        all_compilers = self.environment.coredata.compilers[self.for_machine]
1408
1409        # If the user set the link_language, just return that.
1410        if self.link_language:
1411            comp = all_compilers[self.link_language]
1412            return comp, comp.language_stdlib_only_link_flags()
1413
1414        # Languages used by dependencies
1415        dep_langs = self.get_langs_used_by_deps()
1416        # Pick a compiler based on the language priority-order
1417        for l in clink_langs:
1418            if l in self.compilers or l in dep_langs:
1419                try:
1420                    linker = all_compilers[l]
1421                except KeyError:
1422                    raise MesonException(
1423                        f'Could not get a dynamic linker for build target {self.name!r}. '
1424                        f'Requires a linker for language "{l}", but that is not '
1425                        'a project language.')
1426                stdlib_args = []
1427                added_languages = set()
1428                for dl in itertools.chain(self.compilers, dep_langs):
1429                    if dl != linker.language:
1430                        stdlib_args += all_compilers[dl].language_stdlib_only_link_flags()
1431                        added_languages.add(dl)
1432                # Type of var 'linker' is Compiler.
1433                # Pretty hard to fix because the return value is passed everywhere
1434                return linker, stdlib_args
1435
1436        raise AssertionError(f'Could not get a dynamic linker for build target {self.name!r}')
1437
1438    def uses_rust(self) -> bool:
1439        """Is this target a rust target."""
1440        if self.sources:
1441            first_file = self.sources[0]
1442            if first_file.fname.endswith('.rs'):
1443                return True
1444        elif self.generated:
1445            if self.generated[0].get_outputs()[0].endswith('.rs'):
1446                return True
1447        return False
1448
1449    def get_using_msvc(self):
1450        '''
1451        Check if the dynamic linker is MSVC. Used by Executable, StaticLibrary,
1452        and SharedLibrary for deciding when to use MSVC-specific file naming
1453        and debug filenames.
1454
1455        If at least some code is built with MSVC and the final library is
1456        linked with MSVC, we can be sure that some debug info will be
1457        generated. We only check the dynamic linker here because the static
1458        linker is guaranteed to be of the same type.
1459
1460        Interesting cases:
1461        1. The Vala compiler outputs C code to be compiled by whatever
1462           C compiler we're using, so all objects will still be created by the
1463           MSVC compiler.
1464        2. If the target contains only objects, process_compilers guesses and
1465           picks the first compiler that smells right.
1466        '''
1467        # Rustc can use msvc style linkers
1468        if self.uses_rust():
1469            compiler = self.environment.coredata.compilers[self.for_machine]['rust']
1470        else:
1471            compiler, _ = self.get_clink_dynamic_linker_and_stdlibs()
1472        # Mixing many languages with MSVC is not supported yet so ignore stdlibs.
1473        return compiler and compiler.get_linker_id() in {'link', 'lld-link', 'xilink', 'optlink'}
1474
1475    def check_module_linking(self):
1476        '''
1477        Warn if shared modules are linked with target: (link_with) #2865
1478        '''
1479        for link_target in self.link_targets:
1480            if isinstance(link_target, SharedModule):
1481                if self.environment.machines[self.for_machine].is_darwin():
1482                    raise MesonException(
1483                        'target links against shared modules. This is not permitted on OSX')
1484                else:
1485                    mlog.warning('target links against shared modules. This '
1486                                 'is not recommended as it is not supported on some '
1487                                 'platforms')
1488                return
1489
1490class Generator(HoldableObject):
1491    def __init__(self, exe: T.Union['Executable', programs.ExternalProgram],
1492                 arguments: T.List[str],
1493                 output: T.List[str],
1494                 *,
1495                 depfile: T.Optional[str] = None,
1496                 capture: bool = False,
1497                 depends: T.Optional[T.List[T.Union[BuildTarget, 'CustomTarget']]] = None,
1498                 name: str = 'Generator'):
1499        self.exe = exe
1500        self.depfile = depfile
1501        self.capture = capture
1502        self.depends: T.List[T.Union[BuildTarget, 'CustomTarget']] = depends or []
1503        self.arglist = arguments
1504        self.outputs = output
1505        self.name = name
1506
1507    def __repr__(self) -> str:
1508        repr_str = "<{0}: {1}>"
1509        return repr_str.format(self.__class__.__name__, self.exe)
1510
1511    def get_exe(self) -> T.Union['Executable', programs.ExternalProgram]:
1512        return self.exe
1513
1514    def get_base_outnames(self, inname: str) -> T.List[str]:
1515        plainname = os.path.basename(inname)
1516        basename = os.path.splitext(plainname)[0]
1517        bases = [x.replace('@BASENAME@', basename).replace('@PLAINNAME@', plainname) for x in self.outputs]
1518        return bases
1519
1520    def get_dep_outname(self, inname: str) -> T.List[str]:
1521        if self.depfile is None:
1522            raise InvalidArguments('Tried to get dep name for rule that does not have dependency file defined.')
1523        plainname = os.path.basename(inname)
1524        basename = os.path.splitext(plainname)[0]
1525        return self.depfile.replace('@BASENAME@', basename).replace('@PLAINNAME@', plainname)
1526
1527    def get_arglist(self, inname: str) -> T.List[str]:
1528        plainname = os.path.basename(inname)
1529        basename = os.path.splitext(plainname)[0]
1530        return [x.replace('@BASENAME@', basename).replace('@PLAINNAME@', plainname) for x in self.arglist]
1531
1532    @staticmethod
1533    def is_parent_path(parent: str, trial: str) -> bool:
1534        relpath = pathlib.PurePath(trial).relative_to(parent)
1535        return relpath.parts[0] != '..' # For subdirs we can only go "down".
1536
1537    def process_files(self, files: T.Iterable[T.Union[str, File, 'CustomTarget', 'CustomTargetIndex', 'GeneratedList']],
1538                      state: T.Union['Interpreter', 'ModuleState'],
1539                      preserve_path_from: T.Optional[str] = None,
1540                      extra_args: T.Optional[T.List[str]] = None) -> 'GeneratedList':
1541        output = GeneratedList(self, state.subdir, preserve_path_from, extra_args=extra_args if extra_args is not None else [])
1542
1543        for e in files:
1544            if isinstance(e, CustomTarget):
1545                output.depends.add(e)
1546            if isinstance(e, CustomTargetIndex):
1547                output.depends.add(e.target)
1548
1549            if isinstance(e, (CustomTarget, CustomTargetIndex, GeneratedList)):
1550                self.depends.append(e) # BUG: this should go in the GeneratedList object, not this object.
1551                fs = [File.from_built_file(state.subdir, f) for f in e.get_outputs()]
1552            elif isinstance(e, str):
1553                fs = [File.from_source_file(state.environment.source_dir, state.subdir, e)]
1554            else:
1555                fs = [e]
1556
1557            for f in fs:
1558                if preserve_path_from:
1559                    abs_f = f.absolute_path(state.environment.source_dir, state.environment.build_dir)
1560                    if not self.is_parent_path(preserve_path_from, abs_f):
1561                        raise InvalidArguments('generator.process: When using preserve_path_from, all input files must be in a subdirectory of the given dir.')
1562                output.add_file(f, state)
1563        return output
1564
1565
1566class GeneratedList(HoldableObject):
1567
1568    """The output of generator.process."""
1569
1570    def __init__(self, generator: Generator, subdir: str,
1571                 preserve_path_from: T.Optional[str],
1572                 extra_args: T.List[str]):
1573        self.generator = generator
1574        self.name = generator.exe
1575        self.depends: T.Set['CustomTarget'] = set() # Things this target depends on (because e.g. a custom target was used as input)
1576        self.subdir = subdir
1577        self.infilelist: T.List['File'] = []
1578        self.outfilelist: T.List[str] = []
1579        self.outmap: T.Dict[File, T.List[str]] = {}
1580        self.extra_depends = []  # XXX: Doesn't seem to be used?
1581        self.depend_files: T.List[File] = []
1582        self.preserve_path_from = preserve_path_from
1583        self.extra_args: T.List[str] = extra_args if extra_args is not None else []
1584
1585        if isinstance(self.generator.exe, programs.ExternalProgram):
1586            if not self.generator.exe.found():
1587                raise InvalidArguments('Tried to use not-found external program as generator')
1588            path = self.generator.exe.get_path()
1589            if os.path.isabs(path):
1590                # Can only add a dependency on an external program which we
1591                # know the absolute path of
1592                self.depend_files.append(File.from_absolute_file(path))
1593
1594    def add_preserved_path_segment(self, infile: File, outfiles: T.List[str], state: T.Union['Interpreter', 'ModuleState']) -> T.List[str]:
1595        result: T.List[str] = []
1596        in_abs = infile.absolute_path(state.environment.source_dir, state.environment.build_dir)
1597        assert os.path.isabs(self.preserve_path_from)
1598        rel = os.path.relpath(in_abs, self.preserve_path_from)
1599        path_segment = os.path.dirname(rel)
1600        for of in outfiles:
1601            result.append(os.path.join(path_segment, of))
1602        return result
1603
1604    def add_file(self, newfile: File, state: T.Union['Interpreter', 'ModuleState']) -> None:
1605        self.infilelist.append(newfile)
1606        outfiles = self.generator.get_base_outnames(newfile.fname)
1607        if self.preserve_path_from:
1608            outfiles = self.add_preserved_path_segment(newfile, outfiles, state)
1609        self.outfilelist += outfiles
1610        self.outmap[newfile] = outfiles
1611
1612    def get_inputs(self) -> T.List['File']:
1613        return self.infilelist
1614
1615    def get_outputs(self) -> T.List[str]:
1616        return self.outfilelist
1617
1618    def get_outputs_for(self, filename: 'File') -> T.List[str]:
1619        return self.outmap[filename]
1620
1621    def get_generator(self) -> 'Generator':
1622        return self.generator
1623
1624    def get_extra_args(self) -> T.List[str]:
1625        return self.extra_args
1626
1627    def get_subdir(self) -> str:
1628        return self.subdir
1629
1630
1631class Executable(BuildTarget):
1632    known_kwargs = known_exe_kwargs
1633
1634    def __init__(self, name: str, subdir: str, subproject: str, for_machine: MachineChoice,
1635                 sources: T.List[File], objects, environment: environment.Environment, kwargs):
1636        self.typename = 'executable'
1637        key = OptionKey('b_pie')
1638        if 'pie' not in kwargs and key in environment.coredata.options:
1639            kwargs['pie'] = environment.coredata.options[key].value
1640        super().__init__(name, subdir, subproject, for_machine, sources, objects, environment, kwargs)
1641        # Unless overridden, executables have no suffix or prefix. Except on
1642        # Windows and with C#/Mono executables where the suffix is 'exe'
1643        if not hasattr(self, 'prefix'):
1644            self.prefix = ''
1645        if not hasattr(self, 'suffix'):
1646            machine = environment.machines[for_machine]
1647            # Executable for Windows or C#/Mono
1648            if machine.is_windows() or machine.is_cygwin() or 'cs' in self.compilers:
1649                self.suffix = 'exe'
1650            elif machine.system.startswith('wasm') or machine.system == 'emscripten':
1651                self.suffix = 'js'
1652            elif ('c' in self.compilers and self.compilers['c'].get_id().startswith('arm') or
1653                  'cpp' in self.compilers and self.compilers['cpp'].get_id().startswith('arm')):
1654                self.suffix = 'axf'
1655            elif ('c' in self.compilers and self.compilers['c'].get_id().startswith('ccrx') or
1656                  'cpp' in self.compilers and self.compilers['cpp'].get_id().startswith('ccrx')):
1657                self.suffix = 'abs'
1658            elif ('c' in self.compilers and self.compilers['c'].get_id().startswith('xc16')):
1659                self.suffix = 'elf'
1660            elif ('c' in self.compilers and self.compilers['c'].get_id().startswith('c2000') or
1661                  'cpp' in self.compilers and self.compilers['cpp'].get_id().startswith('c2000')):
1662                self.suffix = 'out'
1663            else:
1664                self.suffix = environment.machines[for_machine].get_exe_suffix()
1665        self.filename = self.name
1666        if self.suffix:
1667            self.filename += '.' + self.suffix
1668        self.outputs = [self.filename]
1669
1670        # The import library this target will generate
1671        self.import_filename = None
1672        # The import library that Visual Studio would generate (and accept)
1673        self.vs_import_filename = None
1674        # The import library that GCC would generate (and prefer)
1675        self.gcc_import_filename = None
1676        # The debugging information file this target will generate
1677        self.debug_filename = None
1678
1679        # Check for export_dynamic
1680        self.export_dynamic = False
1681        if kwargs.get('export_dynamic'):
1682            if not isinstance(kwargs['export_dynamic'], bool):
1683                raise InvalidArguments('"export_dynamic" keyword argument must be a boolean')
1684            self.export_dynamic = True
1685        if kwargs.get('implib'):
1686            self.export_dynamic = True
1687        if self.export_dynamic and kwargs.get('implib') is False:
1688            raise InvalidArguments('"implib" keyword argument must not be false for if "export_dynamic" is true')
1689
1690        m = environment.machines[for_machine]
1691
1692        # If using export_dynamic, set the import library name
1693        if self.export_dynamic:
1694            implib_basename = self.name + '.exe'
1695            if not isinstance(kwargs.get('implib', False), bool):
1696                implib_basename = kwargs['implib']
1697            if m.is_windows() or m.is_cygwin():
1698                self.vs_import_filename = f'{implib_basename}.lib'
1699                self.gcc_import_filename = f'lib{implib_basename}.a'
1700                if self.get_using_msvc():
1701                    self.import_filename = self.vs_import_filename
1702                else:
1703                    self.import_filename = self.gcc_import_filename
1704
1705        if m.is_windows() and ('cs' in self.compilers or
1706                               self.uses_rust() or
1707                               self.get_using_msvc()):
1708            self.debug_filename = self.name + '.pdb'
1709
1710        # Only linkwithable if using export_dynamic
1711        self.is_linkwithable = self.export_dynamic
1712
1713        # Remember that this exe was returned by `find_program()` through an override
1714        self.was_returned_by_find_program = False
1715
1716    def get_default_install_dir(self, environment: environment.Environment) -> str:
1717        return environment.get_bindir()
1718
1719    def description(self):
1720        '''Human friendly description of the executable'''
1721        return self.name
1722
1723    def type_suffix(self):
1724        return "@exe"
1725
1726    def get_import_filename(self):
1727        """
1728        The name of the import library that will be outputted by the compiler
1729
1730        Returns None if there is no import library required for this platform
1731        """
1732        return self.import_filename
1733
1734    def get_import_filenameslist(self):
1735        if self.import_filename:
1736            return [self.vs_import_filename, self.gcc_import_filename]
1737        return []
1738
1739    def get_debug_filename(self):
1740        """
1741        The name of debuginfo file that will be created by the compiler
1742
1743        Returns None if the build won't create any debuginfo file
1744        """
1745        return self.debug_filename
1746
1747    def is_linkable_target(self):
1748        return self.is_linkwithable
1749
1750class StaticLibrary(BuildTarget):
1751    known_kwargs = known_stlib_kwargs
1752
1753    def __init__(self, name, subdir, subproject, for_machine: MachineChoice, sources, objects, environment, kwargs):
1754        self.typename = 'static library'
1755        super().__init__(name, subdir, subproject, for_machine, sources, objects, environment, kwargs)
1756        if 'cs' in self.compilers:
1757            raise InvalidArguments('Static libraries not supported for C#.')
1758        if 'rust' in self.compilers:
1759            # If no crate type is specified, or it's the generic lib type, use rlib
1760            if not hasattr(self, 'rust_crate_type') or self.rust_crate_type == 'lib':
1761                mlog.debug('Defaulting Rust static library target crate type to rlib')
1762                self.rust_crate_type = 'rlib'
1763            # Don't let configuration proceed with a non-static crate type
1764            elif self.rust_crate_type not in ['rlib', 'staticlib']:
1765                raise InvalidArguments(f'Crate type "{self.rust_crate_type}" invalid for static libraries; must be "rlib" or "staticlib"')
1766        # By default a static library is named libfoo.a even on Windows because
1767        # MSVC does not have a consistent convention for what static libraries
1768        # are called. The MSVC CRT uses libfoo.lib syntax but nothing else uses
1769        # it and GCC only looks for static libraries called foo.lib and
1770        # libfoo.a. However, we cannot use foo.lib because that's the same as
1771        # the import library. Using libfoo.a is ok because people using MSVC
1772        # always pass the library filename while linking anyway.
1773        if not hasattr(self, 'prefix'):
1774            self.prefix = 'lib'
1775        if not hasattr(self, 'suffix'):
1776            if 'rust' in self.compilers:
1777                if not hasattr(self, 'rust_crate_type') or self.rust_crate_type == 'rlib':
1778                    # default Rust static library suffix
1779                    self.suffix = 'rlib'
1780                elif self.rust_crate_type == 'staticlib':
1781                    self.suffix = 'a'
1782            else:
1783                self.suffix = 'a'
1784        self.filename = self.prefix + self.name + '.' + self.suffix
1785        self.outputs = [self.filename]
1786        self.prelink = kwargs.get('prelink', False)
1787        if not isinstance(self.prelink, bool):
1788            raise InvalidArguments('Prelink keyword argument must be a boolean.')
1789
1790    def get_link_deps_mapping(self, prefix: str, environment: environment.Environment) -> T.Mapping[str, str]:
1791        return {}
1792
1793    def get_default_install_dir(self, environment):
1794        return environment.get_static_lib_dir()
1795
1796    def type_suffix(self):
1797        return "@sta"
1798
1799    def process_kwargs(self, kwargs, environment):
1800        super().process_kwargs(kwargs, environment)
1801        if 'rust_crate_type' in kwargs:
1802            rust_crate_type = kwargs['rust_crate_type']
1803            if isinstance(rust_crate_type, str):
1804                self.rust_crate_type = rust_crate_type
1805            else:
1806                raise InvalidArguments(f'Invalid rust_crate_type "{rust_crate_type}": must be a string.')
1807
1808    def is_linkable_target(self):
1809        return True
1810
1811class SharedLibrary(BuildTarget):
1812    known_kwargs = known_shlib_kwargs
1813
1814    def __init__(self, name, subdir, subproject, for_machine: MachineChoice, sources, objects, environment, kwargs):
1815        self.typename = 'shared library'
1816        self.soversion = None
1817        self.ltversion = None
1818        # Max length 2, first element is compatibility_version, second is current_version
1819        self.darwin_versions = []
1820        self.vs_module_defs = None
1821        # The import library this target will generate
1822        self.import_filename = None
1823        # The import library that Visual Studio would generate (and accept)
1824        self.vs_import_filename = None
1825        # The import library that GCC would generate (and prefer)
1826        self.gcc_import_filename = None
1827        # The debugging information file this target will generate
1828        self.debug_filename = None
1829        # Use by the pkgconfig module
1830        self.shared_library_only = False
1831        super().__init__(name, subdir, subproject, for_machine, sources, objects, environment, kwargs)
1832        if 'rust' in self.compilers:
1833            # If no crate type is specified, or it's the generic lib type, use dylib
1834            if not hasattr(self, 'rust_crate_type') or self.rust_crate_type == 'lib':
1835                mlog.debug('Defaulting Rust dynamic library target crate type to "dylib"')
1836                self.rust_crate_type = 'dylib'
1837            # Don't let configuration proceed with a non-dynamic crate type
1838            elif self.rust_crate_type not in ['dylib', 'cdylib']:
1839                raise InvalidArguments(f'Crate type "{self.rust_crate_type}" invalid for dynamic libraries; must be "dylib" or "cdylib"')
1840        if not hasattr(self, 'prefix'):
1841            self.prefix = None
1842        if not hasattr(self, 'suffix'):
1843            self.suffix = None
1844        self.basic_filename_tpl = '{0.prefix}{0.name}.{0.suffix}'
1845        self.determine_filenames(environment)
1846
1847    def get_link_deps_mapping(self, prefix: str, environment: environment.Environment) -> T.Mapping[str, str]:
1848        result: T.Dict[str, str] = {}
1849        mappings = self.get_transitive_link_deps_mapping(prefix, environment)
1850        old = get_target_macos_dylib_install_name(self)
1851        if old not in mappings:
1852            fname = self.get_filename()
1853            outdirs, _ = self.get_install_dir(self.environment)
1854            new = os.path.join(prefix, outdirs[0], fname)
1855            result.update({old: new})
1856        mappings.update(result)
1857        return mappings
1858
1859    def get_default_install_dir(self, environment):
1860        return environment.get_shared_lib_dir()
1861
1862    def determine_filenames(self, env):
1863        """
1864        See https://github.com/mesonbuild/meson/pull/417 for details.
1865
1866        First we determine the filename template (self.filename_tpl), then we
1867        set the output filename (self.filename).
1868
1869        The template is needed while creating aliases (self.get_aliases),
1870        which are needed while generating .so shared libraries for Linux.
1871
1872        Besides this, there's also the import library name, which is only used
1873        on Windows since on that platform the linker uses a separate library
1874        called the "import library" during linking instead of the shared
1875        library (DLL). The toolchain will output an import library in one of
1876        two formats: GCC or Visual Studio.
1877
1878        When we're building with Visual Studio, the import library that will be
1879        generated by the toolchain is self.vs_import_filename, and with
1880        MinGW/GCC, it's self.gcc_import_filename. self.import_filename will
1881        always contain the import library name this target will generate.
1882        """
1883        prefix = ''
1884        suffix = ''
1885        create_debug_file = False
1886        self.filename_tpl = self.basic_filename_tpl
1887        # NOTE: manual prefix/suffix override is currently only tested for C/C++
1888        # C# and Mono
1889        if 'cs' in self.compilers:
1890            prefix = ''
1891            suffix = 'dll'
1892            self.filename_tpl = '{0.prefix}{0.name}.{0.suffix}'
1893            create_debug_file = True
1894        # C, C++, Swift, Vala
1895        # Only Windows uses a separate import library for linking
1896        # For all other targets/platforms import_filename stays None
1897        elif env.machines[self.for_machine].is_windows():
1898            suffix = 'dll'
1899            self.vs_import_filename = '{}{}.lib'.format(self.prefix if self.prefix is not None else '', self.name)
1900            self.gcc_import_filename = '{}{}.dll.a'.format(self.prefix if self.prefix is not None else 'lib', self.name)
1901            if self.uses_rust():
1902                # Shared library is of the form foo.dll
1903                prefix = ''
1904                # Import library is called foo.dll.lib
1905                self.import_filename = f'{self.name}.dll.lib'
1906                create_debug_file = True
1907            elif self.get_using_msvc():
1908                # Shared library is of the form foo.dll
1909                prefix = ''
1910                # Import library is called foo.lib
1911                self.import_filename = self.vs_import_filename
1912                create_debug_file = True
1913            # Assume GCC-compatible naming
1914            else:
1915                # Shared library is of the form libfoo.dll
1916                prefix = 'lib'
1917                # Import library is called libfoo.dll.a
1918                self.import_filename = self.gcc_import_filename
1919            # Shared library has the soversion if it is defined
1920            if self.soversion:
1921                self.filename_tpl = '{0.prefix}{0.name}-{0.soversion}.{0.suffix}'
1922            else:
1923                self.filename_tpl = '{0.prefix}{0.name}.{0.suffix}'
1924        elif env.machines[self.for_machine].is_cygwin():
1925            suffix = 'dll'
1926            self.gcc_import_filename = '{}{}.dll.a'.format(self.prefix if self.prefix is not None else 'lib', self.name)
1927            # Shared library is of the form cygfoo.dll
1928            # (ld --dll-search-prefix=cyg is the default)
1929            prefix = 'cyg'
1930            # Import library is called libfoo.dll.a
1931            self.import_filename = self.gcc_import_filename
1932            if self.soversion:
1933                self.filename_tpl = '{0.prefix}{0.name}-{0.soversion}.{0.suffix}'
1934            else:
1935                self.filename_tpl = '{0.prefix}{0.name}.{0.suffix}'
1936        elif env.machines[self.for_machine].is_darwin():
1937            prefix = 'lib'
1938            suffix = 'dylib'
1939            # On macOS, the filename can only contain the major version
1940            if self.soversion:
1941                # libfoo.X.dylib
1942                self.filename_tpl = '{0.prefix}{0.name}.{0.soversion}.{0.suffix}'
1943            else:
1944                # libfoo.dylib
1945                self.filename_tpl = '{0.prefix}{0.name}.{0.suffix}'
1946        elif env.machines[self.for_machine].is_android():
1947            prefix = 'lib'
1948            suffix = 'so'
1949            # Android doesn't support shared_library versioning
1950            self.filename_tpl = '{0.prefix}{0.name}.{0.suffix}'
1951        else:
1952            prefix = 'lib'
1953            suffix = 'so'
1954            if self.ltversion:
1955                # libfoo.so.X[.Y[.Z]] (.Y and .Z are optional)
1956                self.filename_tpl = '{0.prefix}{0.name}.{0.suffix}.{0.ltversion}'
1957            elif self.soversion:
1958                # libfoo.so.X
1959                self.filename_tpl = '{0.prefix}{0.name}.{0.suffix}.{0.soversion}'
1960            else:
1961                # No versioning, libfoo.so
1962                self.filename_tpl = '{0.prefix}{0.name}.{0.suffix}'
1963        if self.prefix is None:
1964            self.prefix = prefix
1965        if self.suffix is None:
1966            self.suffix = suffix
1967        self.filename = self.filename_tpl.format(self)
1968        self.outputs = [self.filename]
1969        if create_debug_file:
1970            self.debug_filename = os.path.splitext(self.filename)[0] + '.pdb'
1971
1972    @staticmethod
1973    def _validate_darwin_versions(darwin_versions):
1974        try:
1975            if isinstance(darwin_versions, int):
1976                darwin_versions = str(darwin_versions)
1977            if isinstance(darwin_versions, str):
1978                darwin_versions = 2 * [darwin_versions]
1979            if not isinstance(darwin_versions, list):
1980                raise InvalidArguments('Shared library darwin_versions: must be a string, integer,'
1981                                       f'or a list, not {darwin_versions!r}')
1982            if len(darwin_versions) > 2:
1983                raise InvalidArguments('Shared library darwin_versions: list must contain 2 or fewer elements')
1984            if len(darwin_versions) == 1:
1985                darwin_versions = 2 * darwin_versions
1986            for i, v in enumerate(darwin_versions[:]):
1987                if isinstance(v, int):
1988                    v = str(v)
1989                if not isinstance(v, str):
1990                    raise InvalidArguments('Shared library darwin_versions: list elements '
1991                                           f'must be strings or integers, not {v!r}')
1992                if not re.fullmatch(r'[0-9]+(\.[0-9]+){0,2}', v):
1993                    raise InvalidArguments('Shared library darwin_versions: must be X.Y.Z where '
1994                                           'X, Y, Z are numbers, and Y and Z are optional')
1995                parts = v.split('.')
1996                if len(parts) in (1, 2, 3) and int(parts[0]) > 65535:
1997                    raise InvalidArguments('Shared library darwin_versions: must be X.Y.Z '
1998                                           'where X is [0, 65535] and Y, Z are optional')
1999                if len(parts) in (2, 3) and int(parts[1]) > 255:
2000                    raise InvalidArguments('Shared library darwin_versions: must be X.Y.Z '
2001                                           'where Y is [0, 255] and Y, Z are optional')
2002                if len(parts) == 3 and int(parts[2]) > 255:
2003                    raise InvalidArguments('Shared library darwin_versions: must be X.Y.Z '
2004                                           'where Z is [0, 255] and Y, Z are optional')
2005                darwin_versions[i] = v
2006        except ValueError:
2007            raise InvalidArguments('Shared library darwin_versions: value is invalid')
2008        return darwin_versions
2009
2010    def process_kwargs(self, kwargs, environment):
2011        super().process_kwargs(kwargs, environment)
2012
2013        if not self.environment.machines[self.for_machine].is_android():
2014            supports_versioning = True
2015        else:
2016            supports_versioning = False
2017
2018        if supports_versioning:
2019            # Shared library version
2020            if 'version' in kwargs:
2021                self.ltversion = kwargs['version']
2022                if not isinstance(self.ltversion, str):
2023                    raise InvalidArguments('Shared library version needs to be a string, not ' + type(self.ltversion).__name__)
2024                if not re.fullmatch(r'[0-9]+(\.[0-9]+){0,2}', self.ltversion):
2025                    raise InvalidArguments(f'Invalid Shared library version "{self.ltversion}". Must be of the form X.Y.Z where all three are numbers. Y and Z are optional.')
2026            # Try to extract/deduce the soversion
2027            if 'soversion' in kwargs:
2028                self.soversion = kwargs['soversion']
2029                if isinstance(self.soversion, int):
2030                    self.soversion = str(self.soversion)
2031                if not isinstance(self.soversion, str):
2032                    raise InvalidArguments('Shared library soversion is not a string or integer.')
2033            elif self.ltversion:
2034                # library version is defined, get the soversion from that
2035                # We replicate what Autotools does here and take the first
2036                # number of the version by default.
2037                self.soversion = self.ltversion.split('.')[0]
2038            # macOS, iOS and tvOS dylib compatibility_version and current_version
2039            if 'darwin_versions' in kwargs:
2040                self.darwin_versions = self._validate_darwin_versions(kwargs['darwin_versions'])
2041            elif self.soversion:
2042                # If unspecified, pick the soversion
2043                self.darwin_versions = 2 * [self.soversion]
2044
2045        # Visual Studio module-definitions file
2046        if 'vs_module_defs' in kwargs:
2047            path = kwargs['vs_module_defs']
2048            if isinstance(path, str):
2049                if os.path.isabs(path):
2050                    self.vs_module_defs = File.from_absolute_file(path)
2051                else:
2052                    self.vs_module_defs = File.from_source_file(environment.source_dir, self.subdir, path)
2053                self.link_depends.append(self.vs_module_defs)
2054            elif isinstance(path, File):
2055                # When passing a generated file.
2056                self.vs_module_defs = path
2057                self.link_depends.append(path)
2058            elif hasattr(path, 'get_filename'):
2059                # When passing output of a Custom Target
2060                path = File.from_built_file(path.subdir, path.get_filename())
2061                self.vs_module_defs = path
2062                self.link_depends.append(path)
2063            else:
2064                raise InvalidArguments(
2065                    'Shared library vs_module_defs must be either a string, '
2066                    'a file object or a Custom Target')
2067        if 'rust_crate_type' in kwargs:
2068            rust_crate_type = kwargs['rust_crate_type']
2069            if isinstance(rust_crate_type, str):
2070                self.rust_crate_type = rust_crate_type
2071            else:
2072                raise InvalidArguments(f'Invalid rust_crate_type "{rust_crate_type}": must be a string.')
2073
2074    def get_import_filename(self):
2075        """
2076        The name of the import library that will be outputted by the compiler
2077
2078        Returns None if there is no import library required for this platform
2079        """
2080        return self.import_filename
2081
2082    def get_debug_filename(self):
2083        """
2084        The name of debuginfo file that will be created by the compiler
2085
2086        Returns None if the build won't create any debuginfo file
2087        """
2088        return self.debug_filename
2089
2090    def get_import_filenameslist(self):
2091        if self.import_filename:
2092            return [self.vs_import_filename, self.gcc_import_filename]
2093        return []
2094
2095    def get_all_link_deps(self):
2096        return [self] + self.get_transitive_link_deps()
2097
2098    def get_aliases(self) -> T.Dict[str, str]:
2099        """
2100        If the versioned library name is libfoo.so.0.100.0, aliases are:
2101        * libfoo.so.0 (soversion) -> libfoo.so.0.100.0
2102        * libfoo.so (unversioned; for linking) -> libfoo.so.0
2103        Same for dylib:
2104        * libfoo.dylib (unversioned; for linking) -> libfoo.0.dylib
2105        """
2106        aliases: T.Dict[str, str] = {}
2107        # Aliases are only useful with .so and .dylib libraries. Also if
2108        # there's no self.soversion (no versioning), we don't need aliases.
2109        if self.suffix not in ('so', 'dylib') or not self.soversion:
2110            return aliases
2111        # With .so libraries, the minor and micro versions are also in the
2112        # filename. If ltversion != soversion we create an soversion alias:
2113        # libfoo.so.0 -> libfoo.so.0.100.0
2114        # Where libfoo.so.0.100.0 is the actual library
2115        if self.suffix == 'so' and self.ltversion and self.ltversion != self.soversion:
2116            alias_tpl = self.filename_tpl.replace('ltversion', 'soversion')
2117            ltversion_filename = alias_tpl.format(self)
2118            aliases[ltversion_filename] = self.filename
2119        # libfoo.so.0/libfoo.0.dylib is the actual library
2120        else:
2121            ltversion_filename = self.filename
2122        # Unversioned alias:
2123        #  libfoo.so -> libfoo.so.0
2124        #  libfoo.dylib -> libfoo.0.dylib
2125        aliases[self.basic_filename_tpl.format(self)] = ltversion_filename
2126        return aliases
2127
2128    def type_suffix(self):
2129        return "@sha"
2130
2131    def is_linkable_target(self):
2132        return True
2133
2134# A shared library that is meant to be used with dlopen rather than linking
2135# into something else.
2136class SharedModule(SharedLibrary):
2137    known_kwargs = known_shmod_kwargs
2138
2139    def __init__(self, name, subdir, subproject, for_machine: MachineChoice, sources, objects, environment, kwargs):
2140        if 'version' in kwargs:
2141            raise MesonException('Shared modules must not specify the version kwarg.')
2142        if 'soversion' in kwargs:
2143            raise MesonException('Shared modules must not specify the soversion kwarg.')
2144        super().__init__(name, subdir, subproject, for_machine, sources, objects, environment, kwargs)
2145        self.typename = 'shared module'
2146
2147    def get_default_install_dir(self, environment):
2148        return environment.get_shared_module_dir()
2149
2150class BothLibraries(SecondLevelHolder):
2151    def __init__(self, shared: SharedLibrary, static: StaticLibrary) -> None:
2152        self._preferred_library = 'shared'
2153        self.shared = shared
2154        self.static = static
2155        self.subproject = self.shared.subproject
2156
2157    def __repr__(self) -> str:
2158        return f'<BothLibraries: static={repr(self.static)}; shared={repr(self.shared)}>'
2159
2160    def get_default_object(self) -> BuildTarget:
2161        if self._preferred_library == 'shared':
2162            return self.shared
2163        elif self._preferred_library == 'static':
2164            return self.static
2165        raise MesonBugException(f'self._preferred_library == "{self._preferred_library}" is neither "shared" nor "static".')
2166
2167class CommandBase:
2168    def flatten_command(self, cmd):
2169        cmd = listify(cmd)
2170        final_cmd = []
2171        for c in cmd:
2172            if isinstance(c, str):
2173                final_cmd.append(c)
2174            elif isinstance(c, File):
2175                self.depend_files.append(c)
2176                final_cmd.append(c)
2177            elif isinstance(c, programs.ExternalProgram):
2178                if not c.found():
2179                    raise InvalidArguments('Tried to use not-found external program in "command"')
2180                path = c.get_path()
2181                if os.path.isabs(path):
2182                    # Can only add a dependency on an external program which we
2183                    # know the absolute path of
2184                    self.depend_files.append(File.from_absolute_file(path))
2185                final_cmd += c.get_command()
2186            elif isinstance(c, (BuildTarget, CustomTarget)):
2187                self.dependencies.append(c)
2188                final_cmd.append(c)
2189            elif isinstance(c, list):
2190                final_cmd += self.flatten_command(c)
2191            else:
2192                raise InvalidArguments(f'Argument {c!r} in "command" is invalid')
2193        return final_cmd
2194
2195class CustomTarget(Target, CommandBase):
2196    known_kwargs = {
2197        'input',
2198        'output',
2199        'command',
2200        'capture',
2201        'feed',
2202        'install',
2203        'install_dir',
2204        'install_mode',
2205        'build_always',
2206        'build_always_stale',
2207        'depends',
2208        'depend_files',
2209        'depfile',
2210        'build_by_default',
2211        'override_options',
2212        'console',
2213        'env',
2214    }
2215
2216    def __init__(self, name: str, subdir: str, subproject: str, kwargs: T.Dict[str, T.Any],
2217                 absolute_paths: bool = False, backend: T.Optional['Backend'] = None):
2218        self.typename = 'custom'
2219        # TODO expose keyword arg to make MachineChoice.HOST configurable
2220        super().__init__(name, subdir, subproject, False, MachineChoice.HOST)
2221        self.dependencies: T.List[T.Union[CustomTarget, BuildTarget]] = []
2222        self.extra_depends = []
2223        self.depend_files = [] # Files that this target depends on but are not on the command line.
2224        self.depfile = None
2225        self.process_kwargs(kwargs, backend)
2226        # Whether to use absolute paths for all files on the commandline
2227        self.absolute_paths = absolute_paths
2228        unknowns = []
2229        for k in kwargs:
2230            if k not in CustomTarget.known_kwargs:
2231                unknowns.append(k)
2232        if unknowns:
2233            mlog.warning('Unknown keyword arguments in target {}: {}'.format(self.name, ', '.join(unknowns)))
2234
2235    def get_default_install_dir(self, environment):
2236        return None
2237
2238    def __repr__(self):
2239        repr_str = "<{0} {1}: {2}>"
2240        return repr_str.format(self.__class__.__name__, self.get_id(), self.command)
2241
2242    def get_target_dependencies(self):
2243        deps = self.dependencies[:]
2244        deps += self.extra_depends
2245        for c in self.sources:
2246            if isinstance(c, (BuildTarget, CustomTarget)):
2247                deps.append(c)
2248        return deps
2249
2250    def get_transitive_build_target_deps(self):
2251        '''
2252        Recursively fetch the build targets that this custom target depends on,
2253        whether through `command:`, `depends:`, or `sources:` The recursion is
2254        only performed on custom targets.
2255        This is useful for setting PATH on Windows for finding required DLLs.
2256        F.ex, if you have a python script that loads a C module that links to
2257        other DLLs in your project.
2258        '''
2259        bdeps = set()
2260        deps = self.get_target_dependencies()
2261        for d in deps:
2262            if isinstance(d, BuildTarget):
2263                bdeps.add(d)
2264            elif isinstance(d, CustomTarget):
2265                bdeps.update(d.get_transitive_build_target_deps())
2266        return bdeps
2267
2268    def process_kwargs(self, kwargs, backend):
2269        self.process_kwargs_base(kwargs)
2270        self.sources = extract_as_list(kwargs, 'input')
2271        if 'output' not in kwargs:
2272            raise InvalidArguments('Missing keyword argument "output".')
2273        self.outputs = listify(kwargs['output'])
2274        # This will substitute values from the input into output and return it.
2275        inputs = get_sources_string_names(self.sources, backend)
2276        values = get_filenames_templates_dict(inputs, [])
2277        for i in self.outputs:
2278            if not(isinstance(i, str)):
2279                raise InvalidArguments('Output argument not a string.')
2280            if i == '':
2281                raise InvalidArguments('Output must not be empty.')
2282            if i.strip() == '':
2283                raise InvalidArguments('Output must not consist only of whitespace.')
2284            if has_path_sep(i):
2285                raise InvalidArguments(f'Output {i!r} must not contain a path segment.')
2286            if '@INPUT@' in i or '@INPUT0@' in i:
2287                m = 'Output cannot contain @INPUT@ or @INPUT0@, did you ' \
2288                    'mean @PLAINNAME@ or @BASENAME@?'
2289                raise InvalidArguments(m)
2290            # We already check this during substitution, but the error message
2291            # will be unclear/confusing, so check it here.
2292            if len(inputs) != 1 and ('@PLAINNAME@' in i or '@BASENAME@' in i):
2293                m = "Output cannot contain @PLAINNAME@ or @BASENAME@ when " \
2294                    "there is more than one input (we can't know which to use)"
2295                raise InvalidArguments(m)
2296        self.outputs = substitute_values(self.outputs, values)
2297        self.capture = kwargs.get('capture', False)
2298        if self.capture and len(self.outputs) != 1:
2299            raise InvalidArguments('Capturing can only output to a single file.')
2300        self.feed = kwargs.get('feed', False)
2301        if self.feed and len(self.sources) != 1:
2302            raise InvalidArguments('Feeding can only input from a single file.')
2303        self.console = kwargs.get('console', False)
2304        if not isinstance(self.console, bool):
2305            raise InvalidArguments('"console" kwarg only accepts booleans')
2306        if self.capture and self.console:
2307            raise InvalidArguments("Can't both capture output and output to console")
2308        if 'command' not in kwargs:
2309            raise InvalidArguments('Missing keyword argument "command".')
2310        if 'depfile' in kwargs:
2311            depfile = kwargs['depfile']
2312            if not isinstance(depfile, str):
2313                raise InvalidArguments('Depfile must be a string.')
2314            if os.path.basename(depfile) != depfile:
2315                raise InvalidArguments('Depfile must be a plain filename without a subdirectory.')
2316            self.depfile = depfile
2317        self.command = self.flatten_command(kwargs['command'])
2318        for c in self.command:
2319            if self.capture and isinstance(c, str) and '@OUTPUT@' in c:
2320                raise InvalidArguments('@OUTPUT@ is not allowed when capturing output.')
2321            if self.feed and isinstance(c, str) and '@INPUT@' in c:
2322                raise InvalidArguments('@INPUT@ is not allowed when feeding input.')
2323        if 'install' in kwargs:
2324            self.install = kwargs['install']
2325            if not isinstance(self.install, bool):
2326                raise InvalidArguments('"install" must be boolean.')
2327            if self.install:
2328                if 'install_dir' not in kwargs:
2329                    raise InvalidArguments('"install_dir" must be specified '
2330                                           'when installing a target')
2331
2332                if isinstance(kwargs['install_dir'], list):
2333                    FeatureNew.single_use('multiple install_dir for custom_target', '0.40.0', self.subproject)
2334                # If an item in this list is False, the output corresponding to
2335                # the list index of that item will not be installed
2336                self.install_dir = typeslistify(kwargs['install_dir'], (str, bool))
2337                self.install_mode = kwargs.get('install_mode', None)
2338        else:
2339            self.install = False
2340            self.install_dir = [None]
2341            self.install_mode = None
2342        if 'build_always' in kwargs and 'build_always_stale' in kwargs:
2343            raise InvalidArguments('build_always and build_always_stale are mutually exclusive. Combine build_by_default and build_always_stale.')
2344        elif 'build_always' in kwargs:
2345            if 'build_by_default' not in kwargs:
2346                self.build_by_default = kwargs['build_always']
2347            self.build_always_stale = kwargs['build_always']
2348        elif 'build_always_stale' in kwargs:
2349            self.build_always_stale = kwargs['build_always_stale']
2350        if not isinstance(self.build_always_stale, bool):
2351            raise InvalidArguments('Argument build_always_stale must be a boolean.')
2352        extra_deps, depend_files = [extract_as_list(kwargs, c, pop=False) for c in ['depends', 'depend_files']]
2353        for ed in extra_deps:
2354            if not isinstance(ed, (CustomTarget, BuildTarget)):
2355                raise InvalidArguments('Can only depend on toplevel targets: custom_target or build_target '
2356                                       f'(executable or a library) got: {type(ed)}({ed})')
2357            self.extra_depends.append(ed)
2358        for i in depend_files:
2359            if isinstance(i, (File, str)):
2360                self.depend_files.append(i)
2361            else:
2362                mlog.debug(i)
2363                raise InvalidArguments(f'Unknown type {type(i).__name__!r} in depend_files.')
2364        self.env = kwargs.get('env')
2365
2366    def get_dependencies(self):
2367        return self.dependencies
2368
2369    def should_install(self) -> bool:
2370        return self.install
2371
2372    def get_custom_install_dir(self):
2373        return self.install_dir
2374
2375    def get_custom_install_mode(self):
2376        return self.install_mode
2377
2378    def get_outputs(self) -> T.List[str]:
2379        return self.outputs
2380
2381    def get_filename(self):
2382        return self.outputs[0]
2383
2384    def get_sources(self):
2385        return self.sources
2386
2387    def get_generated_lists(self):
2388        genlists = []
2389        for c in self.sources:
2390            if isinstance(c, GeneratedList):
2391                genlists.append(c)
2392        return genlists
2393
2394    def get_generated_sources(self):
2395        return self.get_generated_lists()
2396
2397    def get_dep_outname(self, infilenames):
2398        if self.depfile is None:
2399            raise InvalidArguments('Tried to get depfile name for custom_target that does not have depfile defined.')
2400        if infilenames:
2401            plainname = os.path.basename(infilenames[0])
2402            basename = os.path.splitext(plainname)[0]
2403            return self.depfile.replace('@BASENAME@', basename).replace('@PLAINNAME@', plainname)
2404        else:
2405            if '@BASENAME@' in self.depfile or '@PLAINNAME@' in self.depfile:
2406                raise InvalidArguments('Substitution in depfile for custom_target that does not have an input file.')
2407            return self.depfile
2408
2409    def is_linkable_target(self):
2410        if len(self.outputs) != 1:
2411            return False
2412        suf = os.path.splitext(self.outputs[0])[-1]
2413        if suf == '.a' or suf == '.dll' or suf == '.lib' or suf == '.so' or suf == '.dylib':
2414            return True
2415
2416    def get_link_deps_mapping(self, prefix: str, environment: environment.Environment) -> T.Mapping[str, str]:
2417        return {}
2418
2419    def get_link_dep_subdirs(self):
2420        return OrderedSet()
2421
2422    def get_all_link_deps(self):
2423        return []
2424
2425    def is_internal(self) -> bool:
2426        if not self.should_install():
2427            return True
2428        for out in self.get_outputs():
2429            # Can't check if this is a static library, so try to guess
2430            if not out.endswith(('.a', '.lib')):
2431                return False
2432        return True
2433
2434    def extract_all_objects_recurse(self):
2435        return self.get_outputs()
2436
2437    def type_suffix(self):
2438        return "@cus"
2439
2440    def __getitem__(self, index: int) -> 'CustomTargetIndex':
2441        return CustomTargetIndex(self, self.outputs[index])
2442
2443    def __setitem__(self, index, value):
2444        raise NotImplementedError
2445
2446    def __delitem__(self, index):
2447        raise NotImplementedError
2448
2449    def __iter__(self):
2450        for i in self.outputs:
2451            yield CustomTargetIndex(self, i)
2452
2453class RunTarget(Target, CommandBase):
2454    def __init__(self, name, command, dependencies, subdir, subproject, env=None):
2455        self.typename = 'run'
2456        # These don't produce output artifacts
2457        super().__init__(name, subdir, subproject, False, MachineChoice.BUILD)
2458        self.dependencies = dependencies
2459        self.depend_files = []
2460        self.command = self.flatten_command(command)
2461        self.absolute_paths = False
2462        self.env = env
2463
2464    def __repr__(self):
2465        repr_str = "<{0} {1}: {2}>"
2466        return repr_str.format(self.__class__.__name__, self.get_id(), self.command[0])
2467
2468    def process_kwargs(self, kwargs):
2469        return self.process_kwargs_base(kwargs)
2470
2471    def get_dependencies(self):
2472        return self.dependencies
2473
2474    def get_generated_sources(self):
2475        return []
2476
2477    def get_sources(self):
2478        return []
2479
2480    def should_install(self) -> bool:
2481        return False
2482
2483    def get_filename(self) -> str:
2484        return self.name
2485
2486    def get_outputs(self) -> T.List[str]:
2487        if isinstance(self.name, str):
2488            return [self.name]
2489        elif isinstance(self.name, list):
2490            return self.name
2491        else:
2492            raise RuntimeError('RunTarget: self.name is neither a list nor a string. This is a bug')
2493
2494    def type_suffix(self):
2495        return "@run"
2496
2497class AliasTarget(RunTarget):
2498    def __init__(self, name, dependencies, subdir, subproject):
2499        super().__init__(name, [], dependencies, subdir, subproject)
2500
2501class Jar(BuildTarget):
2502    known_kwargs = known_jar_kwargs
2503
2504    def __init__(self, name, subdir, subproject, for_machine: MachineChoice, sources, objects, environment, kwargs):
2505        self.typename = 'jar'
2506        super().__init__(name, subdir, subproject, for_machine, sources, objects, environment, kwargs)
2507        for s in self.sources:
2508            if not s.endswith('.java'):
2509                raise InvalidArguments(f'Jar source {s} is not a java file.')
2510        for t in self.link_targets:
2511            if not isinstance(t, Jar):
2512                raise InvalidArguments(f'Link target {t} is not a jar target.')
2513        self.filename = self.name + '.jar'
2514        self.outputs = [self.filename]
2515        self.java_args = kwargs.get('java_args', [])
2516
2517    def get_main_class(self):
2518        return self.main_class
2519
2520    def type_suffix(self):
2521        return "@jar"
2522
2523    def get_java_args(self):
2524        return self.java_args
2525
2526    def validate_install(self, environment):
2527        # All jar targets are installable.
2528        pass
2529
2530    def is_linkable_target(self):
2531        return True
2532
2533    def get_classpath_args(self):
2534        cp_paths = [os.path.join(l.get_subdir(), l.get_filename()) for l in self.link_targets]
2535        cp_string = os.pathsep.join(cp_paths)
2536        if cp_string:
2537            return ['-cp', os.pathsep.join(cp_paths)]
2538        return []
2539
2540class CustomTargetIndex(HoldableObject):
2541
2542    """A special opaque object returned by indexing a CustomTarget. This object
2543    exists in Meson, but acts as a proxy in the backends, making targets depend
2544    on the CustomTarget it's derived from, but only adding one source file to
2545    the sources.
2546    """
2547
2548    def __init__(self, target: CustomTarget, output: int):
2549        self.typename = 'custom'
2550        self.target = target
2551        self.output = output
2552        self.for_machine = target.for_machine
2553
2554    def __repr__(self):
2555        return '<CustomTargetIndex: {!r}[{}]>'.format(
2556            self.target, self.target.get_outputs().index(self.output))
2557
2558    def get_outputs(self) -> T.List[str]:
2559        return [self.output]
2560
2561    def get_subdir(self) -> str:
2562        return self.target.get_subdir()
2563
2564    def get_filename(self):
2565        return self.output
2566
2567    def get_id(self):
2568        return self.target.get_id()
2569
2570    def get_all_link_deps(self):
2571        return self.target.get_all_link_deps()
2572
2573    def get_link_deps_mapping(self, prefix: str, environment: environment.Environment) -> T.Mapping[str, str]:
2574        return self.target.get_link_deps_mapping(prefix, environment)
2575
2576    def get_link_dep_subdirs(self):
2577        return self.target.get_link_dep_subdirs()
2578
2579    def is_linkable_target(self):
2580        suf = os.path.splitext(self.output)[-1]
2581        if suf == '.a' or suf == '.dll' or suf == '.lib' or suf == '.so':
2582            return True
2583
2584    def should_install(self) -> bool:
2585        return self.target.should_install()
2586
2587    def is_internal(self) -> bool:
2588        return self.target.is_internal()
2589
2590    def extract_all_objects_recurse(self):
2591        return self.target.extract_all_objects_recurse()
2592
2593    def get_custom_install_dir(self):
2594        return self.target.get_custom_install_dir()
2595
2596class ConfigurationData(HoldableObject):
2597    def __init__(self) -> None:
2598        super().__init__()
2599        self.values: T.Dict[
2600            str,
2601            T.Tuple[
2602                T.Union[str, int, bool],
2603                T.Optional[str]
2604            ]
2605        ] = {}
2606
2607    def __repr__(self):
2608        return repr(self.values)
2609
2610    def __contains__(self, value: str) -> bool:
2611        return value in self.values
2612
2613    def get(self, name: str) -> T.Tuple[T.Union[str, int, bool], T.Optional[str]]:
2614        return self.values[name] # (val, desc)
2615
2616    def keys(self) -> T.Iterator[str]:
2617        return self.values.keys()
2618
2619# A bit poorly named, but this represents plain data files to copy
2620# during install.
2621class Data(HoldableObject):
2622    def __init__(self, sources: T.List[File], install_dir: str,
2623                 install_mode: 'FileMode', subproject: str,
2624                 rename: T.List[str] = None):
2625        self.sources = sources
2626        self.install_dir = install_dir
2627        self.install_mode = install_mode
2628        if rename is None:
2629            self.rename = [os.path.basename(f.fname) for f in self.sources]
2630        else:
2631            self.rename = rename
2632        self.subproject = subproject
2633
2634class TestSetup:
2635    def __init__(self, exe_wrapper: T.Optional[T.List[str]], gdb: bool,
2636                 timeout_multiplier: int, env: EnvironmentVariables,
2637                 exclude_suites: T.List[str]):
2638        self.exe_wrapper = exe_wrapper
2639        self.gdb = gdb
2640        self.timeout_multiplier = timeout_multiplier
2641        self.env = env
2642        self.exclude_suites = exclude_suites
2643
2644def get_sources_string_names(sources, backend):
2645    '''
2646    For the specified list of @sources which can be strings, Files, or targets,
2647    get all the output basenames.
2648    '''
2649    names = []
2650    for s in sources:
2651        if isinstance(s, str):
2652            names.append(s)
2653        elif isinstance(s, (BuildTarget, CustomTarget, CustomTargetIndex, GeneratedList)):
2654            names += s.get_outputs()
2655        elif isinstance(s, ExtractedObjects):
2656            names += s.get_outputs(backend)
2657        elif isinstance(s, File):
2658            names.append(s.fname)
2659        else:
2660            raise AssertionError(f'Unknown source type: {s!r}')
2661    return names
2662
2663def load(build_dir: str) -> Build:
2664    filename = os.path.join(build_dir, 'meson-private', 'build.dat')
2665    load_fail_msg = f'Build data file {filename!r} is corrupted. Try with a fresh build tree.'
2666    nonexisting_fail_msg = f'No such build data file as "{filename!r}".'
2667    try:
2668        with open(filename, 'rb') as f:
2669            obj = pickle.load(f)
2670    except FileNotFoundError:
2671        raise MesonException(nonexisting_fail_msg)
2672    except (pickle.UnpicklingError, EOFError):
2673        raise MesonException(load_fail_msg)
2674    except AttributeError:
2675        raise MesonException(
2676            f"Build data file {filename!r} references functions or classes that don't "
2677            "exist. This probably means that it was generated with an old "
2678            "version of meson. Try running from the source directory "
2679            f"meson {build_dir} --wipe")
2680    if not isinstance(obj, Build):
2681        raise MesonException(load_fail_msg)
2682    return obj
2683
2684def save(obj: Build, filename: str) -> None:
2685    with open(filename, 'wb') as f:
2686        pickle.dump(obj, f)
2687