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.
14import typing as T
15import os
16import re
17import pickle
18import shlex
19import subprocess
20from collections import OrderedDict
21from enum import Enum, unique
22import itertools
23from pathlib import PurePath, Path
24from functools import lru_cache
25
26from . import backends
27from .. import modules
28from .. import environment, mesonlib
29from .. import build
30from .. import mlog
31from .. import dependencies
32from .. import compilers
33from ..arglist import CompilerArgs
34from ..compilers import (
35    Compiler, CCompiler,
36    DmdDCompiler,
37    FortranCompiler, PGICCompiler,
38    VisualStudioCsCompiler,
39    VisualStudioLikeCompiler,
40)
41from ..linkers import ArLinker, VisualStudioLinker
42from ..mesonlib import (
43    File, LibType, MachineChoice, MesonException, OrderedSet, PerMachine,
44    ProgressBar, quote_arg, unholder,
45)
46from ..mesonlib import get_compiler_for_source, has_path_sep
47from .backends import CleanTrees
48from ..build import InvalidArguments
49from ..interpreter import Interpreter
50
51FORTRAN_INCLUDE_PAT = r"^\s*#?include\s*['\"](\w+\.\w+)['\"]"
52FORTRAN_MODULE_PAT = r"^\s*\bmodule\b\s+(\w+)\s*(?:!+.*)*$"
53FORTRAN_SUBMOD_PAT = r"^\s*\bsubmodule\b\s*\((\w+:?\w+)\)\s*(\w+)"
54FORTRAN_USE_PAT = r"^\s*use,?\s*(?:non_intrinsic)?\s*(?:::)?\s*(\w+)"
55
56def cmd_quote(s):
57    # see: https://docs.microsoft.com/en-us/windows/desktop/api/shellapi/nf-shellapi-commandlinetoargvw#remarks
58
59    # backslash escape any existing double quotes
60    # any existing backslashes preceding a quote are doubled
61    s = re.sub(r'(\\*)"', lambda m: '\\' * (len(m.group(1)) * 2 + 1) + '"', s)
62    # any terminal backslashes likewise need doubling
63    s = re.sub(r'(\\*)$', lambda m: '\\' * (len(m.group(1)) * 2), s)
64    # and double quote
65    s = '"{}"'.format(s)
66
67    return s
68
69def gcc_rsp_quote(s):
70    # see: the function buildargv() in libiberty
71    #
72    # this differs from sh-quoting in that a backslash *always* escapes the
73    # following character, even inside single quotes.
74
75    s = s.replace('\\', '\\\\')
76
77    return shlex.quote(s)
78
79# How ninja executes command lines differs between Unix and Windows
80# (see https://ninja-build.org/manual.html#ref_rule_command)
81if mesonlib.is_windows():
82    quote_func = cmd_quote
83    execute_wrapper = ['cmd', '/c']  # unused
84    rmfile_prefix = ['del', '/f', '/s', '/q', '{}', '&&']
85else:
86    quote_func = quote_arg
87    execute_wrapper = []
88    rmfile_prefix = ['rm', '-f', '{}', '&&']
89
90def get_rsp_threshold():
91    '''Return a conservative estimate of the commandline size in bytes
92    above which a response file should be used.  May be overridden for
93    debugging by setting environment variable MESON_RSP_THRESHOLD.'''
94
95    if mesonlib.is_windows():
96        # Usually 32k, but some projects might use cmd.exe,
97        # and that has a limit of 8k.
98        limit = 8192
99    else:
100        # On Linux, ninja always passes the commandline as a single
101        # big string to /bin/sh, and the kernel limits the size of a
102        # single argument; see MAX_ARG_STRLEN
103        limit = 131072
104    # Be conservative
105    limit = limit / 2
106    return int(os.environ.get('MESON_RSP_THRESHOLD', limit))
107
108# a conservative estimate of the command-line length limit
109rsp_threshold = get_rsp_threshold()
110
111# ninja variables whose value should remain unquoted. The value of these ninja
112# variables (or variables we use them in) is interpreted directly by ninja
113# (e.g. the value of the depfile variable is a pathname that ninja will read
114# from, etc.), so it must not be shell quoted.
115raw_names = {'DEPFILE_UNQUOTED', 'DESC', 'pool', 'description', 'targetdep'}
116
117def ninja_quote(text, is_build_line=False):
118    if is_build_line:
119        qcs = ('$', ' ', ':')
120    else:
121        qcs = ('$', ' ')
122    for char in qcs:
123        text = text.replace(char, '$' + char)
124    if '\n' in text:
125        errmsg = '''Ninja does not support newlines in rules. The content was:
126
127{}
128
129Please report this error with a test case to the Meson bug tracker.'''.format(text)
130        raise MesonException(errmsg)
131    return text
132
133@unique
134class Quoting(Enum):
135    both = 0
136    notShell = 1
137    notNinja = 2
138    none = 3
139
140class NinjaCommandArg:
141    def __init__(self, s, quoting = Quoting.both):
142        self.s = s
143        self.quoting = quoting
144
145    def __str__(self):
146        return self.s
147
148    @staticmethod
149    def list(l, q):
150        return [NinjaCommandArg(i, q) for i in l]
151
152class NinjaComment:
153    def __init__(self, comment):
154        self.comment = comment
155
156    def write(self, outfile):
157        for l in self.comment.split('\n'):
158            outfile.write('# ')
159            outfile.write(l)
160            outfile.write('\n')
161        outfile.write('\n')
162
163class NinjaRule:
164    def __init__(self, rule, command, args, description,
165                 rspable = False, deps = None, depfile = None, extra = None,
166                 rspfile_quote_style = 'gcc'):
167
168        def strToCommandArg(c):
169            if isinstance(c, NinjaCommandArg):
170                return c
171
172            # deal with common cases here, so we don't have to explicitly
173            # annotate the required quoting everywhere
174            if c == '&&':
175                # shell constructs shouldn't be shell quoted
176                return NinjaCommandArg(c, Quoting.notShell)
177            if c.startswith('$'):
178                var = re.search(r'\$\{?(\w*)\}?', c).group(1)
179                if var not in raw_names:
180                    # ninja variables shouldn't be ninja quoted, and their value
181                    # is already shell quoted
182                    return NinjaCommandArg(c, Quoting.none)
183                else:
184                    # shell quote the use of ninja variables whose value must
185                    # not be shell quoted (as it also used by ninja)
186                    return NinjaCommandArg(c, Quoting.notNinja)
187
188            return NinjaCommandArg(c)
189
190        self.name = rule
191        self.command = list(map(strToCommandArg, command))  # includes args which never go into a rspfile
192        self.args = list(map(strToCommandArg, args))  # args which will go into a rspfile, if used
193        self.description = description
194        self.deps = deps  # depstyle 'gcc' or 'msvc'
195        self.depfile = depfile
196        self.extra = extra
197        self.rspable = rspable  # if a rspfile can be used
198        self.refcount = 0
199        self.rsprefcount = 0
200        self.rspfile_quote_style = rspfile_quote_style  # rspfile quoting style is 'gcc' or 'cl'
201
202        if self.depfile == '$DEPFILE':
203            self.depfile += '_UNQUOTED'
204
205    @staticmethod
206    def _quoter(x, qf = quote_func):
207        if isinstance(x, NinjaCommandArg):
208            if x.quoting == Quoting.none:
209                return x.s
210            elif x.quoting == Quoting.notNinja:
211                return qf(x.s)
212            elif x.quoting == Quoting.notShell:
213                return ninja_quote(x.s)
214            # fallthrough
215        return ninja_quote(qf(str(x)))
216
217    def write(self, outfile):
218        if self.rspfile_quote_style == 'cl':
219            rspfile_quote_func = cmd_quote
220        else:
221            rspfile_quote_func = gcc_rsp_quote
222
223        def rule_iter():
224            if self.refcount:
225                yield ''
226            if self.rsprefcount:
227                yield '_RSP'
228
229        for rsp in rule_iter():
230            outfile.write('rule {}{}\n'.format(self.name, rsp))
231            if rsp == '_RSP':
232                outfile.write(' command = {} @$out.rsp\n'.format(' '.join([self._quoter(x) for x in self.command])))
233                outfile.write(' rspfile = $out.rsp\n')
234                outfile.write(' rspfile_content = {}\n'.format(' '.join([self._quoter(x, rspfile_quote_func) for x in self.args])))
235            else:
236                outfile.write(' command = {}\n'.format(' '.join([self._quoter(x) for x in (self.command + self.args)])))
237            if self.deps:
238                outfile.write(' deps = {}\n'.format(self.deps))
239            if self.depfile:
240                outfile.write(' depfile = {}\n'.format(self.depfile))
241            outfile.write(' description = {}\n'.format(self.description))
242            if self.extra:
243                for l in self.extra.split('\n'):
244                    outfile.write(' ')
245                    outfile.write(l)
246                    outfile.write('\n')
247            outfile.write('\n')
248
249    def length_estimate(self, infiles, outfiles, elems):
250        # determine variables
251        # this order of actions only approximates ninja's scoping rules, as
252        # documented at: https://ninja-build.org/manual.html#ref_scope
253        ninja_vars = {}
254        for e in elems:
255            (name, value) = e
256            ninja_vars[name] = value
257        ninja_vars['deps'] = self.deps
258        ninja_vars['depfile'] = self.depfile
259        ninja_vars['in'] = infiles
260        ninja_vars['out'] = outfiles
261
262        # expand variables in command
263        command = ' '.join([self._quoter(x) for x in self.command + self.args])
264        expanded_command = ''
265        for m in re.finditer(r'(\${\w*})|(\$\w*)|([^$]*)', command):
266            chunk = m.group()
267            if chunk.startswith('$'):
268                chunk = chunk[1:]
269                chunk = re.sub(r'{(.*)}', r'\1', chunk)
270                chunk = ninja_vars.get(chunk, [])  # undefined ninja variables are empty
271                chunk = ' '.join(chunk)
272            expanded_command += chunk
273
274        # determine command length
275        return len(expanded_command)
276
277class NinjaBuildElement:
278    def __init__(self, all_outputs, outfilenames, rulename, infilenames, implicit_outs=None):
279        self.implicit_outfilenames = implicit_outs or []
280        if isinstance(outfilenames, str):
281            self.outfilenames = [outfilenames]
282        else:
283            self.outfilenames = outfilenames
284        assert(isinstance(rulename, str))
285        self.rulename = rulename
286        if isinstance(infilenames, str):
287            self.infilenames = [infilenames]
288        else:
289            self.infilenames = infilenames
290        self.deps = OrderedSet()
291        self.orderdeps = OrderedSet()
292        self.elems = []
293        self.all_outputs = all_outputs
294
295    def add_dep(self, dep):
296        if isinstance(dep, list):
297            self.deps.update(dep)
298        else:
299            self.deps.add(dep)
300
301    def add_orderdep(self, dep):
302        if isinstance(dep, list):
303            self.orderdeps.update(dep)
304        else:
305            self.orderdeps.add(dep)
306
307    def add_item(self, name, elems):
308        # Always convert from GCC-style argument naming to the naming used by the
309        # current compiler. Also filter system include paths, deduplicate, etc.
310        if isinstance(elems, CompilerArgs):
311            elems = elems.to_native()
312        if isinstance(elems, str):
313            elems = [elems]
314        self.elems.append((name, elems))
315
316        if name == 'DEPFILE':
317            self.elems.append((name + '_UNQUOTED', elems))
318
319    def _should_use_rspfile(self):
320        # 'phony' is a rule built-in to ninja
321        if self.rulename == 'phony':
322            return False
323
324        if not self.rule.rspable:
325            return False
326
327        infilenames = ' '.join([ninja_quote(i, True) for i in self.infilenames])
328        outfilenames = ' '.join([ninja_quote(i, True) for i in self.outfilenames])
329
330        return self.rule.length_estimate(infilenames,
331                                         outfilenames,
332                                         self.elems) >= rsp_threshold
333
334    def count_rule_references(self):
335        if self.rulename != 'phony':
336            if self._should_use_rspfile():
337                self.rule.rsprefcount += 1
338            else:
339                self.rule.refcount += 1
340
341    def write(self, outfile):
342        self.check_outputs()
343        ins = ' '.join([ninja_quote(i, True) for i in self.infilenames])
344        outs = ' '.join([ninja_quote(i, True) for i in self.outfilenames])
345        implicit_outs = ' '.join([ninja_quote(i, True) for i in self.implicit_outfilenames])
346        if implicit_outs:
347            implicit_outs = ' | ' + implicit_outs
348        use_rspfile = self._should_use_rspfile()
349        if use_rspfile:
350            rulename = self.rulename + '_RSP'
351            mlog.debug("Command line for building %s is long, using a response file" % self.outfilenames)
352        else:
353            rulename = self.rulename
354        line = 'build {}{}: {} {}'.format(outs, implicit_outs, rulename, ins)
355        if len(self.deps) > 0:
356            line += ' | ' + ' '.join([ninja_quote(x, True) for x in self.deps])
357        if len(self.orderdeps) > 0:
358            line += ' || ' + ' '.join([ninja_quote(x, True) for x in self.orderdeps])
359        line += '\n'
360        # This is the only way I could find to make this work on all
361        # platforms including Windows command shell. Slash is a dir separator
362        # on Windows, too, so all characters are unambiguous and, more importantly,
363        # do not require quoting, unless explicitly specified, which is necessary for
364        # the csc compiler.
365        line = line.replace('\\', '/')
366        outfile.write(line)
367
368        if use_rspfile:
369            if self.rule.rspfile_quote_style == 'cl':
370                qf = cmd_quote
371            else:
372                qf = gcc_rsp_quote
373        else:
374            qf = quote_func
375
376        for e in self.elems:
377            (name, elems) = e
378            should_quote = name not in raw_names
379            line = ' {} = '.format(name)
380            newelems = []
381            for i in elems:
382                if not should_quote or i == '&&': # Hackety hack hack
383                    quoter = ninja_quote
384                else:
385                    quoter = lambda x: ninja_quote(qf(x))
386                newelems.append(quoter(i))
387            line += ' '.join(newelems)
388            line += '\n'
389            outfile.write(line)
390        outfile.write('\n')
391
392    def check_outputs(self):
393        for n in self.outfilenames:
394            if n in self.all_outputs:
395                raise MesonException('Multiple producers for Ninja target "{}". Please rename your targets.'.format(n))
396            self.all_outputs[n] = True
397
398class NinjaBackend(backends.Backend):
399
400    def __init__(self, build: T.Optional[build.Build], interpreter: T.Optional[Interpreter]):
401        super().__init__(build, interpreter)
402        self.name = 'ninja'
403        self.ninja_filename = 'build.ninja'
404        self.fortran_deps = {}
405        self.all_outputs = {}
406        self.introspection_data = {}
407        self.created_llvm_ir_rule = PerMachine(False, False)
408
409    def create_target_alias(self, to_target):
410        # We need to use aliases for targets that might be used as directory
411        # names to workaround a Ninja bug that breaks `ninja -t clean`.
412        # This is used for 'reserved' targets such as 'test', 'install',
413        # 'benchmark', etc, and also for RunTargets.
414        # https://github.com/mesonbuild/meson/issues/1644
415        if not to_target.startswith('meson-'):
416            m = 'Invalid usage of create_target_alias with {!r}'
417            raise AssertionError(m.format(to_target))
418        from_target = to_target[len('meson-'):]
419        elem = NinjaBuildElement(self.all_outputs, from_target, 'phony', to_target)
420        self.add_build(elem)
421
422    def detect_vs_dep_prefix(self, tempfilename):
423        '''VS writes its dependency in a locale dependent format.
424        Detect the search prefix to use.'''
425        # TODO don't hard-code host
426        for compiler in self.environment.coredata.compilers.host.values():
427            # Have to detect the dependency format
428
429            # IFort on windows is MSVC like, but doesn't have /showincludes
430            if isinstance(compiler, FortranCompiler):
431                continue
432            if isinstance(compiler, PGICCompiler) and mesonlib.is_windows():
433                # for the purpose of this function, PGI doesn't act enough like MSVC
434                return open(tempfilename, 'a', encoding='utf-8')
435            if isinstance(compiler, VisualStudioLikeCompiler):
436                break
437        else:
438            # None of our compilers are MSVC, we're done.
439            return open(tempfilename, 'a', encoding='utf-8')
440        filename = os.path.join(self.environment.get_scratch_dir(),
441                                'incdetect.c')
442        with open(filename, 'w') as f:
443            f.write('''#include<stdio.h>
444int dummy;
445''')
446
447        # The output of cl dependency information is language
448        # and locale dependent. Any attempt at converting it to
449        # Python strings leads to failure. We _must_ do this detection
450        # in raw byte mode and write the result in raw bytes.
451        pc = subprocess.Popen(compiler.get_exelist() +
452                              ['/showIncludes', '/c', 'incdetect.c'],
453                              cwd=self.environment.get_scratch_dir(),
454                              stdout=subprocess.PIPE, stderr=subprocess.PIPE)
455        (stdout, stderr) = pc.communicate()
456
457        # We want to match 'Note: including file: ' in the line
458        # 'Note: including file: d:\MyDir\include\stdio.h', however
459        # different locales have different messages with a different
460        # number of colons. Match up to the the drive name 'd:\'.
461        # When used in cross compilation, the path separator is a
462        # backslash rather than a forward slash so handle both.
463        matchre = re.compile(rb"^(.*\s)([a-zA-Z]:\\|\/).*stdio.h$")
464
465        def detect_prefix(out):
466            for line in re.split(rb'\r?\n', out):
467                match = matchre.match(line)
468                if match:
469                    with open(tempfilename, 'ab') as binfile:
470                        binfile.write(b'msvc_deps_prefix = ' + match.group(1) + b'\n')
471                    return open(tempfilename, 'a', encoding='utf-8')
472            return None
473
474        # Some cl wrappers (e.g. Squish Coco) output dependency info
475        # to stderr rather than stdout
476        result = detect_prefix(stdout) or detect_prefix(stderr)
477        if result:
478            return result
479
480        raise MesonException('Could not determine vs dep dependency prefix string.')
481
482    def generate(self):
483        ninja = environment.detect_ninja_command_and_version(log=True)
484        if ninja is None:
485            raise MesonException('Could not detect Ninja v1.7 or newer')
486        (self.ninja_command, self.ninja_version) = ninja
487        outfilename = os.path.join(self.environment.get_build_dir(), self.ninja_filename)
488        tempfilename = outfilename + '~'
489        with open(tempfilename, 'w', encoding='utf-8') as outfile:
490            outfile.write('# This is the build file for project "{}"\n'.format(self.build.get_project()))
491            outfile.write('# It is autogenerated by the Meson build system.\n')
492            outfile.write('# Do not edit by hand.\n\n')
493            outfile.write('ninja_required_version = 1.7.1\n\n')
494
495            num_pools = self.environment.coredata.backend_options['backend_max_links'].value
496            if num_pools > 0:
497                outfile.write('''pool link_pool
498  depth = {}
499
500'''.format(num_pools))
501
502        with self.detect_vs_dep_prefix(tempfilename) as outfile:
503            self.generate_rules()
504
505            self.build_elements = []
506            self.generate_phony()
507            self.add_build_comment(NinjaComment('Build rules for targets'))
508            for t in ProgressBar(self.build.get_targets().values(), desc='Generating targets'):
509                self.generate_target(t)
510            self.add_build_comment(NinjaComment('Test rules'))
511            self.generate_tests()
512            self.add_build_comment(NinjaComment('Install rules'))
513            self.generate_install()
514            self.generate_dist()
515            if 'b_coverage' in self.environment.coredata.base_options and \
516                    self.environment.coredata.base_options['b_coverage'].value:
517                self.add_build_comment(NinjaComment('Coverage rules'))
518                self.generate_coverage_rules()
519            self.add_build_comment(NinjaComment('Suffix'))
520            self.generate_utils()
521            self.generate_ending()
522
523            self.write_rules(outfile)
524            self.write_builds(outfile)
525
526            default = 'default all\n\n'
527            outfile.write(default)
528        # Only overwrite the old build file after the new one has been
529        # fully created.
530        os.replace(tempfilename, outfilename)
531        mlog.cmd_ci_include(outfilename)  # For CI debugging
532        self.generate_compdb()
533
534    # http://clang.llvm.org/docs/JSONCompilationDatabase.html
535    def generate_compdb(self):
536        rules = []
537        # TODO: Rather than an explicit list here, rules could be marked in the
538        # rule store as being wanted in compdb
539        for for_machine in MachineChoice:
540            for lang in self.environment.coredata.compilers[for_machine]:
541                rules += [ "%s%s" % (rule, ext) for rule in [self.get_compiler_rule_name(lang, for_machine)]
542                                                for ext in ['', '_RSP']]
543                rules += [ "%s%s" % (rule, ext) for rule in [self.get_pch_rule_name(lang, for_machine)]
544                                                for ext in ['', '_RSP']]
545        compdb_options = ['-x'] if mesonlib.version_compare(self.ninja_version, '>=1.9') else []
546        ninja_compdb = self.ninja_command + ['-t', 'compdb'] + compdb_options + rules
547        builddir = self.environment.get_build_dir()
548        try:
549            jsondb = subprocess.check_output(ninja_compdb, cwd=builddir)
550            with open(os.path.join(builddir, 'compile_commands.json'), 'wb') as f:
551                f.write(jsondb)
552        except Exception:
553            mlog.warning('Could not create compilation database.')
554
555    # Get all generated headers. Any source file might need them so
556    # we need to add an order dependency to them.
557    def get_generated_headers(self, target):
558        if hasattr(target, 'cached_generated_headers'):
559            return target.cached_generated_headers
560        header_deps = []
561        # XXX: Why don't we add deps to CustomTarget headers here?
562        for genlist in target.get_generated_sources():
563            if isinstance(genlist, (build.CustomTarget, build.CustomTargetIndex)):
564                continue
565            for src in genlist.get_outputs():
566                if self.environment.is_header(src):
567                    header_deps.append(self.get_target_generated_dir(target, genlist, src))
568        if 'vala' in target.compilers and not isinstance(target, build.Executable):
569            vala_header = File.from_built_file(self.get_target_dir(target), target.vala_header)
570            header_deps.append(vala_header)
571        # Recurse and find generated headers
572        for dep in itertools.chain(target.link_targets, target.link_whole_targets):
573            if isinstance(dep, (build.StaticLibrary, build.SharedLibrary)):
574                header_deps += self.get_generated_headers(dep)
575        target.cached_generated_headers = header_deps
576        return header_deps
577
578    def get_target_generated_sources(self, target):
579        """
580        Returns a dictionary with the keys being the path to the file
581        (relative to the build directory) of that type and the value
582        being the GeneratorList or CustomTarget that generated it.
583        """
584        srcs = OrderedDict()
585        for gensrc in target.get_generated_sources():
586            for s in gensrc.get_outputs():
587                f = self.get_target_generated_dir(target, gensrc, s)
588                srcs[f] = s
589        return srcs
590
591    def get_target_sources(self, target):
592        srcs = OrderedDict()
593        for s in target.get_sources():
594            # BuildTarget sources are always mesonlib.File files which are
595            # either in the source root, or generated with configure_file and
596            # in the build root
597            if not isinstance(s, File):
598                raise InvalidArguments('All sources in target {!r} must be of type mesonlib.File'.format(s))
599            f = s.rel_to_builddir(self.build_to_src)
600            srcs[f] = s
601        return srcs
602
603    # Languages that can mix with C or C++ but don't support unity builds yet
604    # because the syntax we use for unity builds is specific to C/++/ObjC/++.
605    # Assembly files cannot be unitified and neither can LLVM IR files
606    langs_cant_unity = ('d', 'fortran')
607
608    def get_target_source_can_unity(self, target, source):
609        if isinstance(source, File):
610            source = source.fname
611        if self.environment.is_llvm_ir(source) or \
612           self.environment.is_assembly(source):
613            return False
614        suffix = os.path.splitext(source)[1][1:]
615        for lang in self.langs_cant_unity:
616            if lang not in target.compilers:
617                continue
618            if suffix in target.compilers[lang].file_suffixes:
619                return False
620        return True
621
622    def create_target_source_introspection(self, target: build.Target, comp: compilers.Compiler, parameters, sources, generated_sources):
623        '''
624        Adds the source file introspection information for a language of a target
625
626        Internal introspection storage formart:
627        self.introspection_data = {
628            '<target ID>': {
629                <id tuple>: {
630                    'language: 'lang',
631                    'compiler': ['comp', 'exe', 'list'],
632                    'parameters': ['UNIQUE', 'parameter', 'list'],
633                    'sources': [],
634                    'generated_sources': [],
635                }
636            }
637        }
638        '''
639        tid = target.get_id()
640        lang = comp.get_language()
641        tgt = self.introspection_data[tid]
642        # Find an existing entry or create a new one
643        id_hash = (lang, tuple(parameters))
644        src_block = tgt.get(id_hash, None)
645        if src_block is None:
646            # Convert parameters
647            if isinstance(parameters, CompilerArgs):
648                parameters = parameters.to_native(copy=True)
649            parameters = comp.compute_parameters_with_absolute_paths(parameters, self.build_dir)
650            # The new entry
651            src_block = {
652                'language': lang,
653                'compiler': comp.get_exelist(),
654                'parameters': parameters,
655                'sources': [],
656                'generated_sources': [],
657            }
658            tgt[id_hash] = src_block
659        # Make source files absolute
660        sources = [x.absolute_path(self.source_dir, self.build_dir) if isinstance(x, File) else os.path.normpath(os.path.join(self.build_dir, x))
661                   for x in sources]
662        generated_sources = [x.absolute_path(self.source_dir, self.build_dir) if isinstance(x, File) else os.path.normpath(os.path.join(self.build_dir, x))
663                             for x in generated_sources]
664        # Add the source files
665        src_block['sources'] += sources
666        src_block['generated_sources'] += generated_sources
667
668    def is_rust_target(self, target):
669        if len(target.sources) > 0:
670            first_file = target.sources[0]
671            if first_file.fname.endswith('.rs'):
672                return True
673        return False
674
675    def generate_target(self, target):
676        if isinstance(target, build.CustomTarget):
677            self.generate_custom_target(target)
678        if isinstance(target, build.RunTarget):
679            self.generate_run_target(target)
680        name = target.get_id()
681        if name in self.processed_targets:
682            return
683        self.processed_targets[name] = True
684        # Initialize an empty introspection source list
685        self.introspection_data[name] = {}
686        # Generate rules for all dependency targets
687        self.process_target_dependencies(target)
688        # If target uses a language that cannot link to C objects,
689        # just generate for that language and return.
690        if isinstance(target, build.Jar):
691            self.generate_jar_target(target)
692            return
693        if self.is_rust_target(target):
694            self.generate_rust_target(target)
695            return
696        if 'cs' in target.compilers:
697            self.generate_cs_target(target)
698            return
699        if 'swift' in target.compilers:
700            self.generate_swift_target(target)
701            return
702
703        # Now we handle the following languages:
704        # ObjC++, ObjC, C++, C, D, Fortran, Vala
705
706        # target_sources:
707        #   Pre-existing target C/C++ sources to be built; dict of full path to
708        #   source relative to build root and the original File object.
709        # generated_sources:
710        #   GeneratedList and CustomTarget sources to be built; dict of the full
711        #   path to source relative to build root and the generating target/list
712        # vala_generated_sources:
713        #   Array of sources generated by valac that have to be compiled
714        if 'vala' in target.compilers:
715            # Sources consumed by valac are filtered out. These only contain
716            # C/C++ sources, objects, generated libs, and unknown sources now.
717            target_sources, generated_sources, \
718                vala_generated_sources = self.generate_vala_compile(target)
719        else:
720            target_sources = self.get_target_sources(target)
721            generated_sources = self.get_target_generated_sources(target)
722            vala_generated_sources = []
723        self.scan_fortran_module_outputs(target)
724        # Generate rules for GeneratedLists
725        self.generate_generator_list_rules(target)
726
727        # Generate rules for building the remaining source files in this target
728        outname = self.get_target_filename(target)
729        obj_list = []
730        is_unity = self.is_unity(target)
731        header_deps = []
732        unity_src = []
733        unity_deps = [] # Generated sources that must be built before compiling a Unity target.
734        header_deps += self.get_generated_headers(target)
735
736        if is_unity:
737            # Warn about incompatible sources if a unity build is enabled
738            langs = set(target.compilers.keys())
739            langs_cant = langs.intersection(self.langs_cant_unity)
740            if langs_cant:
741                langs_are = langs = ', '.join(langs_cant).upper()
742                langs_are += ' are' if len(langs_cant) > 1 else ' is'
743                msg = '{} not supported in Unity builds yet, so {} ' \
744                      'sources in the {!r} target will be compiled normally' \
745                      ''.format(langs_are, langs, target.name)
746                mlog.log(mlog.red('FIXME'), msg)
747
748        # Get a list of all generated headers that will be needed while building
749        # this target's sources (generated sources and pre-existing sources).
750        # This will be set as dependencies of all the target's sources. At the
751        # same time, also deal with generated sources that need to be compiled.
752        generated_source_files = []
753        for rel_src in generated_sources.keys():
754            dirpart, fnamepart = os.path.split(rel_src)
755            raw_src = File(True, dirpart, fnamepart)
756            if self.environment.is_source(rel_src) and not self.environment.is_header(rel_src):
757                if is_unity and self.get_target_source_can_unity(target, rel_src):
758                    unity_deps.append(raw_src)
759                    abs_src = os.path.join(self.environment.get_build_dir(), rel_src)
760                    unity_src.append(abs_src)
761                else:
762                    generated_source_files.append(raw_src)
763            elif self.environment.is_object(rel_src):
764                obj_list.append(rel_src)
765            elif self.environment.is_library(rel_src) or modules.is_module_library(rel_src):
766                pass
767            else:
768                # Assume anything not specifically a source file is a header. This is because
769                # people generate files with weird suffixes (.inc, .fh) that they then include
770                # in their source files.
771                header_deps.append(raw_src)
772        # These are the generated source files that need to be built for use by
773        # this target. We create the Ninja build file elements for this here
774        # because we need `header_deps` to be fully generated in the above loop.
775        for src in generated_source_files:
776            if self.environment.is_llvm_ir(src):
777                o = self.generate_llvm_ir_compile(target, src)
778            else:
779                o = self.generate_single_compile(target, src, True,
780                                                 order_deps=header_deps)
781            obj_list.append(o)
782
783        use_pch = self.environment.coredata.base_options.get('b_pch', False)
784        if use_pch and target.has_pch():
785            pch_objects = self.generate_pch(target, header_deps=header_deps)
786        else:
787            pch_objects = []
788
789        # Generate compilation targets for C sources generated from Vala
790        # sources. This can be extended to other $LANG->C compilers later if
791        # necessary. This needs to be separate for at least Vala
792        vala_generated_source_files = []
793        for src in vala_generated_sources:
794            dirpart, fnamepart = os.path.split(src)
795            raw_src = File(True, dirpart, fnamepart)
796            if is_unity:
797                unity_src.append(os.path.join(self.environment.get_build_dir(), src))
798                header_deps.append(raw_src)
799            else:
800                # Generated targets are ordered deps because the must exist
801                # before the sources compiling them are used. After the first
802                # compile we get precise dependency info from dep files.
803                # This should work in all cases. If it does not, then just
804                # move them from orderdeps to proper deps.
805                if self.environment.is_header(src):
806                    header_deps.append(raw_src)
807                else:
808                    # We gather all these and generate compile rules below
809                    # after `header_deps` (above) is fully generated
810                    vala_generated_source_files.append(raw_src)
811        for src in vala_generated_source_files:
812            # Passing 'vala' here signifies that we want the compile
813            # arguments to be specialized for C code generated by
814            # valac. For instance, no warnings should be emitted.
815            obj_list.append(self.generate_single_compile(target, src, 'vala', [], header_deps))
816
817        # Generate compile targets for all the pre-existing sources for this target
818        for src in target_sources.values():
819            if not self.environment.is_header(src):
820                if self.environment.is_llvm_ir(src):
821                    obj_list.append(self.generate_llvm_ir_compile(target, src))
822                elif is_unity and self.get_target_source_can_unity(target, src):
823                    abs_src = os.path.join(self.environment.get_build_dir(),
824                                           src.rel_to_builddir(self.build_to_src))
825                    unity_src.append(abs_src)
826                else:
827                    obj_list.append(self.generate_single_compile(target, src, False, [], header_deps))
828        obj_list += self.flatten_object_list(target)
829        if is_unity:
830            for src in self.generate_unity_files(target, unity_src):
831                obj_list.append(self.generate_single_compile(target, src, True, unity_deps + header_deps))
832        linker, stdlib_args = self.determine_linker_and_stdlib_args(target)
833        elem = self.generate_link(target, outname, obj_list, linker, pch_objects, stdlib_args=stdlib_args)
834        self.generate_shlib_aliases(target, self.get_target_dir(target))
835        self.add_build(elem)
836
837    def process_target_dependencies(self, target):
838        for t in target.get_dependencies():
839            if t.get_id() not in self.processed_targets:
840                self.generate_target(t)
841
842    def custom_target_generator_inputs(self, target):
843        for s in unholder(target.sources):
844            if isinstance(s, build.GeneratedList):
845                self.generate_genlist_for_target(s, target)
846
847    def unwrap_dep_list(self, target):
848        deps = []
849        for i in target.get_dependencies():
850            # FIXME, should not grab element at zero but rather expand all.
851            if isinstance(i, list):
852                i = i[0]
853            # Add a dependency on all the outputs of this target
854            for output in i.get_outputs():
855                deps.append(os.path.join(self.get_target_dir(i), output))
856        return deps
857
858    def generate_custom_target(self, target):
859        self.custom_target_generator_inputs(target)
860        (srcs, ofilenames, cmd) = self.eval_custom_target_command(target)
861        deps = self.unwrap_dep_list(target)
862        deps += self.get_custom_target_depend_files(target)
863        desc = 'Generating {0} with a {1} command'
864        if target.build_always_stale:
865            deps.append('PHONY')
866        if target.depfile is None:
867            rulename = 'CUSTOM_COMMAND'
868        else:
869            rulename = 'CUSTOM_COMMAND_DEP'
870        elem = NinjaBuildElement(self.all_outputs, ofilenames, rulename, srcs)
871        elem.add_dep(deps)
872        for d in target.extra_depends:
873            # Add a dependency on all the outputs of this target
874            for output in d.get_outputs():
875                elem.add_dep(os.path.join(self.get_target_dir(d), output))
876
877        meson_exe_cmd = self.as_meson_exe_cmdline(target.name, target.command[0], cmd[1:],
878                                                  for_machine=target.for_machine,
879                                                  extra_bdeps=target.get_transitive_build_target_deps(),
880                                                  capture=ofilenames[0] if target.capture else None)
881        if meson_exe_cmd:
882            cmd = meson_exe_cmd
883            cmd_type = 'meson_exe.py custom'
884        else:
885            cmd_type = 'custom'
886        if target.depfile is not None:
887            depfile = target.get_dep_outname(elem.infilenames)
888            rel_dfile = os.path.join(self.get_target_dir(target), depfile)
889            abs_pdir = os.path.join(self.environment.get_build_dir(), self.get_target_dir(target))
890            os.makedirs(abs_pdir, exist_ok=True)
891            elem.add_item('DEPFILE', rel_dfile)
892        if target.console:
893            elem.add_item('pool', 'console')
894        cmd = self.replace_paths(target, cmd)
895        elem.add_item('COMMAND', cmd)
896        elem.add_item('description', desc.format(target.name, cmd_type))
897        self.add_build(elem)
898        self.processed_targets[target.get_id()] = True
899
900    def build_run_target_name(self, target):
901        if target.subproject != '':
902            subproject_prefix = '{}@@'.format(target.subproject)
903        else:
904            subproject_prefix = ''
905        return '{}{}'.format(subproject_prefix, target.name)
906
907    def generate_run_target(self, target):
908        cmd = self.environment.get_build_command() + ['--internal', 'commandrunner']
909        deps = self.unwrap_dep_list(target)
910        arg_strings = []
911        for i in target.args:
912            if isinstance(i, str):
913                arg_strings.append(i)
914            elif isinstance(i, (build.BuildTarget, build.CustomTarget)):
915                relfname = self.get_target_filename(i)
916                arg_strings.append(os.path.join(self.environment.get_build_dir(), relfname))
917                deps.append(relfname)
918            elif isinstance(i, mesonlib.File):
919                relfname = i.rel_to_builddir(self.build_to_src)
920                arg_strings.append(os.path.join(self.environment.get_build_dir(), relfname))
921            else:
922                raise AssertionError('Unreachable code in generate_run_target: ' + str(i))
923        cmd += [self.environment.get_source_dir(),
924                self.environment.get_build_dir(),
925                target.subdir] + self.environment.get_build_command()
926        texe = target.command
927        try:
928            texe = texe.held_object
929        except AttributeError:
930            pass
931        if isinstance(texe, build.Executable):
932            abs_exe = os.path.join(self.environment.get_build_dir(), self.get_target_filename(texe))
933            deps.append(self.get_target_filename(texe))
934            if self.environment.is_cross_build():
935                exe_wrap = self.environment.get_exe_wrapper()
936                if exe_wrap:
937                    if not exe_wrap.found():
938                        msg = 'The exe_wrapper {!r} defined in the cross file is ' \
939                              'needed by run target {!r}, but was not found. ' \
940                              'Please check the command and/or add it to PATH.'
941                        raise MesonException(msg.format(exe_wrap.name, target.name))
942                    cmd += exe_wrap.get_command()
943            cmd.append(abs_exe)
944        elif isinstance(texe, dependencies.ExternalProgram):
945            cmd += texe.get_command()
946        elif isinstance(texe, build.CustomTarget):
947            deps.append(self.get_target_filename(texe))
948            cmd += [os.path.join(self.environment.get_build_dir(), self.get_target_filename(texe))]
949        elif isinstance(texe, mesonlib.File):
950            cmd.append(texe.absolute_path(self.environment.get_source_dir(), self.environment.get_build_dir()))
951        else:
952            cmd.append(target.command)
953        cmd += arg_strings
954
955        if texe:
956            target_name = 'meson-{}'.format(self.build_run_target_name(target))
957            elem = NinjaBuildElement(self.all_outputs, target_name, 'CUSTOM_COMMAND', [])
958            elem.add_item('COMMAND', cmd)
959            elem.add_item('description', 'Running external command {}'.format(target.name))
960            elem.add_item('pool', 'console')
961            # Alias that runs the target defined above with the name the user specified
962            self.create_target_alias(target_name)
963        else:
964            target_name = self.build_run_target_name(target)
965            elem = NinjaBuildElement(self.all_outputs, target_name, 'phony', [])
966
967        elem.add_dep(deps)
968        self.add_build(elem)
969        self.processed_targets[target.get_id()] = True
970
971    def generate_coverage_command(self, elem, outputs):
972        targets = self.build.get_targets().values()
973        use_llvm_cov = False
974        for target in targets:
975            if not hasattr(target, 'compilers'):
976                continue
977            for compiler in target.compilers.values():
978                if compiler.get_id() == 'clang' and not compiler.info.is_darwin():
979                    use_llvm_cov = True
980                    break
981        elem.add_item('COMMAND', self.environment.get_build_command() +
982                      ['--internal', 'coverage'] +
983                      outputs +
984                      [self.environment.get_source_dir(),
985                       os.path.join(self.environment.get_source_dir(),
986                                    self.build.get_subproject_dir()),
987                       self.environment.get_build_dir(),
988                       self.environment.get_log_dir()] +
989                       (['--use_llvm_cov'] if use_llvm_cov else []))
990
991    def generate_coverage_rules(self):
992        e = NinjaBuildElement(self.all_outputs, 'meson-coverage', 'CUSTOM_COMMAND', 'PHONY')
993        self.generate_coverage_command(e, [])
994        e.add_item('description', 'Generates coverage reports')
995        self.add_build(e)
996        # Alias that runs the target defined above
997        self.create_target_alias('meson-coverage')
998        self.generate_coverage_legacy_rules()
999
1000    def generate_coverage_legacy_rules(self):
1001        e = NinjaBuildElement(self.all_outputs, 'meson-coverage-xml', 'CUSTOM_COMMAND', 'PHONY')
1002        self.generate_coverage_command(e, ['--xml'])
1003        e.add_item('description', 'Generates XML coverage report')
1004        self.add_build(e)
1005        # Alias that runs the target defined above
1006        self.create_target_alias('meson-coverage-xml')
1007
1008        e = NinjaBuildElement(self.all_outputs, 'meson-coverage-text', 'CUSTOM_COMMAND', 'PHONY')
1009        self.generate_coverage_command(e, ['--text'])
1010        e.add_item('description', 'Generates text coverage report')
1011        self.add_build(e)
1012        # Alias that runs the target defined above
1013        self.create_target_alias('meson-coverage-text')
1014
1015        e = NinjaBuildElement(self.all_outputs, 'meson-coverage-html', 'CUSTOM_COMMAND', 'PHONY')
1016        self.generate_coverage_command(e, ['--html'])
1017        e.add_item('description', 'Generates HTML coverage report')
1018        self.add_build(e)
1019        # Alias that runs the target defined above
1020        self.create_target_alias('meson-coverage-html')
1021
1022    def generate_install(self):
1023        self.create_install_data_files()
1024        elem = NinjaBuildElement(self.all_outputs, 'meson-install', 'CUSTOM_COMMAND', 'PHONY')
1025        elem.add_dep('all')
1026        elem.add_item('DESC', 'Installing files.')
1027        elem.add_item('COMMAND', self.environment.get_build_command() + ['install', '--no-rebuild'])
1028        elem.add_item('pool', 'console')
1029        self.add_build(elem)
1030        # Alias that runs the target defined above
1031        self.create_target_alias('meson-install')
1032
1033    def generate_tests(self):
1034        self.serialize_tests()
1035        cmd = self.environment.get_build_command(True) + ['test', '--no-rebuild']
1036        if not self.environment.coredata.get_builtin_option('stdsplit'):
1037            cmd += ['--no-stdsplit']
1038        if self.environment.coredata.get_builtin_option('errorlogs'):
1039            cmd += ['--print-errorlogs']
1040        elem = NinjaBuildElement(self.all_outputs, 'meson-test', 'CUSTOM_COMMAND', ['all', 'PHONY'])
1041        elem.add_item('COMMAND', cmd)
1042        elem.add_item('DESC', 'Running all tests.')
1043        elem.add_item('pool', 'console')
1044        self.add_build(elem)
1045        # Alias that runs the above-defined meson-test target
1046        self.create_target_alias('meson-test')
1047
1048        # And then benchmarks.
1049        cmd = self.environment.get_build_command(True) + [
1050            'test', '--benchmark', '--logbase',
1051            'benchmarklog', '--num-processes=1', '--no-rebuild']
1052        elem = NinjaBuildElement(self.all_outputs, 'meson-benchmark', 'CUSTOM_COMMAND', ['all', 'PHONY'])
1053        elem.add_item('COMMAND', cmd)
1054        elem.add_item('DESC', 'Running benchmark suite.')
1055        elem.add_item('pool', 'console')
1056        self.add_build(elem)
1057        # Alias that runs the above-defined meson-benchmark target
1058        self.create_target_alias('meson-benchmark')
1059
1060    def generate_rules(self):
1061        self.rules = []
1062        self.ruledict = {}
1063
1064        self.add_rule_comment(NinjaComment('Rules for compiling.'))
1065        self.generate_compile_rules()
1066        self.add_rule_comment(NinjaComment('Rules for linking.'))
1067        self.generate_static_link_rules()
1068        self.generate_dynamic_link_rules()
1069        self.add_rule_comment(NinjaComment('Other rules'))
1070        # Ninja errors out if you have deps = gcc but no depfile, so we must
1071        # have two rules for custom commands.
1072        self.add_rule(NinjaRule('CUSTOM_COMMAND', ['$COMMAND'], [], '$DESC',
1073                                extra='restat = 1'))
1074        self.add_rule(NinjaRule('CUSTOM_COMMAND_DEP', ['$COMMAND'], [], '$DESC',
1075                                deps='gcc', depfile='$DEPFILE',
1076                                extra='restat = 1'))
1077
1078        c = self.environment.get_build_command() + \
1079            ['--internal',
1080             'regenerate',
1081             self.environment.get_source_dir(),
1082             self.environment.get_build_dir(),
1083             '--backend',
1084             'ninja']
1085        self.add_rule(NinjaRule('REGENERATE_BUILD',
1086                                c, [],
1087                                'Regenerating build files.',
1088                                extra='generator = 1'))
1089
1090    def add_rule_comment(self, comment):
1091        self.rules.append(comment)
1092
1093    def add_build_comment(self, comment):
1094        self.build_elements.append(comment)
1095
1096    def add_rule(self, rule):
1097        self.rules.append(rule)
1098        self.ruledict[rule.name] = rule
1099
1100    def add_build(self, build):
1101        self.build_elements.append(build)
1102
1103        if build.rulename != 'phony':
1104            # reference rule
1105            build.rule = self.ruledict[build.rulename]
1106
1107    def write_rules(self, outfile):
1108        for b in self.build_elements:
1109            if isinstance(b, NinjaBuildElement):
1110                b.count_rule_references()
1111
1112        for r in self.rules:
1113            r.write(outfile)
1114
1115    def write_builds(self, outfile):
1116        for b in ProgressBar(self.build_elements, desc='Writing build.ninja'):
1117            b.write(outfile)
1118
1119    def generate_phony(self):
1120        self.add_build_comment(NinjaComment('Phony build target, always out of date'))
1121        elem = NinjaBuildElement(self.all_outputs, 'PHONY', 'phony', '')
1122        self.add_build(elem)
1123
1124    def generate_jar_target(self, target):
1125        fname = target.get_filename()
1126        outname_rel = os.path.join(self.get_target_dir(target), fname)
1127        src_list = target.get_sources()
1128        class_list = []
1129        compiler = target.compilers['java']
1130        c = 'c'
1131        m = 'm'
1132        e = ''
1133        f = 'f'
1134        main_class = target.get_main_class()
1135        if main_class != '':
1136            e = 'e'
1137
1138        # Add possible java generated files to src list
1139        generated_sources = self.get_target_generated_sources(target)
1140        gen_src_list = []
1141        for rel_src in generated_sources.keys():
1142            dirpart, fnamepart = os.path.split(rel_src)
1143            raw_src = File(True, dirpart, fnamepart)
1144            if rel_src.endswith('.java'):
1145                gen_src_list.append(raw_src)
1146
1147        compile_args = self.determine_single_java_compile_args(target, compiler)
1148        for src in src_list + gen_src_list:
1149            plain_class_path = self.generate_single_java_compile(src, target, compiler, compile_args)
1150            class_list.append(plain_class_path)
1151        class_dep_list = [os.path.join(self.get_target_private_dir(target), i) for i in class_list]
1152        manifest_path = os.path.join(self.get_target_private_dir(target), 'META-INF', 'MANIFEST.MF')
1153        manifest_fullpath = os.path.join(self.environment.get_build_dir(), manifest_path)
1154        os.makedirs(os.path.dirname(manifest_fullpath), exist_ok=True)
1155        with open(manifest_fullpath, 'w') as manifest:
1156            if any(target.link_targets):
1157                manifest.write('Class-Path: ')
1158                cp_paths = [os.path.join(self.get_target_dir(l), l.get_filename()) for l in target.link_targets]
1159                manifest.write(' '.join(cp_paths))
1160            manifest.write('\n')
1161        jar_rule = 'java_LINKER'
1162        commands = [c + m + e + f]
1163        commands.append(manifest_path)
1164        if e != '':
1165            commands.append(main_class)
1166        commands.append(self.get_target_filename(target))
1167        # Java compilation can produce an arbitrary number of output
1168        # class files for a single source file. Thus tell jar to just
1169        # grab everything in the final package.
1170        commands += ['-C', self.get_target_private_dir(target), '.']
1171        elem = NinjaBuildElement(self.all_outputs, outname_rel, jar_rule, [])
1172        elem.add_dep(class_dep_list)
1173        elem.add_item('ARGS', commands)
1174        self.add_build(elem)
1175        # Create introspection information
1176        self.create_target_source_introspection(target, compiler, compile_args, src_list, gen_src_list)
1177
1178    def generate_cs_resource_tasks(self, target):
1179        args = []
1180        deps = []
1181        for r in target.resources:
1182            rel_sourcefile = os.path.join(self.build_to_src, target.subdir, r)
1183            if r.endswith('.resources'):
1184                a = '-resource:' + rel_sourcefile
1185            elif r.endswith('.txt') or r.endswith('.resx'):
1186                ofilebase = os.path.splitext(os.path.basename(r))[0] + '.resources'
1187                ofilename = os.path.join(self.get_target_private_dir(target), ofilebase)
1188                elem = NinjaBuildElement(self.all_outputs, ofilename, "CUSTOM_COMMAND", rel_sourcefile)
1189                elem.add_item('COMMAND', ['resgen', rel_sourcefile, ofilename])
1190                elem.add_item('DESC', 'Compiling resource {}'.format(rel_sourcefile))
1191                self.add_build(elem)
1192                deps.append(ofilename)
1193                a = '-resource:' + ofilename
1194            else:
1195                raise InvalidArguments('Unknown resource file {}.'.format(r))
1196            args.append(a)
1197        return args, deps
1198
1199    def generate_cs_target(self, target):
1200        buildtype = self.get_option_for_target('buildtype', target)
1201        fname = target.get_filename()
1202        outname_rel = os.path.join(self.get_target_dir(target), fname)
1203        src_list = target.get_sources()
1204        compiler = target.compilers['cs']
1205        rel_srcs = [os.path.normpath(s.rel_to_builddir(self.build_to_src)) for s in src_list]
1206        deps = []
1207        commands = compiler.compiler_args(target.extra_args.get('cs', []))
1208        commands += compiler.get_buildtype_args(buildtype)
1209        commands += compiler.get_optimization_args(self.get_option_for_target('optimization', target))
1210        commands += compiler.get_debug_args(self.get_option_for_target('debug', target))
1211        if isinstance(target, build.Executable):
1212            commands.append('-target:exe')
1213        elif isinstance(target, build.SharedLibrary):
1214            commands.append('-target:library')
1215        else:
1216            raise MesonException('Unknown C# target type.')
1217        (resource_args, resource_deps) = self.generate_cs_resource_tasks(target)
1218        commands += resource_args
1219        deps += resource_deps
1220        commands += compiler.get_output_args(outname_rel)
1221        for l in target.link_targets:
1222            lname = os.path.join(self.get_target_dir(l), l.get_filename())
1223            commands += compiler.get_link_args(lname)
1224            deps.append(lname)
1225        if '-g' in commands:
1226            outputs = [outname_rel, outname_rel + '.mdb']
1227        else:
1228            outputs = [outname_rel]
1229        generated_sources = self.get_target_generated_sources(target)
1230        generated_rel_srcs = []
1231        for rel_src in generated_sources.keys():
1232            if rel_src.lower().endswith('.cs'):
1233                generated_rel_srcs.append(os.path.normpath(rel_src))
1234            deps.append(os.path.normpath(rel_src))
1235
1236        for dep in target.get_external_deps():
1237            commands.extend_direct(dep.get_link_args())
1238        commands += self.build.get_project_args(compiler, target.subproject, target.for_machine)
1239        commands += self.build.get_global_args(compiler, target.for_machine)
1240
1241        elem = NinjaBuildElement(self.all_outputs, outputs, self.get_compiler_rule_name('cs', target.for_machine), rel_srcs + generated_rel_srcs)
1242        elem.add_dep(deps)
1243        elem.add_item('ARGS', commands)
1244        self.add_build(elem)
1245
1246        self.generate_generator_list_rules(target)
1247        self.create_target_source_introspection(target, compiler, commands, rel_srcs, generated_rel_srcs)
1248
1249    def determine_single_java_compile_args(self, target, compiler):
1250        args = []
1251        args += compiler.get_buildtype_args(self.get_option_for_target('buildtype', target))
1252        args += self.build.get_global_args(compiler, target.for_machine)
1253        args += self.build.get_project_args(compiler, target.subproject, target.for_machine)
1254        args += target.get_java_args()
1255        args += compiler.get_output_args(self.get_target_private_dir(target))
1256        args += target.get_classpath_args()
1257        curdir = target.get_subdir()
1258        sourcepath = os.path.join(self.build_to_src, curdir) + os.pathsep
1259        sourcepath += os.path.normpath(curdir) + os.pathsep
1260        for i in target.include_dirs:
1261            for idir in i.get_incdirs():
1262                sourcepath += os.path.join(self.build_to_src, i.curdir, idir) + os.pathsep
1263        args += ['-sourcepath', sourcepath]
1264        return args
1265
1266    def generate_single_java_compile(self, src, target, compiler, args):
1267        deps = [os.path.join(self.get_target_dir(l), l.get_filename()) for l in target.link_targets]
1268        generated_sources = self.get_target_generated_sources(target)
1269        for rel_src in generated_sources.keys():
1270            if rel_src.endswith('.java'):
1271                deps.append(rel_src)
1272        rel_src = src.rel_to_builddir(self.build_to_src)
1273        plain_class_path = src.fname[:-4] + 'class'
1274        rel_obj = os.path.join(self.get_target_private_dir(target), plain_class_path)
1275        element = NinjaBuildElement(self.all_outputs, rel_obj, self.compiler_to_rule_name(compiler), rel_src)
1276        element.add_dep(deps)
1277        element.add_item('ARGS', args)
1278        self.add_build(element)
1279        return plain_class_path
1280
1281    def generate_java_link(self):
1282        rule = 'java_LINKER'
1283        command = ['jar', '$ARGS']
1284        description = 'Creating JAR $out'
1285        self.add_rule(NinjaRule(rule, command, [], description))
1286
1287    def determine_dep_vapis(self, target):
1288        """
1289        Peek into the sources of BuildTargets we're linking with, and if any of
1290        them was built with Vala, assume that it also generated a .vapi file of
1291        the same name as the BuildTarget and return the path to it relative to
1292        the build directory.
1293        """
1294        result = OrderedSet()
1295        for dep in itertools.chain(target.link_targets, target.link_whole_targets):
1296            if not dep.is_linkable_target():
1297                continue
1298            for i in dep.sources:
1299                if hasattr(i, 'fname'):
1300                    i = i.fname
1301                if i.endswith('vala'):
1302                    vapiname = dep.vala_vapi
1303                    fullname = os.path.join(self.get_target_dir(dep), vapiname)
1304                    result.add(fullname)
1305                    break
1306        return list(result)
1307
1308    def split_vala_sources(self, t):
1309        """
1310        Splits the target's sources into .vala, .gs, .vapi, and other sources.
1311        Handles both pre-existing and generated sources.
1312
1313        Returns a tuple (vala, vapi, others) each of which is a dictionary with
1314        the keys being the path to the file (relative to the build directory)
1315        and the value being the object that generated or represents the file.
1316        """
1317        vala = OrderedDict()
1318        vapi = OrderedDict()
1319        others = OrderedDict()
1320        othersgen = OrderedDict()
1321        # Split pre-existing sources
1322        for s in t.get_sources():
1323            # BuildTarget sources are always mesonlib.File files which are
1324            # either in the source root, or generated with configure_file and
1325            # in the build root
1326            if not isinstance(s, File):
1327                msg = 'All sources in target {!r} must be of type ' \
1328                      'mesonlib.File, not {!r}'.format(t, s)
1329                raise InvalidArguments(msg)
1330            f = s.rel_to_builddir(self.build_to_src)
1331            if s.endswith(('.vala', '.gs')):
1332                srctype = vala
1333            elif s.endswith('.vapi'):
1334                srctype = vapi
1335            else:
1336                srctype = others
1337            srctype[f] = s
1338        # Split generated sources
1339        for gensrc in t.get_generated_sources():
1340            for s in gensrc.get_outputs():
1341                f = self.get_target_generated_dir(t, gensrc, s)
1342                if s.endswith(('.vala', '.gs')):
1343                    srctype = vala
1344                elif s.endswith('.vapi'):
1345                    srctype = vapi
1346                # Generated non-Vala (C/C++) sources. Won't be used for
1347                # generating the Vala compile rule below.
1348                else:
1349                    srctype = othersgen
1350                # Duplicate outputs are disastrous
1351                if f in srctype and srctype[f] is not gensrc:
1352                    msg = 'Duplicate output {0!r} from {1!r} {2!r}; ' \
1353                          'conflicts with {0!r} from {4!r} {3!r}' \
1354                          ''.format(f, type(gensrc).__name__, gensrc.name,
1355                                    srctype[f].name, type(srctype[f]).__name__)
1356                    raise InvalidArguments(msg)
1357                # Store 'somefile.vala': GeneratedList (or CustomTarget)
1358                srctype[f] = gensrc
1359        return vala, vapi, (others, othersgen)
1360
1361    def generate_vala_compile(self, target):
1362        """Vala is compiled into C. Set up all necessary build steps here."""
1363        (vala_src, vapi_src, other_src) = self.split_vala_sources(target)
1364        extra_dep_files = []
1365        if not vala_src:
1366            msg = 'Vala library {!r} has no Vala or Genie source files.'
1367            raise InvalidArguments(msg.format(target.name))
1368
1369        valac = target.compilers['vala']
1370        c_out_dir = self.get_target_private_dir(target)
1371        # C files generated by valac
1372        vala_c_src = []
1373        # Files generated by valac
1374        valac_outputs = []
1375        # All sources that are passed to valac on the commandline
1376        all_files = list(vapi_src.keys())
1377        # Passed as --basedir
1378        srcbasedir = os.path.join(self.build_to_src, target.get_subdir())
1379        for (vala_file, gensrc) in vala_src.items():
1380            all_files.append(vala_file)
1381            # Figure out where the Vala compiler will write the compiled C file
1382            #
1383            # If the Vala file is in a subdir of the build dir (in our case
1384            # because it was generated/built by something else), and is also
1385            # a subdir of --basedir (because the builddir is in the source
1386            # tree, and the target subdir is the source root), the subdir
1387            # components from the source root till the private builddir will be
1388            # duplicated inside the private builddir. Otherwise, just the
1389            # basename will be used.
1390            #
1391            # If the Vala file is outside the build directory, the paths from
1392            # the --basedir till the subdir will be duplicated inside the
1393            # private builddir.
1394            if isinstance(gensrc, (build.CustomTarget, build.GeneratedList)) or gensrc.is_built:
1395                vala_c_file = os.path.splitext(os.path.basename(vala_file))[0] + '.c'
1396                # Check if the vala file is in a subdir of --basedir
1397                abs_srcbasedir = os.path.join(self.environment.get_source_dir(), target.get_subdir())
1398                abs_vala_file = os.path.join(self.environment.get_build_dir(), vala_file)
1399                if PurePath(os.path.commonpath((abs_srcbasedir, abs_vala_file))) == PurePath(abs_srcbasedir):
1400                    vala_c_subdir = PurePath(abs_vala_file).parent.relative_to(abs_srcbasedir)
1401                    vala_c_file = os.path.join(str(vala_c_subdir), vala_c_file)
1402            else:
1403                path_to_target = os.path.join(self.build_to_src, target.get_subdir())
1404                if vala_file.startswith(path_to_target):
1405                    vala_c_file = os.path.splitext(os.path.relpath(vala_file, path_to_target))[0] + '.c'
1406                else:
1407                    vala_c_file = os.path.splitext(os.path.basename(vala_file))[0] + '.c'
1408            # All this will be placed inside the c_out_dir
1409            vala_c_file = os.path.join(c_out_dir, vala_c_file)
1410            vala_c_src.append(vala_c_file)
1411            valac_outputs.append(vala_c_file)
1412
1413        args = self.generate_basic_compiler_args(target, valac)
1414        args += valac.get_colorout_args(self.environment.coredata.base_options.get('b_colorout').value)
1415        # Tell Valac to output everything in our private directory. Sadly this
1416        # means it will also preserve the directory components of Vala sources
1417        # found inside the build tree (generated sources).
1418        args += ['--directory', c_out_dir]
1419        args += ['--basedir', srcbasedir]
1420        if target.is_linkable_target():
1421            # Library name
1422            args += ['--library', target.name]
1423            # Outputted header
1424            hname = os.path.join(self.get_target_dir(target), target.vala_header)
1425            args += ['--header', hname]
1426            if self.is_unity(target):
1427                # Without this the declarations will get duplicated in the .c
1428                # files and cause a build failure when all of them are
1429                # #include-d in one .c file.
1430                # https://github.com/mesonbuild/meson/issues/1969
1431                args += ['--use-header']
1432            valac_outputs.append(hname)
1433            # Outputted vapi file
1434            vapiname = os.path.join(self.get_target_dir(target), target.vala_vapi)
1435            # Force valac to write the vapi and gir files in the target build dir.
1436            # Without this, it will write it inside c_out_dir
1437            args += ['--vapi', os.path.join('..', target.vala_vapi)]
1438            valac_outputs.append(vapiname)
1439            target.outputs += [target.vala_header, target.vala_vapi]
1440            # Install header and vapi to default locations if user requests this
1441            if len(target.install_dir) > 1 and target.install_dir[1] is True:
1442                target.install_dir[1] = self.environment.get_includedir()
1443            if len(target.install_dir) > 2 and target.install_dir[2] is True:
1444                target.install_dir[2] = os.path.join(self.environment.get_datadir(), 'vala', 'vapi')
1445            # Generate GIR if requested
1446            if isinstance(target.vala_gir, str):
1447                girname = os.path.join(self.get_target_dir(target), target.vala_gir)
1448                args += ['--gir', os.path.join('..', target.vala_gir)]
1449                valac_outputs.append(girname)
1450                target.outputs.append(target.vala_gir)
1451                # Install GIR to default location if requested by user
1452                if len(target.install_dir) > 3 and target.install_dir[3] is True:
1453                    target.install_dir[3] = os.path.join(self.environment.get_datadir(), 'gir-1.0')
1454        # Detect gresources and add --gresources arguments for each
1455        for gensrc in other_src[1].values():
1456            if isinstance(gensrc, modules.GResourceTarget):
1457                gres_xml, = self.get_custom_target_sources(gensrc)
1458                args += ['--gresources=' + gres_xml]
1459        extra_args = []
1460
1461        for a in target.extra_args.get('vala', []):
1462            if isinstance(a, File):
1463                relname = a.rel_to_builddir(self.build_to_src)
1464                extra_dep_files.append(relname)
1465                extra_args.append(relname)
1466            else:
1467                extra_args.append(a)
1468        dependency_vapis = self.determine_dep_vapis(target)
1469        extra_dep_files += dependency_vapis
1470        args += extra_args
1471        element = NinjaBuildElement(self.all_outputs, valac_outputs,
1472                                    self.compiler_to_rule_name(valac),
1473                                    all_files + dependency_vapis)
1474        element.add_item('ARGS', args)
1475        element.add_dep(extra_dep_files)
1476        self.add_build(element)
1477        self.create_target_source_introspection(target, valac, args, all_files, [])
1478        return other_src[0], other_src[1], vala_c_src
1479
1480    def generate_rust_target(self, target):
1481        rustc = target.compilers['rust']
1482        # Rust compiler takes only the main file as input and
1483        # figures out what other files are needed via import
1484        # statements and magic.
1485        main_rust_file = None
1486        for i in target.get_sources():
1487            if not rustc.can_compile(i):
1488                raise InvalidArguments('Rust target {} contains a non-rust source file.'.format(target.get_basename()))
1489            if main_rust_file is None:
1490                main_rust_file = i.rel_to_builddir(self.build_to_src)
1491        if main_rust_file is None:
1492            raise RuntimeError('A Rust target has no Rust sources. This is weird. Also a bug. Please report')
1493        target_name = os.path.join(target.subdir, target.get_filename())
1494        args = ['--crate-type']
1495        if isinstance(target, build.Executable):
1496            cratetype = 'bin'
1497        elif hasattr(target, 'rust_crate_type'):
1498            cratetype = target.rust_crate_type
1499        elif isinstance(target, build.SharedLibrary):
1500            cratetype = 'dylib'
1501        elif isinstance(target, build.StaticLibrary):
1502            cratetype = 'rlib'
1503        else:
1504            raise InvalidArguments('Unknown target type for rustc.')
1505        args.append(cratetype)
1506
1507        # If we're dynamically linking, add those arguments
1508        #
1509        # Rust is super annoying, calling -C link-arg foo does not work, it has
1510        # to be -C link-arg=foo
1511        if cratetype in {'bin', 'dylib'}:
1512            for a in rustc.linker.get_always_args():
1513                args += ['-C', 'link-arg={}'.format(a)]
1514
1515        args += ['--crate-name', target.name]
1516        args += rustc.get_buildtype_args(self.get_option_for_target('buildtype', target))
1517        args += rustc.get_debug_args(self.get_option_for_target('debug', target))
1518        args += rustc.get_optimization_args(self.get_option_for_target('optimization', target))
1519        args += self.build.get_global_args(rustc, target.for_machine)
1520        args += self.build.get_project_args(rustc, target.subproject, target.for_machine)
1521        depfile = os.path.join(target.subdir, target.name + '.d')
1522        args += ['--emit', 'dep-info={}'.format(depfile), '--emit', 'link']
1523        args += target.get_extra_args('rust')
1524        args += ['-o', os.path.join(target.subdir, target.get_filename())]
1525        orderdeps = [os.path.join(t.subdir, t.get_filename()) for t in target.link_targets]
1526        linkdirs = OrderedDict()
1527        for d in target.link_targets:
1528            linkdirs[d.subdir] = True
1529            # specify `extern CRATE_NAME=OUTPUT_FILE` for each Rust
1530            # dependency, so that collisions with libraries in rustc's
1531            # sysroot don't cause ambiguity
1532            args += ['--extern', '{}={}'.format(d.name, os.path.join(d.subdir, d.filename))]
1533        for d in linkdirs.keys():
1534            if d == '':
1535                d = '.'
1536            args += ['-L', d]
1537        has_shared_deps = False
1538        for dep in target.get_dependencies():
1539            if isinstance(dep, build.SharedLibrary):
1540                has_shared_deps = True
1541        if isinstance(target, build.SharedLibrary) or has_shared_deps:
1542            # add prefer-dynamic if any of the Rust libraries we link
1543            # against are dynamic, otherwise we'll end up with
1544            # multiple implementations of crates
1545            args += ['-C', 'prefer-dynamic']
1546
1547            # build the usual rpath arguments as well...
1548
1549            # Set runtime-paths so we can run executables without needing to set
1550            # LD_LIBRARY_PATH, etc in the environment. Doesn't work on Windows.
1551            if has_path_sep(target.name):
1552                # Target names really should not have slashes in them, but
1553                # unfortunately we did not check for that and some downstream projects
1554                # now have them. Once slashes are forbidden, remove this bit.
1555                target_slashname_workaround_dir = os.path.join(os.path.dirname(target.name),
1556                                                               self.get_target_dir(target))
1557            else:
1558                target_slashname_workaround_dir = self.get_target_dir(target)
1559            (rpath_args, target.rpath_dirs_to_remove) = \
1560                         rustc.build_rpath_args(self.environment,
1561                                                self.environment.get_build_dir(),
1562                                                target_slashname_workaround_dir,
1563                                                self.determine_rpath_dirs(target),
1564                                                target.build_rpath,
1565                                                target.install_rpath)
1566            # ... but then add rustc's sysroot to account for rustup
1567            # installations
1568            for rpath_arg in rpath_args:
1569                args += ['-C', 'link-arg=' + rpath_arg + ':' + os.path.join(rustc.get_sysroot(), 'lib')]
1570        compiler_name = self.get_compiler_rule_name('rust', target.for_machine)
1571        element = NinjaBuildElement(self.all_outputs, target_name, compiler_name, main_rust_file)
1572        if len(orderdeps) > 0:
1573            element.add_orderdep(orderdeps)
1574        element.add_item('ARGS', args)
1575        element.add_item('targetdep', depfile)
1576        element.add_item('cratetype', cratetype)
1577        self.add_build(element)
1578        if isinstance(target, build.SharedLibrary):
1579            self.generate_shsym(target)
1580        self.create_target_source_introspection(target, rustc, args, [main_rust_file], [])
1581
1582    @staticmethod
1583    def get_rule_suffix(for_machine: MachineChoice) -> str:
1584        return PerMachine('_FOR_BUILD', '')[for_machine]
1585
1586    @classmethod
1587    def get_compiler_rule_name(cls, lang: str, for_machine: MachineChoice) -> str:
1588        return '{}_COMPILER{}'.format(lang, cls.get_rule_suffix(for_machine))
1589
1590    @classmethod
1591    def get_pch_rule_name(cls, lang: str, for_machine: MachineChoice) -> str:
1592        return '{}_PCH{}'.format(lang, cls.get_rule_suffix(for_machine))
1593
1594    @classmethod
1595    def compiler_to_rule_name(cls, compiler: Compiler) -> str:
1596        return cls.get_compiler_rule_name(compiler.get_language(), compiler.for_machine)
1597
1598    @classmethod
1599    def compiler_to_pch_rule_name(cls, compiler: Compiler) -> str:
1600        return cls.get_pch_rule_name(compiler.get_language(), compiler.for_machine)
1601
1602    def swift_module_file_name(self, target):
1603        return os.path.join(self.get_target_private_dir(target),
1604                            self.target_swift_modulename(target) + '.swiftmodule')
1605
1606    def target_swift_modulename(self, target):
1607        return target.name
1608
1609    def is_swift_target(self, target):
1610        for s in target.sources:
1611            if s.endswith('swift'):
1612                return True
1613        return False
1614
1615    def determine_swift_dep_modules(self, target):
1616        result = []
1617        for l in target.link_targets:
1618            if self.is_swift_target(l):
1619                result.append(self.swift_module_file_name(l))
1620        return result
1621
1622    def determine_swift_dep_dirs(self, target):
1623        result = []
1624        for l in target.link_targets:
1625            result.append(self.get_target_private_dir_abs(l))
1626        return result
1627
1628    def get_swift_link_deps(self, target):
1629        result = []
1630        for l in target.link_targets:
1631            result.append(self.get_target_filename(l))
1632        return result
1633
1634    def split_swift_generated_sources(self, target):
1635        all_srcs = self.get_target_generated_sources(target)
1636        srcs = []
1637        others = []
1638        for i in all_srcs:
1639            if i.endswith('.swift'):
1640                srcs.append(i)
1641            else:
1642                others.append(i)
1643        return srcs, others
1644
1645    def generate_swift_target(self, target):
1646        module_name = self.target_swift_modulename(target)
1647        swiftc = target.compilers['swift']
1648        abssrc = []
1649        relsrc = []
1650        abs_headers = []
1651        header_imports = []
1652        for i in target.get_sources():
1653            if swiftc.can_compile(i):
1654                rels = i.rel_to_builddir(self.build_to_src)
1655                abss = os.path.normpath(os.path.join(self.environment.get_build_dir(), rels))
1656                relsrc.append(rels)
1657                abssrc.append(abss)
1658            elif self.environment.is_header(i):
1659                relh = i.rel_to_builddir(self.build_to_src)
1660                absh = os.path.normpath(os.path.join(self.environment.get_build_dir(), relh))
1661                abs_headers.append(absh)
1662                header_imports += swiftc.get_header_import_args(absh)
1663            else:
1664                raise InvalidArguments('Swift target {} contains a non-swift source file.'.format(target.get_basename()))
1665        os.makedirs(self.get_target_private_dir_abs(target), exist_ok=True)
1666        compile_args = swiftc.get_compile_only_args()
1667        compile_args += swiftc.get_optimization_args(self.get_option_for_target('optimization', target))
1668        compile_args += swiftc.get_debug_args(self.get_option_for_target('debug', target))
1669        compile_args += swiftc.get_module_args(module_name)
1670        compile_args += self.build.get_project_args(swiftc, target.subproject, target.for_machine)
1671        compile_args += self.build.get_global_args(swiftc, target.for_machine)
1672        for i in reversed(target.get_include_dirs()):
1673            basedir = i.get_curdir()
1674            for d in i.get_incdirs():
1675                if d not in ('', '.'):
1676                    expdir = os.path.join(basedir, d)
1677                else:
1678                    expdir = basedir
1679                srctreedir = os.path.normpath(os.path.join(self.environment.get_build_dir(), self.build_to_src, expdir))
1680                sargs = swiftc.get_include_args(srctreedir)
1681                compile_args += sargs
1682        link_args = swiftc.get_output_args(os.path.join(self.environment.get_build_dir(), self.get_target_filename(target)))
1683        link_args += self.build.get_project_link_args(swiftc, target.subproject, target.for_machine)
1684        link_args += self.build.get_global_link_args(swiftc, target.for_machine)
1685        rundir = self.get_target_private_dir(target)
1686        out_module_name = self.swift_module_file_name(target)
1687        in_module_files = self.determine_swift_dep_modules(target)
1688        abs_module_dirs = self.determine_swift_dep_dirs(target)
1689        module_includes = []
1690        for x in abs_module_dirs:
1691            module_includes += swiftc.get_include_args(x)
1692        link_deps = self.get_swift_link_deps(target)
1693        abs_link_deps = [os.path.join(self.environment.get_build_dir(), x) for x in link_deps]
1694        for d in target.link_targets:
1695            reldir = self.get_target_dir(d)
1696            if reldir == '':
1697                reldir = '.'
1698            link_args += ['-L', os.path.normpath(os.path.join(self.environment.get_build_dir(), reldir))]
1699        (rel_generated, _) = self.split_swift_generated_sources(target)
1700        abs_generated = [os.path.join(self.environment.get_build_dir(), x) for x in rel_generated]
1701        # We need absolute paths because swiftc needs to be invoked in a subdir
1702        # and this is the easiest way about it.
1703        objects = [] # Relative to swift invocation dir
1704        rel_objects = [] # Relative to build.ninja
1705        for i in abssrc + abs_generated:
1706            base = os.path.basename(i)
1707            oname = os.path.splitext(base)[0] + '.o'
1708            objects.append(oname)
1709            rel_objects.append(os.path.join(self.get_target_private_dir(target), oname))
1710
1711        rulename = self.get_compiler_rule_name('swift', target.for_machine)
1712
1713        # Swiftc does not seem to be able to emit objects and module files in one go.
1714        elem = NinjaBuildElement(self.all_outputs, rel_objects, rulename, abssrc)
1715        elem.add_dep(in_module_files + rel_generated)
1716        elem.add_dep(abs_headers)
1717        elem.add_item('ARGS', compile_args + header_imports + abs_generated + module_includes)
1718        elem.add_item('RUNDIR', rundir)
1719        self.add_build(elem)
1720        elem = NinjaBuildElement(self.all_outputs, out_module_name,
1721                                 self.get_compiler_rule_name('swift', target.for_machine),
1722                                 abssrc)
1723        elem.add_dep(in_module_files + rel_generated)
1724        elem.add_item('ARGS', compile_args + abs_generated + module_includes + swiftc.get_mod_gen_args())
1725        elem.add_item('RUNDIR', rundir)
1726        self.add_build(elem)
1727        if isinstance(target, build.StaticLibrary):
1728            elem = self.generate_link(target, self.get_target_filename(target),
1729                                      rel_objects, self.build.static_linker[target.for_machine])
1730            self.add_build(elem)
1731        elif isinstance(target, build.Executable):
1732            elem = NinjaBuildElement(self.all_outputs, self.get_target_filename(target), rulename, [])
1733            elem.add_dep(rel_objects)
1734            elem.add_dep(link_deps)
1735            elem.add_item('ARGS', link_args + swiftc.get_std_exe_link_args() + objects + abs_link_deps)
1736            elem.add_item('RUNDIR', rundir)
1737            self.add_build(elem)
1738        else:
1739            raise MesonException('Swift supports only executable and static library targets.')
1740        # Introspection information
1741        self.create_target_source_introspection(target, swiftc, compile_args + header_imports + module_includes, relsrc, rel_generated)
1742
1743    def generate_static_link_rules(self):
1744        num_pools = self.environment.coredata.backend_options['backend_max_links'].value
1745        if 'java' in self.environment.coredata.compilers.host:
1746            self.generate_java_link()
1747        for for_machine in MachineChoice:
1748            static_linker = self.build.static_linker[for_machine]
1749            if static_linker is None:
1750                return
1751            rule = 'STATIC_LINKER{}'.format(self.get_rule_suffix(for_machine))
1752            cmdlist = []
1753            args = ['$in']
1754            # FIXME: Must normalize file names with pathlib.Path before writing
1755            #        them out to fix this properly on Windows. See:
1756            # https://github.com/mesonbuild/meson/issues/1517
1757            # https://github.com/mesonbuild/meson/issues/1526
1758            if isinstance(static_linker, ArLinker) and not mesonlib.is_windows():
1759                # `ar` has no options to overwrite archives. It always appends,
1760                # which is never what we want. Delete an existing library first if
1761                # it exists. https://github.com/mesonbuild/meson/issues/1355
1762                cmdlist = execute_wrapper + [c.format('$out') for c in rmfile_prefix]
1763            cmdlist += static_linker.get_exelist()
1764            cmdlist += ['$LINK_ARGS']
1765            cmdlist += NinjaCommandArg.list(static_linker.get_output_args('$out'), Quoting.none)
1766            description = 'Linking static target $out'
1767            if num_pools > 0:
1768                pool = 'pool = link_pool'
1769            else:
1770                pool = None
1771            self.add_rule(NinjaRule(rule, cmdlist, args, description,
1772                                    rspable=static_linker.can_linker_accept_rsp(),
1773                                    rspfile_quote_style='cl' if isinstance(static_linker, VisualStudioLinker) else 'gcc',
1774                                    extra=pool))
1775
1776    def generate_dynamic_link_rules(self):
1777        num_pools = self.environment.coredata.backend_options['backend_max_links'].value
1778        for for_machine in MachineChoice:
1779            complist = self.environment.coredata.compilers[for_machine]
1780            for langname, compiler in complist.items():
1781                if langname == 'java' \
1782                        or langname == 'vala' \
1783                        or langname == 'rust' \
1784                        or langname == 'cs':
1785                    continue
1786                rule = '{}_LINKER{}'.format(langname, self.get_rule_suffix(for_machine))
1787                command = compiler.get_linker_exelist()
1788                args = ['$ARGS'] + NinjaCommandArg.list(compiler.get_linker_output_args('$out'), Quoting.none) + ['$in', '$LINK_ARGS']
1789                description = 'Linking target $out'
1790                if num_pools > 0:
1791                    pool = 'pool = link_pool'
1792                else:
1793                    pool = None
1794                self.add_rule(NinjaRule(rule, command, args, description,
1795                                        rspable=compiler.can_linker_accept_rsp(),
1796                                        rspfile_quote_style='cl' if (compiler.get_argument_syntax() == 'msvc' or
1797                                                                     isinstance(compiler, DmdDCompiler)) else 'gcc',
1798                                        extra=pool))
1799
1800        args = self.environment.get_build_command() + \
1801            ['--internal',
1802             'symbolextractor',
1803             self.environment.get_build_dir(),
1804             '$in',
1805             '$IMPLIB',
1806             '$out']
1807        symrule = 'SHSYM'
1808        symcmd = args + ['$CROSS']
1809        syndesc = 'Generating symbol file $out'
1810        synstat = 'restat = 1'
1811        self.add_rule(NinjaRule(symrule, symcmd, [], syndesc, extra=synstat))
1812
1813    def generate_java_compile_rule(self, compiler):
1814        rule = self.compiler_to_rule_name(compiler)
1815        command = compiler.get_exelist() + ['$ARGS', '$in']
1816        description = 'Compiling Java object $in'
1817        self.add_rule(NinjaRule(rule, command, [], description))
1818
1819    def generate_cs_compile_rule(self, compiler):
1820        rule = self.compiler_to_rule_name(compiler)
1821        command = compiler.get_exelist()
1822        args = ['$ARGS', '$in']
1823        description = 'Compiling C Sharp target $out'
1824        self.add_rule(NinjaRule(rule, command, args, description,
1825                                rspable=mesonlib.is_windows(),
1826                                rspfile_quote_style='cl' if isinstance(compiler, VisualStudioCsCompiler) else 'gcc'))
1827
1828    def generate_vala_compile_rules(self, compiler):
1829        rule = self.compiler_to_rule_name(compiler)
1830        command = compiler.get_exelist() + ['$ARGS', '$in']
1831        description = 'Compiling Vala source $in'
1832        self.add_rule(NinjaRule(rule, command, [], description, extra='restat = 1'))
1833
1834    def generate_rust_compile_rules(self, compiler):
1835        rule = self.compiler_to_rule_name(compiler)
1836        command = compiler.get_exelist() + ['$ARGS', '$in']
1837        description = 'Compiling Rust source $in'
1838        depfile = '$targetdep'
1839        depstyle = 'gcc'
1840        self.add_rule(NinjaRule(rule, command, [], description, deps=depstyle,
1841                                depfile=depfile))
1842
1843    def generate_swift_compile_rules(self, compiler):
1844        rule = self.compiler_to_rule_name(compiler)
1845        full_exe = self.environment.get_build_command() + [
1846            '--internal',
1847            'dirchanger',
1848            '$RUNDIR',
1849        ]
1850        invoc = full_exe + compiler.get_exelist()
1851        command = invoc + ['$ARGS', '$in']
1852        description = 'Compiling Swift source $in'
1853        self.add_rule(NinjaRule(rule, command, [], description))
1854
1855    def generate_fortran_dep_hack(self, crstr):
1856        rule = 'FORTRAN_DEP_HACK{}'.format(crstr)
1857        if mesonlib.is_windows():
1858            cmd = ['cmd', '/C']
1859        else:
1860            cmd = ['true']
1861        self.add_rule_comment(NinjaComment('''Workaround for these issues:
1862https://groups.google.com/forum/#!topic/ninja-build/j-2RfBIOd_8
1863https://gcc.gnu.org/bugzilla/show_bug.cgi?id=47485'''))
1864        self.add_rule(NinjaRule(rule, cmd, [], 'Dep hack', extra='restat = 1'))
1865
1866    def generate_llvm_ir_compile_rule(self, compiler):
1867        if self.created_llvm_ir_rule[compiler.for_machine]:
1868            return
1869        rule = self.get_compiler_rule_name('llvm_ir', compiler.for_machine)
1870        command = compiler.get_exelist()
1871        args = ['$ARGS'] + NinjaCommandArg.list(compiler.get_output_args('$out'), Quoting.none) + compiler.get_compile_only_args() + ['$in']
1872        description = 'Compiling LLVM IR object $in'
1873        self.add_rule(NinjaRule(rule, command, args, description,
1874                                rspable=compiler.can_linker_accept_rsp()))
1875        self.created_llvm_ir_rule[compiler.for_machine] = True
1876
1877    def generate_compile_rule_for(self, langname, compiler):
1878        if langname == 'java':
1879            if self.environment.machines.matches_build_machine(compiler.for_machine):
1880                self.generate_java_compile_rule(compiler)
1881            return
1882        if langname == 'cs':
1883            if self.environment.machines.matches_build_machine(compiler.for_machine):
1884                self.generate_cs_compile_rule(compiler)
1885            return
1886        if langname == 'vala':
1887            self.generate_vala_compile_rules(compiler)
1888            return
1889        if langname == 'rust':
1890            self.generate_rust_compile_rules(compiler)
1891            return
1892        if langname == 'swift':
1893            if self.environment.machines.matches_build_machine(compiler.for_machine):
1894                self.generate_swift_compile_rules(compiler)
1895            return
1896        crstr = self.get_rule_suffix(compiler.for_machine)
1897        if langname == 'fortran':
1898            self.generate_fortran_dep_hack(crstr)
1899        rule = self.get_compiler_rule_name(langname, compiler.for_machine)
1900        depargs = NinjaCommandArg.list(compiler.get_dependency_gen_args('$out', '$DEPFILE'), Quoting.none)
1901        command = compiler.get_exelist()
1902        args = ['$ARGS'] + depargs + NinjaCommandArg.list(compiler.get_output_args('$out'), Quoting.none) + compiler.get_compile_only_args() + ['$in']
1903        description = 'Compiling {} object $out'.format(compiler.get_display_language())
1904        if isinstance(compiler, VisualStudioLikeCompiler):
1905            deps = 'msvc'
1906            depfile = None
1907        else:
1908            deps = 'gcc'
1909            depfile = '$DEPFILE'
1910        self.add_rule(NinjaRule(rule, command, args, description,
1911                                rspable=compiler.can_linker_accept_rsp(),
1912                                rspfile_quote_style='cl' if (compiler.get_argument_syntax() == 'msvc' or
1913                                                             isinstance(compiler, DmdDCompiler)) else 'gcc',
1914                                deps=deps, depfile=depfile))
1915
1916    def generate_pch_rule_for(self, langname, compiler):
1917        if langname != 'c' and langname != 'cpp':
1918            return
1919        rule = self.compiler_to_pch_rule_name(compiler)
1920        depargs = compiler.get_dependency_gen_args('$out', '$DEPFILE')
1921
1922        if isinstance(compiler, VisualStudioLikeCompiler):
1923            output = []
1924        else:
1925            output = NinjaCommandArg.list(compiler.get_output_args('$out'), Quoting.none)
1926        command = compiler.get_exelist() + ['$ARGS'] + depargs + output + compiler.get_compile_only_args() + ['$in']
1927        description = 'Precompiling header $in'
1928        if isinstance(compiler, VisualStudioLikeCompiler):
1929            deps = 'msvc'
1930            depfile = None
1931        else:
1932            deps = 'gcc'
1933            depfile = '$DEPFILE'
1934        self.add_rule(NinjaRule(rule, command, [], description, deps=deps,
1935                                depfile=depfile))
1936
1937    def generate_compile_rules(self):
1938        for for_machine in MachineChoice:
1939            clist = self.environment.coredata.compilers[for_machine]
1940            for langname, compiler in clist.items():
1941                if compiler.get_id() == 'clang':
1942                    self.generate_llvm_ir_compile_rule(compiler)
1943                self.generate_compile_rule_for(langname, compiler)
1944                self.generate_pch_rule_for(langname, compiler)
1945
1946    def generate_generator_list_rules(self, target):
1947        # CustomTargets have already written their rules and
1948        # CustomTargetIndexes don't actually get generated, so write rules for
1949        # GeneratedLists here
1950        for genlist in target.get_generated_sources():
1951            if isinstance(genlist, (build.CustomTarget, build.CustomTargetIndex)):
1952                continue
1953            self.generate_genlist_for_target(genlist, target)
1954
1955    def replace_paths(self, target, args, override_subdir=None):
1956        if override_subdir:
1957            source_target_dir = os.path.join(self.build_to_src, override_subdir)
1958        else:
1959            source_target_dir = self.get_target_source_dir(target)
1960        relout = self.get_target_private_dir(target)
1961        args = [x.replace("@SOURCE_DIR@", self.build_to_src).replace("@BUILD_DIR@", relout)
1962                for x in args]
1963        args = [x.replace("@CURRENT_SOURCE_DIR@", source_target_dir) for x in args]
1964        args = [x.replace("@SOURCE_ROOT@", self.build_to_src).replace("@BUILD_ROOT@", '.')
1965                for x in args]
1966        args = [x.replace('\\', '/') for x in args]
1967        return args
1968
1969    def generate_genlist_for_target(self, genlist, target):
1970        generator = genlist.get_generator()
1971        subdir = genlist.subdir
1972        exe = generator.get_exe()
1973        exe_arr = self.build_target_to_cmd_array(exe)
1974        infilelist = genlist.get_inputs()
1975        outfilelist = genlist.get_outputs()
1976        extra_dependencies = self.get_custom_target_depend_files(genlist)
1977        for i in range(len(infilelist)):
1978            curfile = infilelist[i]
1979            if len(generator.outputs) == 1:
1980                sole_output = os.path.join(self.get_target_private_dir(target), outfilelist[i])
1981            else:
1982                sole_output = '{}'.format(curfile)
1983            infilename = curfile.rel_to_builddir(self.build_to_src)
1984            base_args = generator.get_arglist(infilename)
1985            outfiles = genlist.get_outputs_for(curfile)
1986            outfiles = [os.path.join(self.get_target_private_dir(target), of) for of in outfiles]
1987            if generator.depfile is None:
1988                rulename = 'CUSTOM_COMMAND'
1989                args = base_args
1990            else:
1991                rulename = 'CUSTOM_COMMAND_DEP'
1992                depfilename = generator.get_dep_outname(infilename)
1993                depfile = os.path.join(self.get_target_private_dir(target), depfilename)
1994                args = [x.replace('@DEPFILE@', depfile) for x in base_args]
1995            args = [x.replace("@INPUT@", infilename).replace('@OUTPUT@', sole_output)
1996                    for x in args]
1997            args = self.replace_outputs(args, self.get_target_private_dir(target), outfilelist)
1998            # We have consumed output files, so drop them from the list of remaining outputs.
1999            if len(generator.outputs) > 1:
2000                outfilelist = outfilelist[len(generator.outputs):]
2001            args = self.replace_paths(target, args, override_subdir=subdir)
2002            cmdlist = exe_arr + self.replace_extra_args(args, genlist)
2003            meson_exe_cmd = self.as_meson_exe_cmdline('generator ' + cmdlist[0],
2004                                                      cmdlist[0], cmdlist[1:],
2005                                                      capture=outfiles[0] if generator.capture else None)
2006            if meson_exe_cmd:
2007                cmdlist = meson_exe_cmd
2008            abs_pdir = os.path.join(self.environment.get_build_dir(), self.get_target_dir(target))
2009            os.makedirs(abs_pdir, exist_ok=True)
2010
2011            elem = NinjaBuildElement(self.all_outputs, outfiles, rulename, infilename)
2012            elem.add_dep([self.get_target_filename(x) for x in generator.depends])
2013            if generator.depfile is not None:
2014                elem.add_item('DEPFILE', depfile)
2015            if len(extra_dependencies) > 0:
2016                elem.add_dep(extra_dependencies)
2017            if len(generator.outputs) == 1:
2018                elem.add_item('DESC', 'Generating {!r}.'.format(sole_output))
2019            else:
2020                # since there are multiple outputs, we log the source that caused the rebuild
2021                elem.add_item('DESC', 'Generating source from {!r}.'.format(sole_output))
2022            if isinstance(exe, build.BuildTarget):
2023                elem.add_dep(self.get_target_filename(exe))
2024            elem.add_item('COMMAND', cmdlist)
2025            self.add_build(elem)
2026
2027    def scan_fortran_module_outputs(self, target):
2028        """
2029        Find all module and submodule made available in a Fortran code file.
2030        """
2031        compiler = None
2032        # TODO other compilers
2033        for lang, c in self.environment.coredata.compilers.host.items():
2034            if lang == 'fortran':
2035                compiler = c
2036                break
2037        if compiler is None:
2038            self.fortran_deps[target.get_basename()] = {}
2039            return
2040
2041        modre = re.compile(FORTRAN_MODULE_PAT, re.IGNORECASE)
2042        submodre = re.compile(FORTRAN_SUBMOD_PAT, re.IGNORECASE)
2043        module_files = {}
2044        submodule_files = {}
2045        for s in target.get_sources():
2046            # FIXME, does not work for Fortran sources generated by
2047            # custom_target() and generator() as those are run after
2048            # the configuration (configure_file() is OK)
2049            if not compiler.can_compile(s):
2050                continue
2051            filename = s.absolute_path(self.environment.get_source_dir(),
2052                                       self.environment.get_build_dir())
2053            # Fortran keywords must be ASCII.
2054            with open(filename, encoding='ascii', errors='ignore') as f:
2055                for line in f:
2056                    modmatch = modre.match(line)
2057                    if modmatch is not None:
2058                        modname = modmatch.group(1).lower()
2059                        if modname in module_files:
2060                            raise InvalidArguments(
2061                                'Namespace collision: module {} defined in '
2062                                'two files {} and {}.'.format(modname, module_files[modname], s))
2063                        module_files[modname] = s
2064                    else:
2065                        submodmatch = submodre.match(line)
2066                        if submodmatch is not None:
2067                            # '_' is arbitrarily used to distinguish submod from mod.
2068                            parents = submodmatch.group(1).lower().split(':')
2069                            submodname = parents[0] + '_' + submodmatch.group(2).lower()
2070
2071                            if submodname in submodule_files:
2072                                raise InvalidArguments(
2073                                    'Namespace collision: submodule {} defined in '
2074                                    'two files {} and {}.'.format(submodname, submodule_files[submodname], s))
2075                            submodule_files[submodname] = s
2076
2077        self.fortran_deps[target.get_basename()] = {**module_files, **submodule_files}
2078
2079    def get_fortran_deps(self, compiler: FortranCompiler, src: Path, target) -> T.List[str]:
2080        """
2081        Find all module and submodule needed by a Fortran target
2082        """
2083
2084        dirname = Path(self.get_target_private_dir(target))
2085        tdeps = self.fortran_deps[target.get_basename()]
2086        srcdir = Path(self.source_dir)
2087
2088        mod_files = _scan_fortran_file_deps(src, srcdir, dirname, tdeps, compiler)
2089        return mod_files
2090
2091    def get_cross_stdlib_args(self, target, compiler):
2092        if self.environment.machines.matches_build_machine(target.for_machine):
2093            return []
2094        if not self.environment.properties.host.has_stdlib(compiler.language):
2095            return []
2096        return compiler.get_no_stdinc_args()
2097
2098    def get_compile_debugfile_args(self, compiler, target, objfile):
2099        # The way MSVC uses PDB files is documented exactly nowhere so
2100        # the following is what we have been able to decipher via
2101        # reverse engineering.
2102        #
2103        # Each object file gets the path of its PDB file written
2104        # inside it.  This can be either the final PDB (for, say,
2105        # foo.exe) or an object pdb (for foo.obj). If the former, then
2106        # each compilation step locks the pdb file for writing, which
2107        # is a bottleneck and object files from one target can not be
2108        # used in a different target. The latter seems to be the
2109        # sensible one (and what Unix does) but there is a catch.  If
2110        # you try to use precompiled headers MSVC will error out
2111        # because both source and pch pdbs go in the same file and
2112        # they must be the same.
2113        #
2114        # This means:
2115        #
2116        # - pch files must be compiled anew for every object file (negating
2117        #   the entire point of having them in the first place)
2118        # - when using pch, output must go to the target pdb
2119        #
2120        # Since both of these are broken in some way, use the one that
2121        # works for each target. This unfortunately means that you
2122        # can't combine pch and object extraction in a single target.
2123        #
2124        # PDB files also lead to filename collisions. A target foo.exe
2125        # has a corresponding foo.pdb. A shared library foo.dll _also_
2126        # has pdb file called foo.pdb. So will a static library
2127        # foo.lib, which clobbers both foo.pdb _and_ the dll file's
2128        # export library called foo.lib (by default, currently we name
2129        # them libfoo.a to avoidt this issue). You can give the files
2130        # unique names such as foo_exe.pdb but VC also generates a
2131        # bunch of other files which take their names from the target
2132        # basename (i.e. "foo") and stomp on each other.
2133        #
2134        # CMake solves this problem by doing two things. First of all
2135        # static libraries do not generate pdb files at
2136        # all. Presumably you don't need them and VC is smart enough
2137        # to look up the original data when linking (speculation, not
2138        # tested). The second solution is that you can only have
2139        # target named "foo" as an exe, shared lib _or_ static
2140        # lib. This makes filename collisions not happen. The downside
2141        # is that you can't have an executable foo that uses a shared
2142        # library libfoo.so, which is a common idiom on Unix.
2143        #
2144        # If you feel that the above is completely wrong and all of
2145        # this is actually doable, please send patches.
2146
2147        if target.has_pch():
2148            tfilename = self.get_target_filename_abs(target)
2149            return compiler.get_compile_debugfile_args(tfilename, pch=True)
2150        else:
2151            return compiler.get_compile_debugfile_args(objfile, pch=False)
2152
2153    def get_link_debugfile_name(self, linker, target, outname):
2154        return linker.get_link_debugfile_name(outname)
2155
2156    def get_link_debugfile_args(self, linker, target, outname):
2157        return linker.get_link_debugfile_args(outname)
2158
2159    def generate_llvm_ir_compile(self, target, src):
2160        compiler = get_compiler_for_source(target.compilers.values(), src)
2161        commands = compiler.compiler_args()
2162        # Compiler args for compiling this target
2163        commands += compilers.get_base_compile_args(self.environment.coredata.base_options,
2164                                                    compiler)
2165        if isinstance(src, File):
2166            if src.is_built:
2167                src_filename = os.path.join(src.subdir, src.fname)
2168            else:
2169                src_filename = src.fname
2170        elif os.path.isabs(src):
2171            src_filename = os.path.basename(src)
2172        else:
2173            src_filename = src
2174        obj_basename = self.canonicalize_filename(src_filename)
2175        rel_obj = os.path.join(self.get_target_private_dir(target), obj_basename)
2176        rel_obj += '.' + self.environment.machines[target.for_machine].get_object_suffix()
2177        commands += self.get_compile_debugfile_args(compiler, target, rel_obj)
2178        if isinstance(src, File) and src.is_built:
2179            rel_src = src.fname
2180        elif isinstance(src, File):
2181            rel_src = src.rel_to_builddir(self.build_to_src)
2182        else:
2183            raise InvalidArguments('Invalid source type: {!r}'.format(src))
2184        # Write the Ninja build command
2185        compiler_name = self.get_compiler_rule_name('llvm_ir', compiler.for_machine)
2186        element = NinjaBuildElement(self.all_outputs, rel_obj, compiler_name, rel_src)
2187        element.add_item('ARGS', commands)
2188        self.add_build(element)
2189        return rel_obj
2190
2191    def get_source_dir_include_args(self, target, compiler):
2192        curdir = target.get_subdir()
2193        tmppath = os.path.normpath(os.path.join(self.build_to_src, curdir))
2194        return compiler.get_include_args(tmppath, False)
2195
2196    def get_build_dir_include_args(self, target, compiler):
2197        curdir = target.get_subdir()
2198        if curdir == '':
2199            curdir = '.'
2200        return compiler.get_include_args(curdir, False)
2201
2202    @lru_cache(maxsize=None)
2203    def get_normpath_target(self, source) -> str:
2204        return os.path.normpath(source)
2205
2206    def get_custom_target_dir_include_args(self, target, compiler):
2207        custom_target_include_dirs = []
2208        for i in target.get_generated_sources():
2209            # Generator output goes into the target private dir which is
2210            # already in the include paths list. Only custom targets have their
2211            # own target build dir.
2212            if not isinstance(i, (build.CustomTarget, build.CustomTargetIndex)):
2213                continue
2214            idir = self.get_normpath_target(self.get_target_dir(i))
2215            if not idir:
2216                idir = '.'
2217            if idir not in custom_target_include_dirs:
2218                custom_target_include_dirs.append(idir)
2219        incs = []
2220        for i in custom_target_include_dirs:
2221            incs += compiler.get_include_args(i, False)
2222        return incs
2223
2224    @lru_cache(maxsize=None)
2225    def generate_inc_dir(self, compiler, d, basedir, is_system):
2226        # Avoid superfluous '/.' at the end of paths when d is '.'
2227        if d not in ('', '.'):
2228            expdir = os.path.normpath(os.path.join(basedir, d))
2229        else:
2230            expdir = basedir
2231        srctreedir = os.path.normpath(os.path.join(self.build_to_src, expdir))
2232        sargs = compiler.get_include_args(srctreedir, is_system)
2233        # There may be include dirs where a build directory has not been
2234        # created for some source dir. For example if someone does this:
2235        #
2236        # inc = include_directories('foo/bar/baz')
2237        #
2238        # But never subdir()s into the actual dir.
2239        if os.path.isdir(os.path.join(self.environment.get_build_dir(), expdir)):
2240            bargs = compiler.get_include_args(expdir, is_system)
2241        else:
2242            bargs = []
2243        return (sargs, bargs)
2244
2245    @lru_cache(maxsize=None)
2246    def _generate_single_compile(self, target, compiler, is_generated=False):
2247        base_proxy = self.get_base_options_for_target(target)
2248        # Create an empty commands list, and start adding arguments from
2249        # various sources in the order in which they must override each other
2250        commands = compiler.compiler_args()
2251        # Start with symbol visibility.
2252        commands += compiler.gnu_symbol_visibility_args(target.gnu_symbol_visibility)
2253        # Add compiler args for compiling this target derived from 'base' build
2254        # options passed on the command-line, in default_options, etc.
2255        # These have the lowest priority.
2256        commands += compilers.get_base_compile_args(base_proxy,
2257                                                    compiler)
2258        # The code generated by valac is usually crap and has tons of unused
2259        # variables and such, so disable warnings for Vala C sources.
2260        no_warn_args = (is_generated == 'vala')
2261        # Add compiler args and include paths from several sources; defaults,
2262        # build options, external dependencies, etc.
2263        commands += self.generate_basic_compiler_args(target, compiler, no_warn_args)
2264        # Add custom target dirs as includes automatically, but before
2265        # target-specific include directories.
2266        # XXX: Not sure if anyone actually uses this? It can cause problems in
2267        # situations which increase the likelihood for a header name collision,
2268        # such as in subprojects.
2269        commands += self.get_custom_target_dir_include_args(target, compiler)
2270        # Add include dirs from the `include_directories:` kwarg on the target
2271        # and from `include_directories:` of internal deps of the target.
2272        #
2273        # Target include dirs should override internal deps include dirs.
2274        # This is handled in BuildTarget.process_kwargs()
2275        #
2276        # Include dirs from internal deps should override include dirs from
2277        # external deps and must maintain the order in which they are specified.
2278        # Hence, we must reverse the list so that the order is preserved.
2279        for i in reversed(target.get_include_dirs()):
2280            basedir = i.get_curdir()
2281            # We should iterate include dirs in reversed orders because
2282            # -Ipath will add to begin of array. And without reverse
2283            # flags will be added in reversed order.
2284            for d in reversed(i.get_incdirs()):
2285                # Add source subdir first so that the build subdir overrides it
2286                (compile_obj, includeargs) = self.generate_inc_dir(compiler, d, basedir, i.is_system)
2287                commands += compile_obj
2288                commands += includeargs
2289            for d in i.get_extra_build_dirs():
2290                commands += compiler.get_include_args(d, i.is_system)
2291        # Add per-target compile args, f.ex, `c_args : ['-DFOO']`. We set these
2292        # near the end since these are supposed to override everything else.
2293        commands += self.escape_extra_args(compiler,
2294                                           target.get_extra_args(compiler.get_language()))
2295
2296        # D specific additional flags
2297        if compiler.language == 'd':
2298            commands += compiler.get_feature_args(target.d_features, self.build_to_src)
2299
2300        # Add source dir and build dir. Project-specific and target-specific
2301        # include paths must override per-target compile args, include paths
2302        # from external dependencies, internal dependencies, and from
2303        # per-target `include_directories:`
2304        #
2305        # We prefer headers in the build dir over the source dir since, for
2306        # instance, the user might have an srcdir == builddir Autotools build
2307        # in their source tree. Many projects that are moving to Meson have
2308        # both Meson and Autotools in parallel as part of the transition.
2309        if target.implicit_include_directories:
2310            commands += self.get_source_dir_include_args(target, compiler)
2311        if target.implicit_include_directories:
2312            commands += self.get_build_dir_include_args(target, compiler)
2313        # Finally add the private dir for the target to the include path. This
2314        # must override everything else and must be the final path added.
2315        commands += compiler.get_include_args(self.get_target_private_dir(target), False)
2316        return commands
2317
2318    def generate_single_compile(self, target, src, is_generated=False, header_deps=None, order_deps=None):
2319        """
2320        Compiles C/C++, ObjC/ObjC++, Fortran, and D sources
2321        """
2322        header_deps = header_deps if header_deps is not None else []
2323        order_deps = order_deps if order_deps is not None else []
2324
2325        if isinstance(src, str) and src.endswith('.h'):
2326            raise AssertionError('BUG: sources should not contain headers {!r}'.format(src))
2327
2328        compiler = get_compiler_for_source(target.compilers.values(), src)
2329        commands = self._generate_single_compile(target, compiler, is_generated)
2330        commands = commands.compiler.compiler_args(commands)
2331
2332        # Create introspection information
2333        if is_generated is False:
2334            self.create_target_source_introspection(target, compiler, commands, [src], [])
2335        else:
2336            self.create_target_source_introspection(target, compiler, commands, [], [src])
2337
2338        build_dir = self.environment.get_build_dir()
2339        if isinstance(src, File):
2340            rel_src = src.rel_to_builddir(self.build_to_src)
2341            if os.path.isabs(rel_src):
2342                # Source files may not be from the source directory if they originate in source-only libraries,
2343                # so we can't assert that the absolute path is anywhere in particular.
2344                if src.is_built:
2345                    assert rel_src.startswith(build_dir)
2346                    rel_src = rel_src[len(build_dir) + 1:]
2347        elif is_generated:
2348            raise AssertionError('BUG: broken generated source file handling for {!r}'.format(src))
2349        else:
2350            raise InvalidArguments('Invalid source type: {!r}'.format(src))
2351        obj_basename = self.object_filename_from_source(target, src)
2352        rel_obj = os.path.join(self.get_target_private_dir(target), obj_basename)
2353        dep_file = compiler.depfile_for_object(rel_obj)
2354
2355        # Add MSVC debug file generation compile flags: /Fd /FS
2356        commands += self.get_compile_debugfile_args(compiler, target, rel_obj)
2357
2358        # PCH handling
2359        if self.environment.coredata.base_options.get('b_pch', False):
2360            commands += self.get_pch_include_args(compiler, target)
2361            pchlist = target.get_pch(compiler.language)
2362        else:
2363            pchlist = []
2364        if not pchlist:
2365            pch_dep = []
2366        elif compiler.id == 'intel':
2367            pch_dep = []
2368        else:
2369            arr = []
2370            i = os.path.join(self.get_target_private_dir(target), compiler.get_pch_name(pchlist[0]))
2371            arr.append(i)
2372            pch_dep = arr
2373
2374        compiler_name = self.compiler_to_rule_name(compiler)
2375        extra_deps = []
2376        if compiler.get_language() == 'fortran':
2377            # Can't read source file to scan for deps if it's generated later
2378            # at build-time. Skip scanning for deps, and just set the module
2379            # outdir argument instead.
2380            # https://github.com/mesonbuild/meson/issues/1348
2381            if not is_generated:
2382                abs_src = Path(build_dir) / rel_src
2383                extra_deps += self.get_fortran_deps(compiler, abs_src, target)
2384            # Dependency hack. Remove once multiple outputs in Ninja is fixed:
2385            # https://groups.google.com/forum/#!topic/ninja-build/j-2RfBIOd_8
2386            for modname, srcfile in self.fortran_deps[target.get_basename()].items():
2387                modfile = os.path.join(self.get_target_private_dir(target),
2388                                       compiler.module_name_to_filename(modname))
2389
2390                if srcfile == src:
2391                    crstr = self.get_rule_suffix(target.for_machine)
2392                    depelem = NinjaBuildElement(self.all_outputs, modfile, 'FORTRAN_DEP_HACK' + crstr, rel_obj)
2393                    self.add_build(depelem)
2394            commands += compiler.get_module_outdir_args(self.get_target_private_dir(target))
2395
2396        element = NinjaBuildElement(self.all_outputs, rel_obj, compiler_name, rel_src)
2397        self.add_header_deps(target, element, header_deps)
2398        for d in extra_deps:
2399            element.add_dep(d)
2400        for d in order_deps:
2401            if isinstance(d, File):
2402                d = d.rel_to_builddir(self.build_to_src)
2403            elif not self.has_dir_part(d):
2404                d = os.path.join(self.get_target_private_dir(target), d)
2405            element.add_orderdep(d)
2406        element.add_dep(pch_dep)
2407        for i in self.get_fortran_orderdeps(target, compiler):
2408            element.add_orderdep(i)
2409        element.add_item('DEPFILE', dep_file)
2410        element.add_item('ARGS', commands)
2411        self.add_build(element)
2412        return rel_obj
2413
2414    def add_header_deps(self, target, ninja_element, header_deps):
2415        for d in header_deps:
2416            if isinstance(d, File):
2417                d = d.rel_to_builddir(self.build_to_src)
2418            elif not self.has_dir_part(d):
2419                d = os.path.join(self.get_target_private_dir(target), d)
2420            ninja_element.add_dep(d)
2421
2422    def has_dir_part(self, fname):
2423        # FIXME FIXME: The usage of this is a terrible and unreliable hack
2424        if isinstance(fname, File):
2425            return fname.subdir != ''
2426        return has_path_sep(fname)
2427
2428    # Fortran is a bit weird (again). When you link against a library, just compiling a source file
2429    # requires the mod files that are output when single files are built. To do this right we would need to
2430    # scan all inputs and write out explicit deps for each file. That is stoo slow and too much effort so
2431    # instead just have an ordered dependency on the library. This ensures all required mod files are created.
2432    # The real deps are then detected via dep file generation from the compiler. This breaks on compilers that
2433    # produce incorrect dep files but such is life.
2434    def get_fortran_orderdeps(self, target, compiler):
2435        if compiler.language != 'fortran':
2436            return []
2437        return [os.path.join(self.get_target_dir(lt), lt.get_filename()) for lt in target.link_targets]
2438
2439    def generate_msvc_pch_command(self, target, compiler, pch):
2440        header = pch[0]
2441        pchname = compiler.get_pch_name(header)
2442        dst = os.path.join(self.get_target_private_dir(target), pchname)
2443
2444        commands = []
2445        commands += self.generate_basic_compiler_args(target, compiler)
2446
2447        if len(pch) == 1:
2448            # Auto generate PCH.
2449            source = self.create_msvc_pch_implementation(target, compiler.get_language(), pch[0])
2450            pch_header_dir = os.path.dirname(os.path.join(self.build_to_src, target.get_source_subdir(), header))
2451            commands += compiler.get_include_args(pch_header_dir, False)
2452        else:
2453            source = os.path.join(self.build_to_src, target.get_source_subdir(), pch[1])
2454
2455        just_name = os.path.basename(header)
2456        (objname, pch_args) = compiler.gen_pch_args(just_name, source, dst)
2457        commands += pch_args
2458        commands += self._generate_single_compile(target, compiler)
2459        commands += self.get_compile_debugfile_args(compiler, target, objname)
2460        dep = dst + '.' + compiler.get_depfile_suffix()
2461        return commands, dep, dst, [objname], source
2462
2463    def generate_gcc_pch_command(self, target, compiler, pch):
2464        commands = self._generate_single_compile(target, compiler)
2465        if pch.split('.')[-1] == 'h' and compiler.language == 'cpp':
2466            # Explicitly compile pch headers as C++. If Clang is invoked in C++ mode, it actually warns if
2467            # this option is not set, and for gcc it also makes sense to use it.
2468            commands += ['-x', 'c++-header']
2469        dst = os.path.join(self.get_target_private_dir(target),
2470                           os.path.basename(pch) + '.' + compiler.get_pch_suffix())
2471        dep = dst + '.' + compiler.get_depfile_suffix()
2472        return commands, dep, dst, []  # Gcc does not create an object file during pch generation.
2473
2474    def generate_pch(self, target, header_deps=None):
2475        header_deps = header_deps if header_deps is not None else []
2476        pch_objects = []
2477        for lang in ['c', 'cpp']:
2478            pch = target.get_pch(lang)
2479            if not pch:
2480                continue
2481            if not has_path_sep(pch[0]) or not has_path_sep(pch[-1]):
2482                msg = 'Precompiled header of {!r} must not be in the same ' \
2483                      'directory as source, please put it in a subdirectory.' \
2484                      ''.format(target.get_basename())
2485                raise InvalidArguments(msg)
2486            compiler = target.compilers[lang]
2487            if isinstance(compiler, VisualStudioLikeCompiler):
2488                (commands, dep, dst, objs, src) = self.generate_msvc_pch_command(target, compiler, pch)
2489                extradep = os.path.join(self.build_to_src, target.get_source_subdir(), pch[0])
2490            elif compiler.id == 'intel':
2491                # Intel generates on target generation
2492                continue
2493            else:
2494                src = os.path.join(self.build_to_src, target.get_source_subdir(), pch[0])
2495                (commands, dep, dst, objs) = self.generate_gcc_pch_command(target, compiler, pch[0])
2496                extradep = None
2497            pch_objects += objs
2498            rulename = self.compiler_to_pch_rule_name(compiler)
2499            elem = NinjaBuildElement(self.all_outputs, dst, rulename, src)
2500            if extradep is not None:
2501                elem.add_dep(extradep)
2502            self.add_header_deps(target, elem, header_deps)
2503            elem.add_item('ARGS', commands)
2504            elem.add_item('DEPFILE', dep)
2505            self.add_build(elem)
2506        return pch_objects
2507
2508    def get_target_shsym_filename(self, target):
2509        # Always name the .symbols file after the primary build output because it always exists
2510        targetdir = self.get_target_private_dir(target)
2511        return os.path.join(targetdir, target.get_filename() + '.symbols')
2512
2513    def generate_shsym(self, target):
2514        target_file = self.get_target_filename(target)
2515        symname = self.get_target_shsym_filename(target)
2516        elem = NinjaBuildElement(self.all_outputs, symname, 'SHSYM', target_file)
2517        # The library we will actually link to, which is an import library on Windows (not the DLL)
2518        elem.add_item('IMPLIB', self.get_target_filename_for_linking(target))
2519        if self.environment.is_cross_build():
2520            elem.add_item('CROSS', '--cross-host=' + self.environment.machines[target.for_machine].system)
2521        self.add_build(elem)
2522
2523    def get_cross_stdlib_link_args(self, target, linker):
2524        if isinstance(target, build.StaticLibrary) or \
2525           self.environment.machines.matches_build_machine(target.for_machine):
2526            return []
2527        if not self.environment.properties.host.has_stdlib(linker.language):
2528            return []
2529        return linker.get_no_stdlib_link_args()
2530
2531    def get_import_filename(self, target):
2532        return os.path.join(self.get_target_dir(target), target.import_filename)
2533
2534    def get_target_type_link_args(self, target, linker):
2535        commands = []
2536        if isinstance(target, build.Executable):
2537            # Currently only used with the Swift compiler to add '-emit-executable'
2538            commands += linker.get_std_exe_link_args()
2539            # If export_dynamic, add the appropriate linker arguments
2540            if target.export_dynamic:
2541                commands += linker.gen_export_dynamic_link_args(self.environment)
2542            # If implib, and that's significant on this platform (i.e. Windows using either GCC or Visual Studio)
2543            if target.import_filename:
2544                commands += linker.gen_import_library_args(self.get_import_filename(target))
2545            if target.pie:
2546                commands += linker.get_pie_link_args()
2547        elif isinstance(target, build.SharedLibrary):
2548            if isinstance(target, build.SharedModule):
2549                options = self.environment.coredata.base_options
2550                commands += linker.get_std_shared_module_link_args(options)
2551            else:
2552                commands += linker.get_std_shared_lib_link_args()
2553            # All shared libraries are PIC
2554            commands += linker.get_pic_args()
2555            # Add -Wl,-soname arguments on Linux, -install_name on OS X
2556            commands += linker.get_soname_args(
2557                self.environment, target.prefix, target.name, target.suffix,
2558                target.soversion, target.darwin_versions,
2559                isinstance(target, build.SharedModule))
2560            # This is only visited when building for Windows using either GCC or Visual Studio
2561            if target.vs_module_defs and hasattr(linker, 'gen_vs_module_defs_args'):
2562                commands += linker.gen_vs_module_defs_args(target.vs_module_defs.rel_to_builddir(self.build_to_src))
2563            # This is only visited when building for Windows using either GCC or Visual Studio
2564            if target.import_filename:
2565                commands += linker.gen_import_library_args(self.get_import_filename(target))
2566        elif isinstance(target, build.StaticLibrary):
2567            commands += linker.get_std_link_args()
2568        else:
2569            raise RuntimeError('Unknown build target type.')
2570        return commands
2571
2572    def get_target_type_link_args_post_dependencies(self, target, linker):
2573        commands = []
2574        if isinstance(target, build.Executable):
2575            # If gui_app is significant on this platform, add the appropriate linker arguments.
2576            # Unfortunately this can't be done in get_target_type_link_args, because some misguided
2577            # libraries (such as SDL2) add -mwindows to their link flags.
2578            commands += linker.get_gui_app_args(target.gui_app)
2579        return commands
2580
2581    def get_link_whole_args(self, linker, target):
2582        target_args = self.build_target_link_arguments(linker, target.link_whole_targets)
2583        return linker.get_link_whole_for(target_args) if len(target_args) else []
2584
2585    @lru_cache(maxsize=None)
2586    def guess_library_absolute_path(self, linker, libname, search_dirs, patterns):
2587        for d in search_dirs:
2588            for p in patterns:
2589                trial = CCompiler._get_trials_from_pattern(p, d, libname)
2590                if not trial:
2591                    continue
2592                trial = CCompiler._get_file_from_list(self.environment, trial)
2593                if not trial:
2594                    continue
2595                # Return the first result
2596                return trial
2597
2598    def guess_external_link_dependencies(self, linker, target, commands, internal):
2599        # Ideally the linker would generate dependency information that could be used.
2600        # But that has 2 problems:
2601        # * currently ld can not create dependency information in a way that ninja can use:
2602        #   https://sourceware.org/bugzilla/show_bug.cgi?id=22843
2603        # * Meson optimizes libraries from the same build using the symbol extractor.
2604        #   Just letting ninja use ld generated dependencies would undo this optimization.
2605        search_dirs = OrderedSet()
2606        libs = OrderedSet()
2607        absolute_libs = []
2608
2609        build_dir = self.environment.get_build_dir()
2610        # the following loop sometimes consumes two items from command in one pass
2611        it = iter(linker.native_args_to_unix(commands))
2612        for item in it:
2613            if item in internal and not item.startswith('-'):
2614                continue
2615
2616            if item.startswith('-L'):
2617                if len(item) > 2:
2618                    path = item[2:]
2619                else:
2620                    try:
2621                        path = next(it)
2622                    except StopIteration:
2623                        mlog.warning("Generated linker command has -L argument without following path")
2624                        break
2625                if not os.path.isabs(path):
2626                    path = os.path.join(build_dir, path)
2627                search_dirs.add(path)
2628            elif item.startswith('-l'):
2629                if len(item) > 2:
2630                    lib = item[2:]
2631                else:
2632                    try:
2633                        lib = next(it)
2634                    except StopIteration:
2635                        mlog.warning("Generated linker command has '-l' argument without following library name")
2636                        break
2637                libs.add(lib)
2638            elif os.path.isabs(item) and self.environment.is_library(item) and os.path.isfile(item):
2639                absolute_libs.append(item)
2640
2641        guessed_dependencies = []
2642        # TODO The get_library_naming requirement currently excludes link targets that use d or fortran as their main linker
2643        if hasattr(linker, 'get_library_naming'):
2644            search_dirs = tuple(search_dirs) + tuple(linker.get_library_dirs(self.environment))
2645            static_patterns = linker.get_library_naming(self.environment, LibType.STATIC, strict=True)
2646            shared_patterns = linker.get_library_naming(self.environment, LibType.SHARED, strict=True)
2647            for libname in libs:
2648                # be conservative and record most likely shared and static resolution, because we don't know exactly
2649                # which one the linker will prefer
2650                staticlibs = self.guess_library_absolute_path(linker, libname,
2651                                                              search_dirs, static_patterns)
2652                sharedlibs = self.guess_library_absolute_path(linker, libname,
2653                                                              search_dirs, shared_patterns)
2654                if staticlibs:
2655                    guessed_dependencies.append(staticlibs.resolve().as_posix())
2656                if sharedlibs:
2657                    guessed_dependencies.append(sharedlibs.resolve().as_posix())
2658
2659        return guessed_dependencies + absolute_libs
2660
2661    def generate_link(self, target, outname, obj_list, linker, extra_args=None, stdlib_args=None):
2662        extra_args = extra_args if extra_args is not None else []
2663        stdlib_args = stdlib_args if stdlib_args is not None else []
2664        implicit_outs = []
2665        if isinstance(target, build.StaticLibrary):
2666            linker_base = 'STATIC'
2667        else:
2668            linker_base = linker.get_language() # Fixme.
2669        if isinstance(target, build.SharedLibrary):
2670            self.generate_shsym(target)
2671        crstr = self.get_rule_suffix(target.for_machine)
2672        linker_rule = linker_base + '_LINKER' + crstr
2673        # Create an empty commands list, and start adding link arguments from
2674        # various sources in the order in which they must override each other
2675        # starting from hard-coded defaults followed by build options and so on.
2676        #
2677        # Once all the linker options have been passed, we will start passing
2678        # libraries and library paths from internal and external sources.
2679        commands = linker.compiler_args()
2680        # First, the trivial ones that are impossible to override.
2681        #
2682        # Add linker args for linking this target derived from 'base' build
2683        # options passed on the command-line, in default_options, etc.
2684        # These have the lowest priority.
2685        if isinstance(target, build.StaticLibrary):
2686            commands += linker.get_base_link_args(self.get_base_options_for_target(target))
2687        else:
2688            commands += compilers.get_base_link_args(self.get_base_options_for_target(target),
2689                                                     linker,
2690                                                     isinstance(target, build.SharedModule))
2691        # Add -nostdlib if needed; can't be overridden
2692        commands += self.get_cross_stdlib_link_args(target, linker)
2693        # Add things like /NOLOGO; usually can't be overridden
2694        commands += linker.get_linker_always_args()
2695        # Add buildtype linker args: optimization level, etc.
2696        commands += linker.get_buildtype_linker_args(self.get_option_for_target('buildtype', target))
2697        # Add /DEBUG and the pdb filename when using MSVC
2698        if self.get_option_for_target('debug', target):
2699            commands += self.get_link_debugfile_args(linker, target, outname)
2700            debugfile = self.get_link_debugfile_name(linker, target, outname)
2701            if debugfile is not None:
2702                implicit_outs += [debugfile]
2703        # Add link args specific to this BuildTarget type, such as soname args,
2704        # PIC, import library generation, etc.
2705        commands += self.get_target_type_link_args(target, linker)
2706        # Archives that are copied wholesale in the result. Must be before any
2707        # other link targets so missing symbols from whole archives are found in those.
2708        if not isinstance(target, build.StaticLibrary):
2709            commands += self.get_link_whole_args(linker, target)
2710
2711        if not isinstance(target, build.StaticLibrary):
2712            # Add link args added using add_project_link_arguments()
2713            commands += self.build.get_project_link_args(linker, target.subproject, target.for_machine)
2714            # Add link args added using add_global_link_arguments()
2715            # These override per-project link arguments
2716            commands += self.build.get_global_link_args(linker, target.for_machine)
2717            # Link args added from the env: LDFLAGS. We want these to override
2718            # all the defaults but not the per-target link args.
2719            commands += self.environment.coredata.get_external_link_args(target.for_machine, linker.get_language())
2720
2721        # Now we will add libraries and library paths from various sources
2722
2723        # Add link args to link to all internal libraries (link_with:) and
2724        # internal dependencies needed by this target.
2725        if linker_base == 'STATIC':
2726            # Link arguments of static libraries are not put in the command
2727            # line of the library. They are instead appended to the command
2728            # line where the static library is used.
2729            dependencies = []
2730        else:
2731            dependencies = target.get_dependencies()
2732        internal = self.build_target_link_arguments(linker, dependencies)
2733        commands += internal
2734        # Only non-static built targets need link args and link dependencies
2735        if not isinstance(target, build.StaticLibrary):
2736            # For 'automagic' deps: Boost and GTest. Also dependency('threads').
2737            # pkg-config puts the thread flags itself via `Cflags:`
2738
2739            commands += linker.get_target_link_args(target)
2740            # External deps must be last because target link libraries may depend on them.
2741            for dep in target.get_external_deps():
2742                # Extend without reordering or de-dup to preserve `-L -l` sets
2743                # https://github.com/mesonbuild/meson/issues/1718
2744                commands.extend_preserving_lflags(linker.get_dependency_link_args(dep))
2745            for d in target.get_dependencies():
2746                if isinstance(d, build.StaticLibrary):
2747                    for dep in d.get_external_deps():
2748                        commands.extend_preserving_lflags(linker.get_dependency_link_args(dep))
2749
2750        # Add link args specific to this BuildTarget type that must not be overridden by dependencies
2751        commands += self.get_target_type_link_args_post_dependencies(target, linker)
2752
2753        # Add link args for c_* or cpp_* build options. Currently this only
2754        # adds c_winlibs and cpp_winlibs when building for Windows. This needs
2755        # to be after all internal and external libraries so that unresolved
2756        # symbols from those can be found here. This is needed when the
2757        # *_winlibs that we want to link to are static mingw64 libraries.
2758        if hasattr(linker, 'get_language'):
2759            # The static linker doesn't know what language it is building, so we
2760            # don't know what option. Fortunately, it doesn't care to see the
2761            # language-specific options either.
2762            #
2763            # We shouldn't check whether we are making a static library, because
2764            # in the LTO case we do use a real compiler here.
2765            commands += linker.get_option_link_args(self.environment.coredata.compiler_options[target.for_machine][linker.get_language()])
2766
2767        dep_targets = []
2768        dep_targets.extend(self.guess_external_link_dependencies(linker, target, commands, internal))
2769
2770        # Set runtime-paths so we can run executables without needing to set
2771        # LD_LIBRARY_PATH, etc in the environment. Doesn't work on Windows.
2772        if has_path_sep(target.name):
2773            # Target names really should not have slashes in them, but
2774            # unfortunately we did not check for that and some downstream projects
2775            # now have them. Once slashes are forbidden, remove this bit.
2776            target_slashname_workaround_dir = os.path.join(
2777                os.path.dirname(target.name),
2778                self.get_target_dir(target))
2779        else:
2780            target_slashname_workaround_dir = self.get_target_dir(target)
2781        (rpath_args, target.rpath_dirs_to_remove) = \
2782                    linker.build_rpath_args(self.environment,
2783                                            self.environment.get_build_dir(),
2784                                            target_slashname_workaround_dir,
2785                                            self.determine_rpath_dirs(target),
2786                                            target.build_rpath,
2787                                            target.install_rpath)
2788        commands += rpath_args
2789        # Add libraries generated by custom targets
2790        custom_target_libraries = self.get_custom_target_provided_libraries(target)
2791        commands += extra_args
2792        commands += custom_target_libraries
2793        commands += stdlib_args # Standard library arguments go last, because they never depend on anything.
2794        dep_targets.extend([self.get_dependency_filename(t) for t in dependencies])
2795        dep_targets.extend([self.get_dependency_filename(t)
2796                            for t in target.link_depends])
2797        elem = NinjaBuildElement(self.all_outputs, outname, linker_rule, obj_list, implicit_outs=implicit_outs)
2798        elem.add_dep(dep_targets + custom_target_libraries)
2799        elem.add_item('LINK_ARGS', commands)
2800        return elem
2801
2802    def get_dependency_filename(self, t):
2803        if isinstance(t, build.SharedLibrary):
2804            return self.get_target_shsym_filename(t)
2805        elif isinstance(t, mesonlib.File):
2806            if t.is_built:
2807                return t.relative_name()
2808            else:
2809                return t.absolute_path(self.environment.get_source_dir(),
2810                                       self.environment.get_build_dir())
2811        return self.get_target_filename(t)
2812
2813    def generate_shlib_aliases(self, target, outdir):
2814        aliases = target.get_aliases()
2815        for alias, to in aliases.items():
2816            aliasfile = os.path.join(self.environment.get_build_dir(), outdir, alias)
2817            try:
2818                os.remove(aliasfile)
2819            except Exception:
2820                pass
2821            try:
2822                os.symlink(to, aliasfile)
2823            except NotImplementedError:
2824                mlog.debug("Library versioning disabled because symlinks are not supported.")
2825            except OSError:
2826                mlog.debug("Library versioning disabled because we do not have symlink creation privileges.")
2827
2828    def generate_custom_target_clean(self, trees):
2829        e = NinjaBuildElement(self.all_outputs, 'meson-clean-ctlist', 'CUSTOM_COMMAND', 'PHONY')
2830        d = CleanTrees(self.environment.get_build_dir(), trees)
2831        d_file = os.path.join(self.environment.get_scratch_dir(), 'cleantrees.dat')
2832        e.add_item('COMMAND', self.environment.get_build_command() + ['--internal', 'cleantrees', d_file])
2833        e.add_item('description', 'Cleaning custom target directories')
2834        self.add_build(e)
2835        # Alias that runs the target defined above
2836        self.create_target_alias('meson-clean-ctlist')
2837        # Write out the data file passed to the script
2838        with open(d_file, 'wb') as ofile:
2839            pickle.dump(d, ofile)
2840        return 'clean-ctlist'
2841
2842    def generate_gcov_clean(self):
2843        gcno_elem = NinjaBuildElement(self.all_outputs, 'meson-clean-gcno', 'CUSTOM_COMMAND', 'PHONY')
2844        gcno_elem.add_item('COMMAND', mesonlib.meson_command + ['--internal', 'delwithsuffix', '.', 'gcno'])
2845        gcno_elem.add_item('description', 'Deleting gcno files')
2846        self.add_build(gcno_elem)
2847        # Alias that runs the target defined above
2848        self.create_target_alias('meson-clean-gcno')
2849
2850        gcda_elem = NinjaBuildElement(self.all_outputs, 'meson-clean-gcda', 'CUSTOM_COMMAND', 'PHONY')
2851        gcda_elem.add_item('COMMAND', mesonlib.meson_command + ['--internal', 'delwithsuffix', '.', 'gcda'])
2852        gcda_elem.add_item('description', 'Deleting gcda files')
2853        self.add_build(gcda_elem)
2854        # Alias that runs the target defined above
2855        self.create_target_alias('meson-clean-gcda')
2856
2857    def get_user_option_args(self):
2858        cmds = []
2859        for (k, v) in self.environment.coredata.user_options.items():
2860            cmds.append('-D' + k + '=' + (v.value if isinstance(v.value, str) else str(v.value).lower()))
2861        # The order of these arguments must be the same between runs of Meson
2862        # to ensure reproducible output. The order we pass them shouldn't
2863        # affect behavior in any other way.
2864        return sorted(cmds)
2865
2866    def generate_dist(self):
2867        elem = NinjaBuildElement(self.all_outputs, 'meson-dist', 'CUSTOM_COMMAND', 'PHONY')
2868        elem.add_item('DESC', 'Creating source packages')
2869        elem.add_item('COMMAND', self.environment.get_build_command() + ['dist'])
2870        elem.add_item('pool', 'console')
2871        self.add_build(elem)
2872        # Alias that runs the target defined above
2873        self.create_target_alias('meson-dist')
2874
2875    def generate_scanbuild(self):
2876        if not environment.detect_scanbuild():
2877            return
2878        if ('', 'scan-build') in self.build.run_target_names:
2879            return
2880        cmd = self.environment.get_build_command() + \
2881            ['--internal', 'scanbuild', self.environment.source_dir, self.environment.build_dir] + \
2882            self.environment.get_build_command() + self.get_user_option_args()
2883        elem = NinjaBuildElement(self.all_outputs, 'meson-scan-build', 'CUSTOM_COMMAND', 'PHONY')
2884        elem.add_item('COMMAND', cmd)
2885        elem.add_item('pool', 'console')
2886        self.add_build(elem)
2887        # Alias that runs the target defined above
2888        self.create_target_alias('meson-scan-build')
2889
2890    def generate_clangtool(self, name):
2891        target_name = 'clang-' + name
2892        if not os.path.exists(os.path.join(self.environment.source_dir, '.clang-' + name)) and \
2893                not os.path.exists(os.path.join(self.environment.source_dir, '_clang-' + name)):
2894            return
2895        if target_name in self.all_outputs:
2896            return
2897        if ('', target_name) in self.build.run_target_names:
2898            return
2899        cmd = self.environment.get_build_command() + \
2900            ['--internal', 'clang' + name, self.environment.source_dir, self.environment.build_dir]
2901        elem = NinjaBuildElement(self.all_outputs, 'meson-' + target_name, 'CUSTOM_COMMAND', 'PHONY')
2902        elem.add_item('COMMAND', cmd)
2903        elem.add_item('pool', 'console')
2904        self.add_build(elem)
2905        self.create_target_alias('meson-' + target_name)
2906
2907    def generate_clangformat(self):
2908        if not environment.detect_clangformat():
2909            return
2910        self.generate_clangtool('format')
2911
2912    def generate_clangtidy(self):
2913        import shutil
2914        if not shutil.which('clang-tidy'):
2915            return
2916        self.generate_clangtool('tidy')
2917
2918    def generate_tags(self, tool, target_name):
2919        import shutil
2920        if not shutil.which(tool):
2921            return
2922        if ('', target_name) in self.build.run_target_names:
2923            return
2924        if target_name in self.all_outputs:
2925            return
2926        cmd = self.environment.get_build_command() + \
2927            ['--internal', 'tags', tool, self.environment.source_dir]
2928        elem = NinjaBuildElement(self.all_outputs, 'meson-' + target_name, 'CUSTOM_COMMAND', 'PHONY')
2929        elem.add_item('COMMAND', cmd)
2930        elem.add_item('pool', 'console')
2931        self.add_build(elem)
2932        # Alias that runs the target defined above
2933        self.create_target_alias('meson-' + target_name)
2934
2935    # For things like scan-build and other helper tools we might have.
2936    def generate_utils(self):
2937        self.generate_scanbuild()
2938        self.generate_clangformat()
2939        self.generate_clangtidy()
2940        self.generate_tags('etags', 'TAGS')
2941        self.generate_tags('ctags', 'ctags')
2942        self.generate_tags('cscope', 'cscope')
2943        cmd = self.environment.get_build_command() + ['--internal', 'uninstall']
2944        elem = NinjaBuildElement(self.all_outputs, 'meson-uninstall', 'CUSTOM_COMMAND', 'PHONY')
2945        elem.add_item('COMMAND', cmd)
2946        elem.add_item('pool', 'console')
2947        self.add_build(elem)
2948        # Alias that runs the target defined above
2949        self.create_target_alias('meson-uninstall')
2950
2951    def generate_ending(self):
2952        targetlist = []
2953        for t in self.get_build_by_default_targets().values():
2954            # Add the first output of each target to the 'all' target so that
2955            # they are all built
2956            targetlist.append(os.path.join(self.get_target_dir(t), t.get_outputs()[0]))
2957
2958        elem = NinjaBuildElement(self.all_outputs, 'all', 'phony', targetlist)
2959        self.add_build(elem)
2960
2961        elem = NinjaBuildElement(self.all_outputs, 'meson-clean', 'CUSTOM_COMMAND', 'PHONY')
2962        elem.add_item('COMMAND', self.ninja_command + ['-t', 'clean'])
2963        elem.add_item('description', 'Cleaning')
2964        # Alias that runs the above-defined meson-clean target
2965        self.create_target_alias('meson-clean')
2966
2967        # If we have custom targets in this project, add all their outputs to
2968        # the list that is passed to the `cleantrees.py` script. The script
2969        # will manually delete all custom_target outputs that are directories
2970        # instead of files. This is needed because on platforms other than
2971        # Windows, Ninja only deletes directories while cleaning if they are
2972        # empty. https://github.com/mesonbuild/meson/issues/1220
2973        ctlist = []
2974        for t in self.build.get_targets().values():
2975            if isinstance(t, build.CustomTarget):
2976                # Create a list of all custom target outputs
2977                for o in t.get_outputs():
2978                    ctlist.append(os.path.join(self.get_target_dir(t), o))
2979        if ctlist:
2980            elem.add_dep(self.generate_custom_target_clean(ctlist))
2981
2982        if 'b_coverage' in self.environment.coredata.base_options and \
2983           self.environment.coredata.base_options['b_coverage'].value:
2984            self.generate_gcov_clean()
2985            elem.add_dep('clean-gcda')
2986            elem.add_dep('clean-gcno')
2987        self.add_build(elem)
2988
2989        deps = self.get_regen_filelist()
2990        elem = NinjaBuildElement(self.all_outputs, 'build.ninja', 'REGENERATE_BUILD', deps)
2991        elem.add_item('pool', 'console')
2992        self.add_build(elem)
2993
2994        elem = NinjaBuildElement(self.all_outputs, 'reconfigure', 'REGENERATE_BUILD', 'PHONY')
2995        elem.add_item('pool', 'console')
2996        self.add_build(elem)
2997
2998        elem = NinjaBuildElement(self.all_outputs, deps, 'phony', '')
2999        self.add_build(elem)
3000
3001    def get_introspection_data(self, target_id, target):
3002        if target_id not in self.introspection_data or len(self.introspection_data[target_id]) == 0:
3003            return super().get_introspection_data(target_id, target)
3004
3005        result = []
3006        for i in self.introspection_data[target_id].values():
3007            result += [i]
3008        return result
3009
3010def load(build_dir):
3011    filename = os.path.join(build_dir, 'meson-private', 'install.dat')
3012    with open(filename, 'rb') as f:
3013        obj = pickle.load(f)
3014    return obj
3015
3016
3017def _scan_fortran_file_deps(src: Path, srcdir: Path, dirname: Path, tdeps, compiler) -> T.List[str]:
3018    """
3019    scan a Fortran file for dependencies. Needs to be distinct from target
3020    to allow for recursion induced by `include` statements.er
3021
3022    It makes a number of assumptions, including
3023
3024    * `use`, `module`, `submodule` name is not on a continuation line
3025
3026    Regex
3027    -----
3028
3029    * `incre` works for `#include "foo.f90"` and `include "foo.f90"`
3030    * `usere` works for legacy and Fortran 2003 `use` statements
3031    * `submodre` is for Fortran >= 2008 `submodule`
3032    """
3033
3034    incre = re.compile(FORTRAN_INCLUDE_PAT, re.IGNORECASE)
3035    usere = re.compile(FORTRAN_USE_PAT, re.IGNORECASE)
3036    submodre = re.compile(FORTRAN_SUBMOD_PAT, re.IGNORECASE)
3037
3038    mod_files = []
3039    src = Path(src)
3040    with src.open(encoding='ascii', errors='ignore') as f:
3041        for line in f:
3042            # included files
3043            incmatch = incre.match(line)
3044            if incmatch is not None:
3045                incfile = srcdir / incmatch.group(1)
3046                if incfile.suffix.lower()[1:] in compiler.file_suffixes:
3047                    mod_files.extend(_scan_fortran_file_deps(incfile, srcdir, dirname, tdeps, compiler))
3048            # modules
3049            usematch = usere.match(line)
3050            if usematch is not None:
3051                usename = usematch.group(1).lower()
3052                if usename == 'intrinsic':  # this keeps the regex simpler
3053                    continue
3054                if usename not in tdeps:
3055                    # The module is not provided by any source file. This
3056                    # is due to:
3057                    #   a) missing file/typo/etc
3058                    #   b) using a module provided by the compiler, such as
3059                    #      OpenMP
3060                    # There's no easy way to tell which is which (that I
3061                    # know of) so just ignore this and go on. Ideally we
3062                    # would print a warning message to the user but this is
3063                    # a common occurrence, which would lead to lots of
3064                    # distracting noise.
3065                    continue
3066                srcfile = srcdir / tdeps[usename].fname  # type: Path
3067                if not srcfile.is_file():
3068                    if srcfile.name != src.name:  # generated source file
3069                        pass
3070                    else:  # subproject
3071                        continue
3072                elif srcfile.samefile(src):  # self-reference
3073                    continue
3074
3075                mod_name = compiler.module_name_to_filename(usename)
3076                mod_files.append(str(dirname / mod_name))
3077            else:  # submodules
3078                submodmatch = submodre.match(line)
3079                if submodmatch is not None:
3080                    parents = submodmatch.group(1).lower().split(':')
3081                    assert len(parents) in (1, 2), (
3082                        'submodule ancestry must be specified as'
3083                        ' ancestor:parent but Meson found {}'.format(parents))
3084
3085                    ancestor_child = '_'.join(parents)
3086                    if ancestor_child not in tdeps:
3087                        raise MesonException("submodule {} relies on ancestor module {} that was not found.".format(submodmatch.group(2).lower(), ancestor_child.split('_')[0]))
3088                    submodsrcfile = srcdir / tdeps[ancestor_child].fname  # type: Path
3089                    if not submodsrcfile.is_file():
3090                        if submodsrcfile.name != src.name:  # generated source file
3091                            pass
3092                        else:  # subproject
3093                            continue
3094                    elif submodsrcfile.samefile(src):  # self-reference
3095                        continue
3096                    mod_name = compiler.module_name_to_filename(ancestor_child)
3097                    mod_files.append(str(dirname / mod_name))
3098    return mod_files
3099