files: # Copyright 2012-2016 The Meson development team
  
  # Licensed under the Apache License, Version 2.0 (the "License");
  # you may not use this file except in compliance with the License.
  # You may obtain a copy of the License at
  
  #     http://www.apache.org/licenses/LICENSE-2.0
  
  # Unless required by applicable law or agreed to in writing, software
  # distributed under the License is distributed on an "AS IS" BASIS,
  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  # See the License for the specific language governing permissions and
  # limitations under the License.
  
  from . import backends
  from .. import modules
  from .. import environment, mesonlib
  from .. import build
  from .. import mlog
  from .. import dependencies
  from .. import compilers
  from ..compilers import CompilerArgs
  from ..mesonlib import File, MesonException, get_compiler_for_source, Popen_safe
  from .backends import CleanTrees, InstallData
  from ..build import InvalidArguments
  import os, sys, pickle, re
  import subprocess, shutil
  from collections import OrderedDict
  
  if mesonlib.is_windows():
      quote_char = '"'
      execute_wrapper = 'cmd /c'
  else:
      quote_char = "'"
      execute_wrapper = ''
  
  def ninja_quote(text):
      return text.replace(' ', '$ ').replace(':', '$:')
  
  class RawFilename:
      """
      Used when a filename is already relative to the root build directory, so
      that we know not to add the target's private build directory to it.
      """
      def __init__(self, fname):
          self.fname = fname
  
      def __str__(self):
          return self.fname
  
      def __repr__(self):
          return '<RawFilename: {0}>'.format(self.fname)
  
      def split(self, c):
          return self.fname.split(c)
  
      def startswith(self, s):
          return self.fname.startswith(s)
  
  class NinjaBuildElement:
      def __init__(self, all_outputs, outfilenames, rule, infilenames):
          if isinstance(outfilenames, str):
              self.outfilenames = [outfilenames]
          else:
              self.outfilenames = outfilenames
          assert(isinstance(rule, str))
          self.rule = rule
          if isinstance(infilenames, str):
              self.infilenames = [infilenames]
          else:
              self.infilenames = infilenames
          self.deps = []
          self.orderdeps = []
          self.elems = []
          self.all_outputs = all_outputs
  
      def add_dep(self, dep):
          if isinstance(dep, list):
              self.deps += dep
          else:
              self.deps.append(dep)
  
      def add_orderdep(self, dep):
          if isinstance(dep, list):
              self.orderdeps += dep
          else:
              self.orderdeps.append(dep)
  
      def add_item(self, name, elems):
          if isinstance(elems, str):
              elems = [elems]
          self.elems.append((name, elems))
  
      def write(self, outfile):
          self.check_outputs()
          line = 'build %s: %s %s' % (
              ' '.join([ninja_quote(i) for i in self.outfilenames]),
              self.rule,
              ' '.join([ninja_quote(i) for i in self.infilenames]))
          if len(self.deps) > 0:
              line += ' | ' + ' '.join([ninja_quote(x) for x in self.deps])
          if len(self.orderdeps) > 0:
              line += ' || ' + ' '.join([ninja_quote(x) for x in self.orderdeps])
          line += '\n'
          # This is the only way I could find to make this work on all
          # platforms including Windows command shell. Slash is a dir separator
          # on Windows, too, so all characters are unambiguous and, more importantly,
          # do not require quoting.
          line = line.replace('\\', '/')
          outfile.write(line)
  
          for e in self.elems:
              (name, elems) = e
              should_quote = True
              if name == 'DEPFILE' or name == 'DESC' or name == 'pool':
                  should_quote = False
              line = ' %s = ' % name
              q_templ = quote_char + "%s" + quote_char
              noq_templ = "%s"
              newelems = []
              for i in elems:
                  if not should_quote or i == '&&': # Hackety hack hack
                      templ = noq_templ
                  else:
                      templ = q_templ
                  i = i.replace('\\', '\\\\')
                  if quote_char == '"':
                      i = i.replace('"', '\\"')
                  newelems.append(templ % ninja_quote(i))
              line += ' '.join(newelems)
              line += '\n'
              outfile.write(line)
          outfile.write('\n')
  
      def check_outputs(self):
          for n in self.outfilenames:
              if n in self.all_outputs:
                  raise MesonException('Multiple producers for Ninja target "%s". Please rename your targets.' % n)
              self.all_outputs[n] = True
  
  class NinjaBackend(backends.Backend):
  
      def __init__(self, build):
          super().__init__(build)
          self.name = 'ninja'
          self.ninja_filename = 'build.ninja'
          self.fortran_deps = {}
          self.all_outputs = {}
  
      def detect_vs_dep_prefix(self, tempfilename):
          '''VS writes its dependency in a locale dependent format.
          Detect the search prefix to use.'''
          # Of course there is another program called 'cl' on
          # some platforms. Let's just require that on Windows
          # cl points to msvc.
          if not mesonlib.is_windows() or shutil.which('cl') is None:
              return open(tempfilename, 'a')
          filename = os.path.join(self.environment.get_scratch_dir(),
                                  'incdetect.c')
          with open(filename, 'w') as f:
              f.write('''#include<stdio.h>
  int dummy;
  ''')
  
          pc, stdo = Popen_safe(['cl', '/showIncludes', '/c', 'incdetect.c'],
                                cwd=self.environment.get_scratch_dir())[0:2]
  
          for line in stdo.split('\n'):
              if line.endswith('stdio.h'):
                  matchstr = ':'.join(line.split(':')[0:2]) + ':'
                  with open(tempfilename, 'a') as binfile:
                      binfile.write('msvc_deps_prefix = ' + matchstr + '\n')
                  return open(tempfilename, 'a')
          raise MesonException('Could not determine vs dep dependency prefix string.')
  
      def generate(self, interp):
          self.interpreter = interp
          outfilename = os.path.join(self.environment.get_build_dir(), self.ninja_filename)
          tempfilename = outfilename + '~'
          with open(tempfilename, 'w') as outfile:
              outfile.write('# This is the build file for project "%s"\n' %
                            self.build.get_project())
              outfile.write('# It is autogenerated by the Meson build system.\n')
              outfile.write('# Do not edit by hand.\n\n')
              outfile.write('ninja_required_version = 1.5.1\n\n')
          with self.detect_vs_dep_prefix(tempfilename) as outfile:
              self.generate_rules(outfile)
              self.generate_phony(outfile)
              outfile.write('# Build rules for targets\n\n')
              for t in self.build.get_targets().values():
                  self.generate_target(t, outfile)
              outfile.write('# Test rules\n\n')
              self.generate_tests(outfile)
              outfile.write('# Install rules\n\n')
              self.generate_install(outfile)
              if 'b_coverage' in self.environment.coredata.base_options and \
                      self.environment.coredata.base_options['b_coverage'].value:
                  outfile.write('# Coverage rules\n\n')
                  self.generate_coverage_rules(outfile)
              outfile.write('# Suffix\n\n')
              self.generate_utils(outfile)
              self.generate_ending(outfile)
          # Only ovewrite the old build file after the new one has been
          # fully created.
          os.replace(tempfilename, outfilename)
          self.generate_compdb()
  
      # http://clang.llvm.org/docs/JSONCompilationDatabase.html
      def generate_compdb(self):
          ninja_exe = environment.detect_ninja()
          builddir = self.environment.get_build_dir()
          try:
              jsondb = subprocess.check_output([ninja_exe, '-t', 'compdb', 'c_COMPILER', 'cpp_COMPILER'], cwd=builddir)
              with open(os.path.join(builddir, 'compile_commands.json'), 'wb') as f:
                  f.write(jsondb)
          except Exception:
              mlog.warning('Could not create compilation database.')
  
      # Get all generated headers. Any source file might need them so
      # we need to add an order dependency to them.
      def get_generated_headers(self, target):
          header_deps = []
          # XXX: Why don't we add deps to CustomTarget headers here?
          for genlist in target.get_generated_sources():
              if isinstance(genlist, build.CustomTarget):
                  continue
              for src in genlist.get_outputs():
                  if self.environment.is_header(src):
                      header_deps.append(self.get_target_generated_dir(target, genlist, src))
          # Recurse and find generated headers
          for dep in target.link_targets:
              if isinstance(dep, (build.StaticLibrary, build.SharedLibrary)):
                  header_deps += self.get_generated_headers(dep)
          return header_deps
  
      def get_target_generated_sources(self, target):
          """
          Returns a dictionary with the keys being the path to the file
          (relative to the build directory) of that type and the value
          being the GeneratorList or CustomTarget that generated it.
          """
          srcs = OrderedDict()
          for gensrc in target.get_generated_sources():
              for s in gensrc.get_outputs():
                  f = self.get_target_generated_dir(target, gensrc, s)
                  srcs[f] = s
          return srcs
  
      def get_target_sources(self, target):
          srcs = OrderedDict()
          for s in target.get_sources():
              # BuildTarget sources are always mesonlib.File files which are
              # either in the source root, or generated with configure_file and
              # in the build root
              if not isinstance(s, File):
                  raise InvalidArguments('All sources in target {!r} must be of type mesonlib.File'.format(s))
              f = s.rel_to_builddir(self.build_to_src)
              srcs[f] = s
          return srcs
  
      # Languages that can mix with C or C++ but don't support unity builds yet
      # because the syntax we use for unity builds is specific to C/++/ObjC/++.
      # Assembly files cannot be unitified and neither can LLVM IR files
      langs_cant_unity = ('d', 'fortran')
  
      def get_target_source_can_unity(self, target, source):
          if isinstance(source, File):
              source = source.fname
          if self.environment.is_llvm_ir(source) or \
             self.environment.is_assembly(source):
              return False
          suffix = os.path.splitext(source)[1][1:]
          for lang in self.langs_cant_unity:
              if lang not in target.compilers:
                  continue
              if suffix in target.compilers[lang].file_suffixes:
                  return False
          return True
  
      def generate_target(self, target, outfile):
          if isinstance(target, build.CustomTarget):
              self.generate_custom_target(target, outfile)
          if isinstance(target, build.RunTarget):
              self.generate_run_target(target, outfile)
          name = target.get_id()
          if name in self.processed_targets:
              return
          self.processed_targets[name] = True
          # Generate rules for all dependency targets
          self.process_target_dependencies(target, outfile)
          # If target uses a language that cannot link to C objects,
          # just generate for that language and return.
          if isinstance(target, build.Jar):
              self.generate_jar_target(target, outfile)
              return
          if 'rust' in target.compilers:
              self.generate_rust_target(target, outfile)
              return
          if 'cs' in target.compilers:
              self.generate_cs_target(target, outfile)
              return
          if 'swift' in target.compilers:
              self.generate_swift_target(target, outfile)
              return
  
          # Now we handle the following languages:
          # ObjC++, ObjC, C++, C, D, Fortran, Vala
  
          # Pre-existing target C/C++ sources to be built; dict of full path to
          # source relative to build root and the original File object.
          target_sources = OrderedDict()
          # GeneratedList and CustomTarget sources to be built; dict of the full
          # path to source relative to build root and the generating target/list
          generated_sources = OrderedDict()
          # Array of sources generated by valac that have to be compiled
          vala_generated_sources = []
          if 'vala' in target.compilers:
              # Sources consumed by valac are filtered out. These only contain
              # C/C++ sources, objects, generated libs, and unknown sources now.
              target_sources, generated_sources, \
                  vala_generated_sources = self.generate_vala_compile(target, outfile)
          else:
              target_sources = self.get_target_sources(target)
              generated_sources = self.get_target_generated_sources(target)
          self.scan_fortran_module_outputs(target)
          # Generate rules for GeneratedLists
          self.generate_generator_list_rules(target, outfile)
  
          # Generate rules for building the remaining source files in this target
          outname = self.get_target_filename(target)
          obj_list = []
          use_pch = self.environment.coredata.base_options.get('b_pch', False)
          is_unity = self.environment.coredata.get_builtin_option('unity')
          if use_pch and target.has_pch():
              pch_objects = self.generate_pch(target, outfile)
          else:
              pch_objects = []
          header_deps = []
          unity_src = []
          unity_deps = [] # Generated sources that must be built before compiling a Unity target.
          header_deps += self.get_generated_headers(target)
  
          if is_unity:
              # Warn about incompatible sources if a unity build is enabled
              langs = set(target.compilers.keys())
              langs_cant = langs.intersection(self.langs_cant_unity)
              if langs_cant:
                  langs_are = langs = ', '.join(langs_cant).upper()
                  langs_are += ' are' if len(langs_cant) > 1 else ' is'
                  msg = '{} not supported in Unity builds yet, so {} ' \
                        'sources in the {!r} target will be compiled normally' \
                        ''.format(langs_are, langs, target.name)
                  mlog.log(mlog.red('FIXME'), msg)
  
          # Get a list of all generated headers that will be needed while building
          # this target's sources (generated sources and pre-existing sources).
          # This will be set as dependencies of all the target's sources. At the
          # same time, also deal with generated sources that need to be compiled.
          generated_source_files = []
          for rel_src, gensrc in generated_sources.items():
              raw_src = RawFilename(rel_src)
              if self.environment.is_source(rel_src) and not self.environment.is_header(rel_src):
                  if is_unity and self.get_target_source_can_unity(target, rel_src):
                      unity_deps.append(raw_src)
                      abs_src = os.path.join(self.environment.get_build_dir(), rel_src)
                      unity_src.append(abs_src)
                  else:
                      generated_source_files.append(raw_src)
              elif self.environment.is_object(rel_src):
                  obj_list.append(rel_src)
              elif self.environment.is_library(rel_src):
                  pass
              else:
                  # Assume anything not specifically a source file is a header. This is because
                  # people generate files with weird suffixes (.inc, .fh) that they then include
                  # in their source files.
                  header_deps.append(raw_src)
          # These are the generated source files that need to be built for use by
          # this target. We create the Ninja build file elements for this here
          # because we need `header_deps` to be fully generated in the above loop.
          for src in generated_source_files:
              if self.environment.is_llvm_ir(src):
                  o = self.generate_llvm_ir_compile(target, outfile, src)
              else:
                  o = self.generate_single_compile(target, outfile, src, True,
                                                   header_deps=header_deps)
              obj_list.append(o)
  
          # Generate compilation targets for C sources generated from Vala
          # sources. This can be extended to other $LANG->C compilers later if
          # necessary. This needs to be separate for at least Vala
          vala_generated_source_files = []
          for src in vala_generated_sources:
              raw_src = RawFilename(src)
              if is_unity:
                  unity_src.append(os.path.join(self.environment.get_build_dir(), src))
                  header_deps.append(raw_src)
              else:
                  # Generated targets are ordered deps because the must exist
                  # before the sources compiling them are used. After the first
                  # compile we get precise dependency
  
  [... the rest is omitted ...]
