Ejemplo n.º 1
0
def GenerateOutput(target_list, target_dicts, data, params):
    """Main entry point for this generator.

  gyp will call this function.
  """
    options = params['options']
    makefilename = os.path.join(options.toplevel_dir, 'Makefile')
    makefile = open(makefilename, 'w')

    build_file, _, _ = gyp.common.ParseQualifiedTarget(target_list[0])
    make_global_settings = data[build_file].get('make_global_settings', [])
    settings_map = dict((key, value) for key, value in make_global_settings)

    target_info = target_dicts[target_list[0]]

    params = {
        'CC.target': GetEnvironFallback(['CC_target'], '$(CC)'),
        'AR.target': GetEnvironFallback(['AR_target'], '$(AR)'),
        'ARFLAGS.target': GetEnvironFallback(['ARFLAGS_target'], 'crs'),
        'CXX.target': GetEnvironFallback(['CXX_target'], '$(CXX)'),
        'LINK.target': GetEnvironFallback(['LINK_target'], '$(LINK)'),
        'ARFLAGS.host': GetEnvironFallback(['ARFLAGS_host'], 'crs'),
        'default_config': target_info['default_configuration'],
        'all_configs': ' '.join(target_info['configurations'].keys()),
    }

    params.update(settings_map)
    makefile.write(preamble % params)

    for target_info in target_dicts.values():
        WriteTarget(makefile, target_info)

    makefile.write('''
# include (if they exists) the .d dependancy files that the compiler generates
-include $(DEPFILES)

endif
''')

    makefile.close()
