def eval_transfer_file()

in tools/make_distrib.py [0:0]


def eval_transfer_file(cef_dir, script_dir, transfer_cfg, output_dir, quiet):
  """ Transfer files based on the specified configuration. """
  if not path_exists(transfer_cfg):
    return

  configs = eval_file(transfer_cfg)
  for cfg in configs:
    dst = os.path.join(output_dir, cfg['target'])

    # perform a copy if source is specified
    if not cfg['source'] is None:
      src = os.path.join(cef_dir, cfg['source'])
      dst_path = os.path.dirname(dst)
      make_dir(dst_path, quiet)
      copy_file(src, dst, quiet)

      # place a readme file in the destination directory
      readme = os.path.join(dst_path, 'README-TRANSFER.txt')
      if not path_exists(readme):
        copy_file(
            os.path.join(script_dir, 'distrib/README-TRANSFER.txt'), readme)

      str = cfg['source'] + "\n"
      with open(readme, 'a', encoding='utf-8') as fp:
        if sys.version_info.major == 2:
          fp.write(str.decode('utf-8'))
        else:
          fp.write(str)

    # perform any required post-processing
    if 'post-process' in cfg:
      post = cfg['post-process']
      if post == 'normalize_headers':
        new_path = ''
        if 'new_header_path' in cfg:
          new_path = cfg['new_header_path']
        normalize_headers(dst, new_path)


def transfer_files(cef_dir, script_dir, transfer_cfg_dir, mode, output_dir,
                   quiet):
  # Non-mode-specific transfers.
  transfer_cfg = os.path.join(transfer_cfg_dir, 'transfer.cfg')
  eval_transfer_file(cef_dir, script_dir, transfer_cfg, output_dir, quiet)
  # Mode-specific transfers.
  transfer_cfg = os.path.join(transfer_cfg_dir, 'transfer_%s.cfg' % mode)
  eval_transfer_file(cef_dir, script_dir, transfer_cfg, output_dir, quiet)


# |paths| is a list of dictionary values with the following keys:
# path        [required]  Input file or directory path relative to |build_dir|.
#                         By default this will also be the output path relative
#                         to |dst_dir|.
# out_path    [optional]  Override the output path relative to |dst_dir|.
# conditional [optional]  Set to True if the path is conditional on build
#                         settings. Missing conditional paths will not be
#                         treated as an error.
# delete      [optional]  Glob pattern of files to delete after the copy.
def copy_files_list(build_dir, dst_dir, paths):
  ''' Copy the files listed in |paths| from |build_dir| to |dst_dir|. '''
  for entry in paths:
    source_path = os.path.join(build_dir, entry['path'])
    if os.path.exists(source_path):
      target_path = os.path.join(dst_dir, entry['out_path']
                                 if 'out_path' in entry else entry['path'])
      make_dir(os.path.dirname(target_path), options.quiet)
      if os.path.isdir(source_path):
        copy_dir(source_path, target_path, options.quiet)
        if 'delete' in entry:
          for delete_path in get_files(
              os.path.join(target_path, entry['delete'])):
            if not os.path.isdir(delete_path):
              remove_file(delete_path, options.quiet)
            else:
              raise Exception('Refusing to delete directory: %s' % delete_path)
      else:
        copy_file(source_path, target_path, options.quiet)
    else:
      if 'conditional' in entry and entry['conditional']:
        sys.stdout.write('Missing conditional path: %s.\n' % source_path)
      else:
        raise Exception('Missing required path: %s' % source_path)


def get_exported_symbols(file):
  """ Returns the global symbols exported by |file|. """
  symbols = []

  # Each symbol line has a value like:
  # 0000000000000000 T _cef_sandbox_initialize
  cmdline = 'nm -g -U %s' % file
  result = exec_cmd(cmdline, os.path.join(cef_dir, 'tools'))
  if len(result['err']) > 0:
    raise Exception('ERROR: nm failed: %s' % result['err'])
  for line in result['out'].split('\n'):
    if line.find(' T ') < 0:
      continue
    symbol = line[line.rfind(' ') + 1:]
    symbols.append(symbol)

  return symbols


