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