Ejemplo n.º 2
0
Archivo: make.py Proyecto: tatewake/GYP
def GenerateOutput(target_list, target_dicts, data, params):
  from gyp.MakefileWriter import MakefileWriter, Sourceify, WriteRootHeaderSuffixRules, SHARED_HEADER
  options = params['options']
  flavor = gyp.common.GetFlavor(params)
  generator_flags = params.get('generator_flags', {})
  builddir_name = generator_flags.get('output_dir', 'out')
  android_ndk_version = generator_flags.get('android_ndk_version', None)
  default_target = generator_flags.get('default_target', 'all')

  def CalculateMakefilePath(build_file_arg, base_name):
    """Determine where to write a Makefile for a given gyp file."""
    # Paths in gyp files are relative to the .gyp file, but we want
    # paths relative to the source root for the master makefile.  Grab
    # the path of the .gyp file as the base to relativize against.
    # E.g. "foo/bar" when we're constructing targets for "foo/bar/baz.gyp".
    base_makefile_path = gyp.common.RelativePath(os.path.dirname(build_file_arg), options.depth)
    # We write the file in the base_makefile_path directory.
    output_makefile = os.path.join(options.depth, base_makefile_path, base_name)
    if options.generator_output:
      output_makefile = os.path.join(options.depth, options.generator_output, base_makefile_path, base_name)
    base_makefile_path = gyp.common.RelativePath(os.path.dirname(build_file_arg), options.toplevel_dir)
    return base_makefile_path, output_makefile

  # TODO:  search for the first non-'Default' target.  This can go
  # away when we add verification that all targets have the
  # necessary configurations.
  default_configuration = None
  toolsets = set([target_dicts[target]['toolset'] for target in target_list])
  for target in target_list:
    spec = target_dicts[target]
    if spec['default_configuration'] != 'Default':
      default_configuration = spec['default_configuration']
      break
  if not default_configuration:
    default_configuration = 'Default'

  srcdir = '.'
  makefile_name = 'Makefile' + options.suffix
  makefile_path = os.path.join(options.toplevel_dir, makefile_name)
  if options.generator_output:
    makefile_path = os.path.join(options.toplevel_dir, options.generator_output, makefile_name)
    srcdir = gyp.common.RelativePath(srcdir, options.generator_output)
    Sourceify.srcdir_prefix = '$(srcdir)/'

  flock_command = 'flock'
  copy_archive_arguments = '-af'
  makedep_arguments = '-MMD'
  header_params = {
    'default_target': default_target,
    'builddir': builddir_name,
    'default_configuration': default_configuration,
    'flock': flock_command,
    'flock_index': 1,
    'link_commands': LINK_COMMANDS_LINUX,
    'extra_commands': '',
    'srcdir': srcdir,
    'copy_archive_args': copy_archive_arguments,
    'makedep_args': makedep_arguments,
  }
  if flavor == 'mac':
    flock_command = './gyp-mac-tool flock'
    header_params.update({
      'flock': flock_command,
      'flock_index': 2,
      'link_commands': LINK_COMMANDS_MAC,
      'extra_commands': SHARED_HEADER_MAC_COMMANDS,
    })
  elif flavor == 'android':
    header_params.update({
      'link_commands': LINK_COMMANDS_ANDROID,
    })
  elif flavor == 'zos':
    copy_archive_arguments = '-fPR'
    makedep_arguments = '-qmakedep=gcc'
    header_params.update({
      'copy_archive_args': copy_archive_arguments,
      'makedep_args': makedep_arguments,
      'link_commands': LINK_COMMANDS_OS390,
    })
  elif flavor == 'solaris':
    header_params.update({
      'flock': './gyp-flock-tool flock',
      'flock_index': 2,
    })
  elif flavor == 'freebsd':
    # Note: OpenBSD has sysutils/flock. lockf seems to be FreeBSD specific.
    header_params.update({
      'flock': 'lockf',
    })
  elif flavor == 'openbsd':
    copy_archive_arguments = '-pPRf'
    header_params.update({
      'copy_archive_args': copy_archive_arguments,
    })
  elif flavor == 'aix':
    copy_archive_arguments = '-pPRf'
    header_params.update({
      'copy_archive_args': copy_archive_arguments,
      'link_commands': LINK_COMMANDS_AIX,
      'flock': './gyp-flock-tool flock',
      'flock_index': 2,
    })

  header_params.update({
    'CC.target': GetEnvironFallback(('CC_target', 'CC'), '$(CC)'),
    'AR.target': GetEnvironFallback(('AR_target', 'AR'), '$(AR)'),
    'CXX.target': GetEnvironFallback(('CXX_target', 'CXX'), '$(CXX)'),
    'LINK.target': GetEnvironFallback(('LINK_target', 'LINK'), '$(LINK)'),
    'CC.host':     GetEnvironFallback(('CC_host',), 'cc'),
    'AR.host':     GetEnvironFallback(('AR_host',), 'ar'),
    'CXX.host':    GetEnvironFallback(('CXX_host',), 'c++'),
    'LINK.host':   GetEnvironFallback(('LINK_host',), '$(CXX.host)'),
  })

  build_file, _, _ = gyp.common.ParseQualifiedTarget(target_list[0])
  make_global_settings_array = data[build_file].get('make_global_settings', [])
  wrappers = {}
  for key, value in make_global_settings_array:
    if key.endswith('_wrapper'):
      wrappers[key[:-len('_wrapper')]] = '$(abspath %s)' % value
  make_global_settings = ''
  for key, value in make_global_settings_array:
    if re.match('.*_wrapper', key):
      continue
    if value[0] != '$':
      value = '$(abspath %s)' % value
    wrapper = wrappers.get(key)
    if wrapper:
      value = '%s %s' % (wrapper, value)
      del wrappers[key]
    if key in ('CC', 'CC.host', 'CXX', 'CXX.host'):
      make_global_settings += 'ifneq (,$(filter $(origin %s), undefined default))\n' % key
      # Let gyp-time envvars win over global settings.
      env_key = key.replace('.', '_')  # CC.host -> CC_host
      if env_key in os.environ:
        value = os.environ[env_key]
      make_global_settings += '  %s = %s\n' % (key, value)
      make_global_settings += 'endif\n'
    else:
      make_global_settings += '%s ?= %s\n' % (key, value)
  # TODO(ukai): define cmd when only wrapper is specified in
  # make_global_settings.

  header_params['make_global_settings'] = make_global_settings

  gyp.common.EnsureDirExists(makefile_path)
  root_makefile = open(makefile_path, 'w')
  root_makefile.write(SHARED_HEADER % header_params)
  # Currently any versions have the same effect, but in future the behavior
  # could be different.
  if android_ndk_version:
    root_makefile.write(
      '# Define LOCAL_PATH for build of Android applications.\n'
      'LOCAL_PATH := $(call my-dir)\n'
      '\n'
    )
  for toolset in toolsets:
    root_makefile.write('TOOLSET := %s\n' % toolset)
    WriteRootHeaderSuffixRules(root_makefile)

  # Put build-time support tools next to the root Makefile.
  dest_path = os.path.dirname(makefile_path)
  gyp.common.CopyTool(flavor, dest_path)

  # Find the list of targets that derive from the gyp file(s) being built.
  needed_targets = set()
  for build_file in params['build_files']:
    for target in gyp.common.AllTargets(target_list, target_dicts, build_file):
      needed_targets.add(target)

  build_files = set()
  include_list = set()
  writer = None
  for qualified_target in target_list:
    build_file, target, toolset = gyp.common.ParseQualifiedTarget(qualified_target)

    this_make_global_settings = data[build_file].get('make_global_settings', [])
    assert make_global_settings_array == this_make_global_settings, (
        "make_global_settings needs to be the same for all targets. %s vs. %s" %
        (this_make_global_settings, make_global_settings))

    build_files.add(gyp.common.RelativePath(build_file, options.toplevel_dir))
    included_files = data[build_file]['included_files']
    for included_file in included_files:
      # The included_files entries are relative to the dir of the build file
      # that included them, so we have to undo that and then make them relative
      # to the root dir.
      relative_include_file = gyp.common.RelativePath(
        gyp.common.UnrelativePath(included_file, build_file),
        options.toplevel_dir
      )
      abs_include_file = os.path.abspath(relative_include_file)
      # If the include file is from the ~/.gyp dir, we should use absolute path
      # so that relocating the src dir doesn't break the path.
      if params['home_dot_gyp'] and abs_include_file.startswith(params['home_dot_gyp']):
        build_files.add(abs_include_file)
      else:
        build_files.add(relative_include_file)

    base_path, output_file = CalculateMakefilePath(build_file, target + '.' + toolset + options.suffix + '.mk')

    spec = target_dicts[qualified_target]
    configs = spec['configurations']

    if flavor == 'mac':
      gyp.xcode_emulation.MergeGlobalXcodeSettingsToSpec(data[build_file], spec)

    writer = MakefileWriter(generator_flags, flavor)
    writer.Write(qualified_target, base_path, output_file, spec, configs, part_of_all=qualified_target in needed_targets)

    # Our root_makefile lives at the source root.  Compute the relative path
    # from there to the output_file for including.
    mkfile_rel_path = gyp.common.RelativePath(output_file, os.path.dirname(makefile_path))
    include_list.add(mkfile_rel_path)

  assert writer
  # Write out per-gyp (sub-project) Makefiles.
  depth_rel_path = gyp.common.RelativePath(options.depth, os.getcwd())
  for build_file in build_files:
    # The paths in build_files were relativized above, so undo that before
    # testing against the non-relativized items in target_list and before
    # calculating the Makefile path.
    build_file_path = os.path.join(depth_rel_path, build_file)
    related_gyp_targets = [t for t in target_list if t.startswith(build_file) and t in needed_targets]
    # Only generate Makefiles for gyp files with targets.
    if not related_gyp_targets:
      continue
    build_file_name = "%s.Makefile" % os.path.splitext(os.path.basename(build_file))[0]
    _, submake_output_file = CalculateMakefilePath(build_file_path, build_file_name)
    makefile_rel_path = gyp.common.RelativePath(os.path.dirname(makefile_path), os.path.dirname(submake_output_file))
    gyp_targets_names = [target_dicts[t]['target_name'] for t in related_gyp_targets]
    writer.WriteSubMake(submake_output_file, makefile_rel_path, gyp_targets_names, builddir_name)

  # Write out the sorted list of includes.
  root_makefile.write('\n')
  for include_file in sorted(include_list):
    # We wrap each .mk include in an if statement so users can tell make to
    # not load a file by setting NO_LOAD.  The below make code says, only
    # load the .mk file if the .mk filename doesn't start with a token in
    # NO_LOAD.
    include_conditional_tmpl="""\
ifeq ($(strip $(foreach prefix,$(NO_LOAD), $(findstring $(join ^,$(prefix)), $(join ^,%(include_file)s)))),)
  include %(include_file)s
endif
"""
    root_makefile.write(include_conditional_tmpl % { 'include_file': include_file })
  root_makefile.write('\n')

  if (not generator_flags.get('standalone')
      and generator_flags.get('auto_regeneration', True)):
    WriteAutoRegenerationRule(params, root_makefile, makefile_name, build_files, Sourceify)

  root_makefile.write(SHARED_FOOTER)

  root_makefile.close()