def get_undefined_symbols(file):
  """ Returns the undefined symbols imported by |file|. """
  symbols = []

  # Each symbol line has a value like:
  # cef_sandbox.a:cef_sandbox.o: _memcpy
  cmdline = 'nm -u -A %s' % file
  result = exec_cmd(cmdline, os.path.join(cef_dir, 'tools'))
  if len(result['err']) > 0:
    raise Exception('ERROR: nm failed: %s' % result['err'])
  for line in result['out'].split('\n'):
    if line.find(': ') < 0:
      continue
    symbol = line[line.rfind(': ') + 2:]
    symbols.append(symbol)

  return symbols


def combine_libs(platform, build_dir, libs, dest_lib):
  """ Combine multiple static libraries into a single static library. """
  intermediate_obj = None
  if platform == 'windows':
    cmdline = 'msvs_env.bat win%s "%s" combine_libs.py -o "%s"' % (
        platform_arch, sys.executable, dest_lib)
  elif platform == 'mac':
    # Find CEF_EXPORT symbols from libcef_sandbox.a (include/cef_sandbox_mac.h)
    # Export only symbols that include these strings.
    symbol_match = [
        '_cef_',  # C symbols
        'Cef',  # C++ symbols
    ]

    print('Finding exported symbols...')
    assert 'libcef_sandbox.a' in libs[0], libs[0]
    symbols = []
    for symbol in get_exported_symbols(os.path.join(build_dir, libs[0])):
      for match in symbol_match:
        if symbol.find(match) >= 0:
          symbols.append(symbol)
          break
    assert len(symbols) > 0

    # Create an intermediate object file that combines all other object files.
    # Symbols not identified above will be made private (local).
    intermediate_obj = os.path.splitext(dest_lib)[0] + '.o'
    arch = 'arm64' if options.arm64build else 'x86_64'
    cmdline = 'ld -arch %s -r -o "%s"' % (arch, intermediate_obj)
    for symbol in symbols:
      cmdline += ' -exported_symbol %s' % symbol

  for lib in libs:
    lib_path = os.path.join(build_dir, lib)
    for path in get_files(lib_path):  # Expand wildcards in |lib_path|.
      if not path_exists(path):
        raise Exception('File not found: ' + path)
      cmdline += ' "%s"' % path
  run(cmdline, os.path.join(cef_dir, 'tools'))

  if not intermediate_obj is None:
    # Create an archive file containing the new object file.
    cmdline = 'libtool -static -o "%s" "%s"' % (dest_lib, intermediate_obj)
    run(cmdline, os.path.join(cef_dir, 'tools'))
    remove_file(intermediate_obj)

    # Verify that only the expected symbols are exported from the archive file.
    print('Verifying exported symbols...')
    result_symbols = get_exported_symbols(dest_lib)
    if set(symbols) != set(result_symbols):
      print('Expected', symbols)
      print('Got', result_symbols)
      raise Exception('Failure verifying exported symbols')

    # Verify that no C++ symbols are imported by the archive file. If the
    # archive imports C++ symbols and the client app links an incompatible C++
    # library, the result will be undefined behavior.
    # For example, to avoid importing libc++ symbols the cef_sandbox target
    # should have a dependency on libc++abi. This dependency can be verified
    # with the following command:
    # gn path out/[config] //cef:cef_sandbox //buildtools/third_party/libc++abi
    print('Verifying imported (undefined) symbols...')
    undefined_symbols = get_undefined_symbols(dest_lib)
    cpp_symbols = list(
        filter(lambda symbol: symbol.startswith('__Z'), undefined_symbols))
    if cpp_symbols:
      print('Found C++ symbols:', cpp_symbols)
      raise Exception('Failure verifying imported (undefined) symbols')


def run(command_line, working_dir):
  """ Run a command. """