files: mesonbuild/backend/ninjabackend.py
files: # Copyright 2012-2016 The Meson development team
  
  # Licensed under the Apache License, Version 2.0 (the "License");
  # you may not use this file except in compliance with the License.
  # You may obtain a copy of the License at
  
  #     http://www.apache.org/licenses/LICENSE-2.0
  
  # Unless required by applicable law or agreed to in writing, software
  # distributed under the License is distributed on an "AS IS" BASIS,
  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  # See the License for the specific language governing permissions and
  # limitations under the License.
  
  from . import backends
  from .. import modules
  from .. import environment, mesonlib
  from .. import build
  from .. import mlog
  from .. import dependencies
  from .. import compilers
  from ..compilers import CompilerArgs
  from ..mesonlib import File, MesonException, get_compiler_for_source, Popen_safe
  from .backends import CleanTrees, InstallData
  from ..build import InvalidArguments
  import os, sys, pickle, re
  import subprocess, shutil
  from collections import OrderedDict
  
  if mesonlib.is_windows():
      quote_char = '"'
      execute_wrapper = 'cmd /c'
  else:
      quote_char = "'"
      execute_wrapper = ''
  
  def ninja_quote(text):
      return text.replace(' ', '$ ').replace(':', '$:')
  
  class RawFilename:
      """
      Used when a filename is already relative to the root build directory, so
      that we know not to add the target's private build directory to it.
      """
      def __init__(self, fname):
          self.fname = fname
  
      def __str__(self):
          return self.fname
  
      def __repr__(self):
          return '<RawFilename: {0}>'.format(self.fname)
  
      def split(self, c):
          return self.fname.split(c)
  
      def startswith(self, s):
          return self.fname.startswith(s)
  
  class NinjaBuildElement:
      def __init__(self, all_outputs, outfilenames, rule, infilenames):
          if isinstance(outfilenames, str):
              self.outfilenames = [outfilenames]
          else:
              self.outfilenames = outfilenames
          assert(isinstance(rule, str))
          self.rule = rule
          if isinstance(infilenames, str):
              self.infilenames = [infilenames]
          else:
              self.infilenames = infilenames
          self.deps = []
          self.orderdeps = []
          self.elems = []
          self.all_outputs = all_outputs
  
      def add_dep(self, dep):
          if isinstance(dep, list):
              self.deps += dep
          else:
              self.deps.append(dep)
  
      def add_orderdep(self, dep):
          if isinstance(dep, list):
              self.orderdeps += dep
          else:
              self.orderdeps.append(dep)
  
      def add_item(self, name, elems):
          if isinstance(elems, str):
              elems = [elems]
          self.elems.append((name, elems))
  
      def write(self, outfile):
          self.check_outputs()
          line = 'build %s: %s %s' % (
              ' '.join([ninja_quote(i) for i in self.outfilenames]),
              self.rule,
              ' '.join([ninja_quote(i) for i in self.infilenames]))
          if len(self.deps) > 0:
              line += ' | ' + ' '.join([ninja_quote(x) for x in self.deps])
          if len(self.orderdeps) > 0:
              line += ' || ' + ' '.join([ninja_quote(x) for x in self.orderdeps])
          line += '\n'
          # This is the only way I could find to make this work on all
          # platforms including Windows command shell. Slash is a dir separator
          # on Windows, too, so all characters are unambiguous and, more importantly,
          # do not require quoting.
          line = line.replace('\\', '/')
          outfile.write(line)
  
          for e in self.elems:
              (name, elems) = e
              should_quote = True
              if name == 'DEPFILE' or name == 'DESC' or name == 'pool':
                  should_quote = False
              line = ' %s = ' % name
              q_templ = quote_char + "%s" + quote_char
              noq_templ = "%s"
              newelems = []
              for i in elems:
                  if not should_quote or i == '&&': # Hackety hack hack
                      templ = noq_templ
                  else:
                      templ = q_templ
                  i = i.replace('\\', '\\\\')
                  if quote_char == '"':
                      i = i.replace('"', '\\"')
                  newelems.append(templ % ninja_quote(i))
              line += ' '.join(newelems)
              line += '\n'
              outfile.write(line)
          outfile.write('\n')
  
      def check_outputs(self):
          for n in self.outfilenames:
              if n in self.all_outputs:
                  raise MesonException('Multiple producers for Ninja target "%s". Please rename your targets.' % n)
              self.all_outputs[n] = True
  
  class NinjaBackend(backends.Backend):
  
      def __init__(self, build):
          super().__init__(build)
          self.name = 'ninja'
          self.ninja_filename = 'build.ninja'
          self.fortran_deps = {}
          self.all_outputs = {}
  
      def detect_vs_dep_prefix(self, tempfilename):
          '''VS writes its dependency in a locale dependent format.
          Detect the search prefix to use.'''
          # Of course there is another program called 'cl' on
          # some platforms. Let's just require that on Windows
          # cl points to msvc.
          if not mesonlib.is_windows() or shutil.which('cl') is None:
              return open(tempfilename, 'a')
          filename = os.path.join(self.environment.get_scratch_dir(),
                                  'incdetect.c')
          with open(filename, 'w') as f:
              f.write('''#include<stdio.h>
  int dummy;
  ''')
  
          pc, stdo = Popen_safe(['cl', '/showIncludes', '/c', 'incdetect.c'],
                                cwd=self.environment.get_scratch_dir())[0:2]
  
          for line in stdo.split('\n'):
              if line.endswith('stdio.h'):
                  matchstr = ':'.join(line.split(':')[0:2]) + ':'
                  with open(tempfilename, 'a') as binfile:
                      binfile.write('msvc_deps_prefix = ' + matchstr + '\n')
                  return open(tempfilename, 'a')
          raise MesonException('Could not determine vs dep dependency prefix string.')
  
      def generate(self, interp):
          self.interpreter = interp
          outfilename = os.path.join(self.environment.get_build_dir(), self.ninja_filename)
          tempfilename = outfilename + '~'
          with open(tempfilename, 'w') as outfile:
              outfile.write('# This is the build file for project "%s"\n' %
                            self.build.get_project())
              outfile.write('# It is autogenerated by the Meson build system.\n')
              outfile.write('# Do not edit by hand.\n\n')
              outfile.write('ninja_required_version = 1.5.1\n\n')
          with self.detect_vs_dep_prefix(tempfilename) as outfile:
              self.generate_rules(outfile)
              self.generate_phony(outfile)
              outfile.write('# Build rules for targets\n\n')
              for t in self.build.get_targets().values():
                  self.generate_target(t, outfile)
              outfile.write('# Test rules\n\n')
              self.generate_tests(outfile)
              outfile.write('# Install rules\n\n')
              self.generate_install(outfile)
              if 'b_coverage' in self.environment.coredata.base_options and \
                      self.environment.coredata.base_options['b_coverage'].value:
                  outfile.write('# Coverage rules\n\n')
                  self.generate_coverage_rules(outfile)
              outfile.write('# Suffix\n\n')
              self.generate_utils(outfile)
              self.generate_ending(outfile)
          # Only ovewrite the old build file after the new one has been
          # fully created.
          os.replace(tempfilename, outfilename)
          self.generate_compdb()
  
      # http://clang.llvm.org/docs/JSONCompilationDatabase.html
      def generate_compdb(self):
          ninja_exe = environment.detect_ninja()
          builddir = self.environment.get_build_dir()
          try:
              jsondb = subprocess.check_output([ninja_exe, '-t', 'compdb', 'c_COMPILER', 'cpp_COMPILER'], cwd=builddir)
              with open(os.path.join(builddir, 'compile_commands.json'), 'wb') as f:
                  f.write(jsondb)
          except Exception:
              mlog.warning('Could not create compilation database.')
  
      # Get all generated headers. Any source file might need them so
      # we need to add an order dependency to them.
      def get_generated_headers(self, target):
          header_deps = []
          # XXX: Why don't we add deps to CustomTarget headers here?
          for genlist in target.get_generated_sources():
              if isinstance(genlist, build.CustomTarget):
                  continue
              for src in genlist.get_outputs():
                  if self.environment.is_header(src):
                      header_deps.append(self.get_target_generated_dir(target, genlist, src))
          # Recurse and find generated headers
          for dep in target.link_targets:
              if isinstance(dep, (build.StaticLibrary, build.SharedLibrary)):
                  header_deps += self.get_generated_headers(dep)
          return header_deps
  
      def get_target_generated_sources(self, target):
          """
          Returns a dictionary with the keys being the path to the file
          (relative to the build directory) of that type and the value
          being the GeneratorList or CustomTarget that generated it.
          """
          srcs = OrderedDict()
          for gensrc in target.get_generated_sources():
              for s in gensrc.get_outputs():
                  f = self.get_target_generated_dir(target, gensrc, s)
                  srcs[f] = s
          return srcs
  
      def get_target_sources(self, target):
          srcs = OrderedDict()
          for s in target.get_sources():
              # BuildTarget sources are always mesonlib.File files which are
              # either in the source root, or generated with configure_file and
              # in the build root
              if not isinstance(s, File):
                  raise InvalidArguments('All sources in target {!r} must be of type mesonlib.File'.format(s))
              f = s.rel_to_builddir(self.build_to_src)
              srcs[f] = s
          return srcs
  
      # Languages that can mix with C or C++ but don't support unity builds yet
      # because the syntax we use for unity builds is specific to C/++/ObjC/++.
      # Assembly files cannot be unitified and neither can LLVM IR files
      langs_cant_unity = ('d', 'fortran')
  
      def get_target_source_can_unity(self, target, source):
          if isinstance(source, File):
              source = source.fname
          if self.environment.is_llvm_ir(source) or \
             self.environment.is_assembly(source):
              return False
          suffix = os.path.splitext(source)[1][1:]
          for lang in self.langs_cant_unity:
              if lang not in target.compilers:
                  continue
              if suffix in target.compilers[lang].file_suffixes:
                  return False
          return True
  
      def generate_target(self, target, outfile):
          if isinstance(target, build.CustomTarget):
              self.generate_custom_target(target, outfile)
          if isinstance(target, build.RunTarget):
              self.generate_run_target(target, outfile)
          name = target.get_id()
          if name in self.processed_targets:
              return
          self.processed_targets[name] = True
          # Generate rules for all dependency targets
          self.process_target_dependencies(target, outfile)
          # If target uses a language that cannot link to C objects,
          # just generate for that language and return.
          if isinstance(target, build.Jar):
              self.generate_jar_target(target, outfile)
              return
          if 'rust' in target.compilers:
              self.generate_rust_target(target, outfile)
              return
          if 'cs' in target.compilers:
              self.generate_cs_target(target, outfile)
              return
          if 'swift' in target.compilers:
              self.generate_swift_target(target, outfile)
              return
  
          # Now we handle the following languages:
          # ObjC++, ObjC, C++, C, D, Fortran, Vala
  
          # Pre-existing target C/C++ sources to be built; dict of full path to
          # source relative to build root and the original File object.
          target_sources = OrderedDict()
          # GeneratedList and CustomTarget sources to be built; dict of the full
          # path to source relative to build root and the generating target/list
          generated_sources = OrderedDict()
          # Array of sources generated by valac that have to be compiled
          vala_generated_sources = []
          if 'vala' in target.compilers:
              # Sources consumed by valac are filtered out. These only contain
              # C/C++ sources, objects, generated libs, and unknown sources now.
              target_sources, generated_sources, \
                  vala_generated_sources = self.generate_vala_compile(target, outfile)
          else:
              target_sources = self.get_target_sources(target)
              generated_sources = self.get_target_generated_sources(target)
          self.scan_fortran_module_outputs(target)
          # Generate rules for GeneratedLists
          self.generate_generator_list_rules(target, outfile)
  
          # Generate rules for building the remaining source files in this target
          outname = self.get_target_filename(target)
          obj_list = []
          use_pch = self.environment.coredata.base_options.get('b_pch', False)
          is_unity = self.environment.coredata.get_builtin_option('unity')
          if use_pch and target.has_pch():
              pch_objects = self.generate_pch(target, outfile)
          else:
              pch_objects = []
          header_deps = []
          unity_src = []
          unity_deps = [] # Generated sources that must be built before compiling a Unity target.
          header_deps += self.get_generated_headers(target)
  
          if is_unity:
              # Warn about incompatible sources if a unity build is enabled
              langs = set(target.compilers.keys())
              langs_cant = langs.intersection(self.langs_cant_unity)
              if langs_cant:
                  langs_are = langs = ', '.join(langs_cant).upper()
                  langs_are += ' are' if len(langs_cant) > 1 else ' is'
                  msg = '{} not supported in Unity builds yet, so {} ' \
                        'sources in the {!r} target will be compiled normally' \
                        ''.format(langs_are, langs, target.name)
                  mlog.log(mlog.red('FIXME'), msg)
  
          # Get a list of all generated headers that will be needed while building
          # this target's sources (generated sources and pre-existing sources).
          # This will be set as dependencies of all the target's sources. At the
          # same time, also deal with generated sources that need to be compiled.
          generated_source_files = []
          for rel_src, gensrc in generated_sources.items():
              raw_src = RawFilename(rel_src)
              if self.environment.is_source(rel_src) and not self.environment.is_header(rel_src):
                  if is_unity and self.get_target_source_can_unity(target, rel_src):
                      unity_deps.append(raw_src)
                      abs_src = os.path.join(self.environment.get_build_dir(), rel_src)
                      unity_src.append(abs_src)
                  else:
                      generated_source_files.append(raw_src)
              elif self.environment.is_object(rel_src):
                  obj_list.append(rel_src)
              elif self.environment.is_library(rel_src):
                  pass
              else:
                  # Assume anything not specifically a source file is a header. This is because
                  # people generate files with weird suffixes (.inc, .fh) that they then include
                  # in their source files.
                  header_deps.append(raw_src)
          # These are the generated source files that need to be built for use by
          # this target. We create the Ninja build file elements for this here
          # because we need `header_deps` to be fully generated in the above loop.
          for src in generated_source_files:
              if self.environment.is_llvm_ir(src):
                  o = self.generate_llvm_ir_compile(target, outfile, src)
              else:
                  o = self.generate_single_compile(target, outfile, src, True,
                                                   header_deps=header_deps)
              obj_list.append(o)
  
          # Generate compilation targets for C sources generated from Vala
          # sources. This can be extended to other $LANG->C compilers later if
          # necessary. This needs to be separate for at least Vala
          vala_generated_source_files = []
          for src in vala_generated_sources:
              raw_src = RawFilename(src)
              if is_unity:
                  unity_src.append(os.path.join(self.environment.get_build_dir(), src))
                  header_deps.append(raw_src)
              else:
                  # Generated targets are ordered deps because the must exist
                  # before the sources compiling them are used. After the first
                  # compile we get precise dependency
  
  [... the rest is omitted ...]
files: mesonbuild/backend/ninjabackend.py