Ejemplo n.º 3
0
Archivo: ninja.py Proyecto: rvagg/GYP
def GenerateOutputForConfig(target_list, target_dicts, data, params,
                            config_name):
    options = params['options']
    flavor = gyp.common.GetFlavor(params)
    generator_flags = params.get('generator_flags', OrderedDict())

    # build_dir: relative path from source root to our output files.
    # e.g. "out/Debug"
    build_dir = os.path.normpath(
        os.path.join(ComputeOutputDir(params), config_name))

    toplevel_build = os.path.join(options.toplevel_dir, build_dir)

    master_ninja_file = OpenOutput(os.path.join(toplevel_build, 'build.ninja'))
    master_ninja_writer = ninja_syntax.Writer(master_ninja_file, width=250)

    # Put build-time support tools in out/{config_name}.
    gyp.common.CopyTool(flavor, toplevel_build,
                        generator_flags.get('mac_toolchain_dir'))

    # Grab make settings for CC/CXX.
    # The rules are
    # - The priority from low to high is gcc/g++, the 'make_global_settings' in
    #   gyp, the environment variable.
    # - If there is no 'make_global_settings' for CC.host/CXX.host or
    #   'CC_host'/'CXX_host' enviroment variable, cc_host/cxx_host should be set
    #   to cc/cxx.
    if flavor == 'win':
        ar = 'lib.exe'
        # cc and cxx must be set to the correct architecture by overriding with one
        # of cl_x86 or cl_x64 below.
        cc = 'cl.exe'
        cxx = 'cl.exe'
        ld = 'link.exe'
        ld_host = '$ld'
        ldxx = 'UNSET'
        ldxx_host = 'UNSET'
    else:
        ar = 'ar'
        cc = 'cc'
        cxx = 'c++'
        ld = '$cc'
        ldxx = '$cxx'
        ld_host = '$cc_host'
        ldxx_host = '$cxx_host'

    ar_host = ar
    cc_host = None
    cxx_host = None
    cc_host_global_setting = None
    cxx_host_global_setting = None
    nm = 'nm'
    nm_host = 'nm'
    readelf = 'readelf'
    readelf_host = 'readelf'

    build_file, _, _ = gyp.common.ParseQualifiedTarget(target_list[0])
    make_global_settings = data[build_file].get('make_global_settings', [])
    build_to_root = gyp.common.InvertRelativePath(build_dir,
                                                  options.toplevel_dir)
    wrappers = {}
    for key, value in make_global_settings:
        if key == 'AR':
            ar = os.path.join(build_to_root, value)
        if key == 'AR.host':
            ar_host = os.path.join(build_to_root, value)
        if key == 'CC':
            cc = os.path.join(build_to_root, value)
        if key == 'CXX':
            cxx = os.path.join(build_to_root, value)
        if key == 'CC.host':
            cc_host = os.path.join(build_to_root, value)
            cc_host_global_setting = value
        if key == 'CXX.host':
            cxx_host = os.path.join(build_to_root, value)
            cxx_host_global_setting = value
        if key == 'LD':
            ld = os.path.join(build_to_root, value)
        if key == 'LD.host':
            ld_host = os.path.join(build_to_root, value)
        if key == 'NM':
            nm = os.path.join(build_to_root, value)
        if key == 'NM.host':
            nm_host = os.path.join(build_to_root, value)
        if key == 'READELF':
            readelf = os.path.join(build_to_root, value)
        if key == 'READELF.host':
            readelf_host = os.path.join(build_to_root, value)
        if key.endswith('_wrapper'):
            wrappers[key[:-len('_wrapper')]] = os.path.join(
                build_to_root, value)

    # Support wrappers from environment variables too.
    for key, value in os.environ.items():
        if key.lower().endswith('_wrapper'):
            key_prefix = key[:-len('_wrapper')]
            key_prefix = re.sub(r'\.HOST$', '.host', key_prefix)
            wrappers[key_prefix] = os.path.join(build_to_root, value)

    mac_toolchain_dir = generator_flags.get('mac_toolchain_dir', None)
    if mac_toolchain_dir:
        wrappers['LINK'] = "export DEVELOPER_DIR='%s' &&" % mac_toolchain_dir

    if flavor == 'win':
        gyp.msvs_emulation.GenerateEnvironmentFiles(toplevel_build,
                                                    generator_flags)

    cc = GetEnvironFallback(['CC_target', 'CC'], cc)
    master_ninja_writer.variable('cc', CommandWithWrapper('CC', wrappers, cc))
    cxx = GetEnvironFallback(['CXX_target', 'CXX'], cxx)
    master_ninja_writer.variable('cxx',
                                 CommandWithWrapper('CXX', wrappers, cxx))

    if flavor == 'win':
        master_ninja_writer.variable('ld', ld)
        master_ninja_writer.variable('idl', 'midl.exe')
        master_ninja_writer.variable('ar', ar)
        master_ninja_writer.variable('rc', 'rc.exe')
        master_ninja_writer.variable('ml_x86', 'ml.exe')
        master_ninja_writer.variable('ml_x64', 'ml64.exe')
        master_ninja_writer.variable('mt', 'mt.exe')
    else:
        master_ninja_writer.variable('ld',
                                     CommandWithWrapper('LINK', wrappers, ld))
        master_ninja_writer.variable(
            'ldxx', CommandWithWrapper('LINK', wrappers, ldxx))
        master_ninja_writer.variable(
            'ar', GetEnvironFallback(['AR_target', 'AR'], ar))
        if flavor != 'mac':
            # Mac does not use readelf/nm for .TOC generation, so avoiding polluting
            # the master ninja with extra unused variables.
            master_ninja_writer.variable(
                'nm', GetEnvironFallback(['NM_target', 'NM'], nm))
            master_ninja_writer.variable(
                'readelf',
                GetEnvironFallback(['READELF_target', 'READELF'], readelf))

    if generator_supports_multiple_toolsets:
        if not cc_host:
            cc_host = cc
        if not cxx_host:
            cxx_host = cxx

        master_ninja_writer.variable('ar_host',
                                     GetEnvironFallback(['AR_host'], ar_host))
        master_ninja_writer.variable('nm_host',
                                     GetEnvironFallback(['NM_host'], nm_host))
        master_ninja_writer.variable(
            'readelf_host', GetEnvironFallback(['READELF_host'], readelf_host))
        cc_host = GetEnvironFallback(['CC_host'], cc_host)
        cxx_host = GetEnvironFallback(['CXX_host'], cxx_host)

        # The environment variable could be used in 'make_global_settings', like
        # ['CC.host', '$(CC)'] or ['CXX.host', '$(CXX)'], transform them here.
        if '$(CC)' in cc_host and cc_host_global_setting:
            cc_host = cc_host_global_setting.replace('$(CC)', cc)
        if '$(CXX)' in cxx_host and cxx_host_global_setting:
            cxx_host = cxx_host_global_setting.replace('$(CXX)', cxx)
        master_ninja_writer.variable(
            'cc_host', CommandWithWrapper('CC.host', wrappers, cc_host))
        master_ninja_writer.variable(
            'cxx_host', CommandWithWrapper('CXX.host', wrappers, cxx_host))
        if flavor == 'win':
            master_ninja_writer.variable('ld_host', ld_host)
        else:
            master_ninja_writer.variable(
                'ld_host', CommandWithWrapper('LINK', wrappers, ld_host))
            master_ninja_writer.variable(
                'ldxx_host', CommandWithWrapper('LINK', wrappers, ldxx_host))

    master_ninja_writer.newline()

    master_ninja_writer.pool('link_pool', depth=GetDefaultConcurrentLinks())
    master_ninja_writer.newline()

    deps = 'msvc' if flavor == 'win' else 'gcc'

    if flavor != 'win':
        master_ninja_writer.rule(
            'cc',
            description='CC $out',
            command=(
                '$cc -MMD -MF $out.d $defines $includes $cflags $cflags_c '
                '$cflags_pch_c -c $in -o $out'),
            depfile='$out.d',
            deps=deps)
        master_ninja_writer.rule(
            'cc_s',
            description='CC $out',
            command=('$cc $defines $includes $cflags $cflags_c '
                     '$cflags_pch_c -c $in -o $out'))
        master_ninja_writer.rule(
            'cxx',
            description='CXX $out',
            command=(
                '$cxx -MMD -MF $out.d $defines $includes $cflags $cflags_cc '
                '$cflags_pch_cc -c $in -o $out'),
            depfile='$out.d',
            deps=deps)
    else:
        # TODO(scottmg) Separate pdb names is a test to see if it works around
        # http://crbug.com/142362. It seems there's a race between the creation of
        # the .pdb by the precompiled header step for .cc and the compilation of
        # .c files. This should be handled by mspdbsrv, but rarely errors out with
        #   c1xx : fatal error C1033: cannot open program database
        # By making the rules target separate pdb files this might be avoided.
        cc_command = ('ninja -t msvc -e $arch ' + '-- '
                      '$cc /nologo /showIncludes /FC '
                      '@$out.rsp /c $in /Fo$out /Fd$pdbname_c ')
        cxx_command = ('ninja -t msvc -e $arch ' + '-- '
                       '$cxx /nologo /showIncludes /FC '
                       '@$out.rsp /c $in /Fo$out /Fd$pdbname_cc ')
        master_ninja_writer.rule(
            'cc',
            description='CC $out',
            command=cc_command,
            rspfile='$out.rsp',
            rspfile_content='$defines $includes $cflags $cflags_c',
            deps=deps)
        master_ninja_writer.rule(
            'cxx',
            description='CXX $out',
            command=cxx_command,
            rspfile='$out.rsp',
            rspfile_content='$defines $includes $cflags $cflags_cc',
            deps=deps)
        master_ninja_writer.rule(
            'idl',
            description='IDL $in',
            command=('%s gyp-win-tool midl-wrapper $arch $outdir '
                     '$tlb $h $dlldata $iid $proxy $in '
                     '$midl_includes $idlflags' % sys.executable))
        master_ninja_writer.rule(
            'rc',
            description='RC $in',
            # Note: $in must be last otherwise rc.exe complains.
            command=(
                '%s gyp-win-tool rc-wrapper '
                '$arch $rc $defines $resource_includes $rcflags /fo$out $in' %
                sys.executable))
        master_ninja_writer.rule(
            'asm',
            description='ASM $out',
            command=(
                '%s gyp-win-tool asm-wrapper '
                '$arch $asm $defines $includes $asmflags /c /Fo $out $in' %
                sys.executable))

    if flavor != 'mac' and flavor != 'win':
        master_ninja_writer.rule(
            'alink',
            description='AR $out',
            command='rm -f $out && $ar rcs $arflags $out $in')
        master_ninja_writer.rule(
            'alink_thin',
            description='AR $out',
            command='rm -f $out && $ar rcsT $arflags $out $in')

        # This allows targets that only need to depend on $lib's API to declare an
        # order-only dependency on $lib.TOC and avoid relinking such downstream
        # dependencies when $lib changes only in non-public ways.
        # The resulting string leaves an uninterpolated %{suffix} which
        # is used in the final substitution below.
        mtime_preserving_solink_base = (
            'if [ ! -e $lib -o ! -e $lib.TOC ]; then '
            '%(solink)s && %(extract_toc)s > $lib.TOC; else '
            '%(solink)s && %(extract_toc)s > $lib.tmp && '
            'if ! cmp -s $lib.tmp $lib.TOC; then mv $lib.tmp $lib.TOC ; '
            'fi; fi' % {
                'solink':
                '$ld -shared $ldflags -o $lib -Wl,-soname=$soname %(suffix)s',
                'extract_toc': ('{ $readelf -d $lib | grep SONAME ; '
                                '$nm -gD -f p $lib | cut -f1-2 -d\' \'; }')
            })

        master_ninja_writer.rule(
            'solink',
            description='SOLINK $lib',
            restat=True,
            command=mtime_preserving_solink_base %
            {'suffix': '@$link_file_list'},
            rspfile='$link_file_list',
            rspfile_content=
            '-Wl,--whole-archive $in $solibs -Wl,--no-whole-archive $libs',
            pool='link_pool')
        master_ninja_writer.rule(
            'solink_module',
            description='SOLINK(module) $lib',
            restat=True,
            command=mtime_preserving_solink_base %
            {'suffix': '@$link_file_list'},
            rspfile='$link_file_list',
            rspfile_content=
            '-Wl,--start-group $in -Wl,--end-group $solibs $libs',
            pool='link_pool')
        master_ninja_writer.rule(
            'link',
            description='LINK $out',
            command=('$ld $ldflags -o $out '
                     '-Wl,--start-group $in -Wl,--end-group $solibs $libs'),
            pool='link_pool')
    elif flavor == 'win':
        master_ninja_writer.rule(
            'alink',
            description='LIB $out',
            command=('%s gyp-win-tool link-wrapper $arch False '
                     '$ar /nologo /ignore:4221 /OUT:$out @$out.rsp' %
                     sys.executable),
            rspfile='$out.rsp',
            rspfile_content='$in_newline $libflags')
        _AddWinLinkRules(master_ninja_writer, embed_manifest=True)
        _AddWinLinkRules(master_ninja_writer, embed_manifest=False)
    else:
        master_ninja_writer.rule(
            'objc',
            description='OBJC $out',
            command=(
                '$cc -MMD -MF $out.d $defines $includes $cflags $cflags_objc '
                '$cflags_pch_objc -c $in -o $out'),
            depfile='$out.d',
            deps=deps)
        master_ninja_writer.rule(
            'objcxx',
            description='OBJCXX $out',
            command=(
                '$cxx -MMD -MF $out.d $defines $includes $cflags $cflags_objcc '
                '$cflags_pch_objcc -c $in -o $out'),
            depfile='$out.d',
            deps=deps)
        master_ninja_writer.rule(
            'alink',
            description='LIBTOOL-STATIC $out, POSTBUILDS',
            command='rm -f $out && '
            './gyp-mac-tool filter-libtool libtool $libtool_flags '
            '-static -o $out $in'
            '$postbuilds')
        master_ninja_writer.rule(
            'lipo',
            description='LIPO $out, POSTBUILDS',
            command='rm -f $out && lipo -create $in -output $out$postbuilds')
        master_ninja_writer.rule(
            'solipo',
            description='SOLIPO $out, POSTBUILDS',
            command=
            ('rm -f $lib $lib.TOC && lipo -create $in -output $lib$postbuilds &&'
             '%(extract_toc)s > $lib.TOC' % {
                 'extract_toc':
                 '{ otool -l $lib | grep LC_ID_DYLIB -A 5; '
                 'nm -gP $lib | cut -f1-2 -d\' \' | grep -v U$$; true; }'
             }))

        # Record the public interface of $lib in $lib.TOC. See the corresponding
        # comment in the posix section above for details.
        solink_base = '$ld %(type)s $ldflags -o $lib %(suffix)s'
        mtime_preserving_solink_base = (
            'if [ ! -e $lib -o ! -e $lib.TOC ] || '
            # Always force dependent targets to relink if this library
            # reexports something. Handling this correctly would require
            # recursive TOC dumping but this is rare in practice, so punt.
            'otool -l $lib | grep -q LC_REEXPORT_DYLIB ; then '
            '%(solink)s && %(extract_toc)s > $lib.TOC; '
            'else '
            '%(solink)s && %(extract_toc)s > $lib.tmp && '
            'if ! cmp -s $lib.tmp $lib.TOC; then '
            'mv $lib.tmp $lib.TOC ; '
            'fi; '
            'fi' % {
                'solink':
                solink_base,
                'extract_toc':
                '{ otool -l $lib | grep LC_ID_DYLIB -A 5; '
                'nm -gP $lib | cut -f1-2 -d\' \' | grep -v U$$; true; }'
            })

        solink_suffix = '@$link_file_list$postbuilds'
        master_ninja_writer.rule('solink',
                                 description='SOLINK $lib, POSTBUILDS',
                                 restat=True,
                                 command=mtime_preserving_solink_base % {
                                     'suffix': solink_suffix,
                                     'type': '-shared'
                                 },
                                 rspfile='$link_file_list',
                                 rspfile_content='$in $solibs $libs',
                                 pool='link_pool')
        master_ninja_writer.rule('solink_notoc',
                                 description='SOLINK $lib, POSTBUILDS',
                                 restat=True,
                                 command=solink_base % {
                                     'suffix': solink_suffix,
                                     'type': '-shared'
                                 },
                                 rspfile='$link_file_list',
                                 rspfile_content='$in $solibs $libs',
                                 pool='link_pool')

        master_ninja_writer.rule('solink_module',
                                 description='SOLINK(module) $lib, POSTBUILDS',
                                 restat=True,
                                 command=mtime_preserving_solink_base % {
                                     'suffix': solink_suffix,
                                     'type': '-bundle'
                                 },
                                 rspfile='$link_file_list',
                                 rspfile_content='$in $solibs $libs',
                                 pool='link_pool')
        master_ninja_writer.rule('solink_module_notoc',
                                 description='SOLINK(module) $lib, POSTBUILDS',
                                 restat=True,
                                 command=solink_base % {
                                     'suffix': solink_suffix,
                                     'type': '-bundle'
                                 },
                                 rspfile='$link_file_list',
                                 rspfile_content='$in $solibs $libs',
                                 pool='link_pool')

        master_ninja_writer.rule('link',
                                 description='LINK $out, POSTBUILDS',
                                 command=('$ld $ldflags -o $out '
                                          '$in $solibs $libs$postbuilds'),
                                 pool='link_pool')
        master_ninja_writer.rule(
            'preprocess_infoplist',
            description='PREPROCESS INFOPLIST $out',
            command=('$cc -E -P -Wno-trigraphs -x c $defines $in -o $out && '
                     'plutil -convert xml1 $out $out'))
        master_ninja_writer.rule(
            'copy_infoplist',
            description='COPY INFOPLIST $in',
            command='$env ./gyp-mac-tool copy-info-plist $in $out $binary $keys'
        )
        master_ninja_writer.rule(
            'merge_infoplist',
            description='MERGE INFOPLISTS $in',
            command='$env ./gyp-mac-tool merge-info-plist $out $in')
        master_ninja_writer.rule(
            'compile_xcassets',
            description='COMPILE XCASSETS $in',
            command='$env ./gyp-mac-tool compile-xcassets $keys $in')
        master_ninja_writer.rule(
            'compile_ios_framework_headers',
            description='COMPILE HEADER MAPS AND COPY FRAMEWORK HEADERS $in',
            command='$env ./gyp-mac-tool compile-ios-framework-header-map $out '
            '$framework $in && $env ./gyp-mac-tool '
            'copy-ios-framework-headers $framework $copy_headers')
        master_ninja_writer.rule(
            'mac_tool',
            description='MACTOOL $mactool_cmd $in',
            command='$env ./gyp-mac-tool $mactool_cmd $in $out $binary')
        master_ninja_writer.rule(
            'package_framework',
            description='PACKAGE FRAMEWORK $out, POSTBUILDS',
            command='./gyp-mac-tool package-framework $out $version$postbuilds '
            '&& touch $out')
        master_ninja_writer.rule(
            'package_ios_framework',
            description='PACKAGE IOS FRAMEWORK $out, POSTBUILDS',
            command='./gyp-mac-tool package-ios-framework $out $postbuilds '
            '&& touch $out')
    if flavor == 'win':
        master_ninja_writer.rule('stamp',
                                 description='STAMP $out',
                                 command='%s gyp-win-tool stamp $out' %
                                 sys.executable)
    else:
        master_ninja_writer.rule('stamp',
                                 description='STAMP $out',
                                 command='${postbuilds}touch $out')
    if flavor == 'win':
        master_ninja_writer.rule(
            'copy',
            description='COPY $in $out',
            command='%s gyp-win-tool recursive-mirror $in $out' %
            sys.executable)
    elif flavor == 'zos':
        master_ninja_writer.rule('copy',
                                 description='COPY $in $out',
                                 command='rm -rf $out && cp -fRP $in $out')
    else:
        master_ninja_writer.rule(
            'copy',
            description='COPY $in $out',
            command=
            'ln -f $in $out 2>/dev/null || (rm -rf $out && cp -af $in $out)')
    master_ninja_writer.newline()

    all_targets = set()
    for build_file in params['build_files']:
        for target in gyp.common.AllTargets(target_list, target_dicts,
                                            os.path.normpath(build_file)):
            all_targets.add(target)
    all_outputs = set()

    # target_outputs is a map from qualified target name to a Target object.
    target_outputs = {}
    # target_short_names is a map from target short name to a list of Target
    # objects.
    target_short_names = {}

    # short name of targets that were skipped because they didn't contain anything
    # interesting.
    # NOTE: there may be overlap between this an non_empty_target_names.
    empty_target_names = set()

    # Set of non-empty short target names.
    # NOTE: there may be overlap between this an empty_target_names.
    non_empty_target_names = set()

    for qualified_target in target_list:
        # qualified_target is like: third_party/icu/icu.gyp:icui18n#target
        build_file, name, toolset = gyp.common.ParseQualifiedTarget(
            qualified_target)

        this_make_global_settings = data[build_file].get(
            'make_global_settings', [])
        assert make_global_settings == this_make_global_settings, (
            "make_global_settings needs to be the same for all targets. %s vs. %s"
            % (this_make_global_settings, make_global_settings))

        spec = target_dicts[qualified_target]
        if flavor == 'mac':
            gyp.xcode_emulation.MergeGlobalXcodeSettingsToSpec(
                data[build_file], spec)

        # If build_file is a symlink, we must not follow it because there's a chance
        # it could point to a path above toplevel_dir, and we cannot correctly deal
        # with that case at the moment.
        build_file = gyp.common.RelativePath(build_file, options.toplevel_dir,
                                             False)

        qualified_target_for_hash = gyp.common.QualifiedTarget(
            build_file, name, toolset)
        qualified_target_for_hash = qualified_target_for_hash.encode('utf-8')
        hash_for_rules = hashlib.md5(qualified_target_for_hash).hexdigest()

        base_path = os.path.dirname(build_file)
        obj = 'obj'
        if toolset != 'target':
            obj += '.' + toolset
        output_file = os.path.join(obj, base_path, name + '.ninja')

        ninja_output = StringIO()
        from gyp.NinjaWriter import NinjaWriter
        writer = NinjaWriter(hash_for_rules, target_outputs, base_path,
                             build_dir, ninja_output, toplevel_build,
                             output_file, flavor, spec, generator_flags,
                             config_name, options.toplevel_dir)

        target = writer.WriteSpec()

        if ninja_output.tell() > 0:
            # Only create files for ninja files that actually have contents.
            with OpenOutput(os.path.join(toplevel_build,
                                         output_file)) as ninja_file:
                ninja_file.write(ninja_output.getvalue())
            ninja_output.close()
            master_ninja_writer.subninja(output_file)

        if target:
            if name != target.FinalOutput() and spec['toolset'] == 'target':
                target_short_names.setdefault(name, []).append(target)
            target_outputs[qualified_target] = target
            if qualified_target in all_targets:
                all_outputs.add(target.FinalOutput())
            non_empty_target_names.add(name)
        else:
            empty_target_names.add(name)

    if target_short_names:
        # Write a short name to build this target.  This benefits both the
        # "build chrome" case as well as the gyp tests, which expect to be
        # able to run actions and build libraries by their short name.
        master_ninja_writer.newline()
        master_ninja_writer.comment('Short names for targets.')
        for short_name in sorted(target_short_names):
            master_ninja_writer.build(
                short_name, 'phony',
                [x.FinalOutput() for x in target_short_names[short_name]])

    # Write phony targets for any empty targets that weren't written yet. As
    # short names are  not necessarily unique only do this for short names that
    # haven't already been output for another target.
    empty_target_names = empty_target_names - non_empty_target_names
    if empty_target_names:
        master_ninja_writer.newline()
        master_ninja_writer.comment('Empty targets (output for completeness).')
        for name in sorted(empty_target_names):
            master_ninja_writer.build(name, 'phony')

    if all_outputs:
        master_ninja_writer.newline()
        master_ninja_writer.build('all', 'phony', sorted(all_outputs))
        master_ninja_writer.default(
            generator_flags.get('default_target', 'all'))

    master_ninja_file.close()