xref: /aosp_15_r20/external/webrtc/tools_webrtc/ios/merge_ios_libs.py (revision d9f758449e529ab9291ac668be2861e7a55c2422)
1#!/usr/bin/env vpython3
2
3#  Copyright 2016 The WebRTC project authors. All Rights Reserved.
4#
5#  Use of this source code is governed by a BSD-style license
6#  that can be found in the LICENSE file in the root of the source
7#  tree. An additional intellectual property rights grant can be found
8#  in the file PATENTS.  All contributing project authors may
9#  be found in the AUTHORS file in the root of the source tree.
10"""Script for merging generated iOS libraries."""
11
12import sys
13import argparse
14import os
15import re
16import subprocess
17from six.moves import range
18
19
20# Valid arch subdir names.
21VALID_ARCHS = ['arm_libs', 'arm64_libs', 'ia32_libs', 'x64_libs']
22
23
24def MergeLibs(lib_base_dir):
25  """Merges generated iOS libraries for different archs.
26
27  Uses libtool to generate FAT archive files for each generated library.
28
29  Args:
30    lib_base_dir: directory whose subdirectories are named by architecture and
31                  contain the built libraries for that architecture
32
33  Returns:
34    Exit code of libtool.
35  """
36  output_dir_name = 'fat_libs'
37  archs = [arch for arch in os.listdir(lib_base_dir) if arch in VALID_ARCHS]
38  # For each arch, find (library name, libary path) for arch. We will merge
39  # all libraries with the same name.
40  libs = {}
41  for lib_dir in [os.path.join(lib_base_dir, arch) for arch in VALID_ARCHS]:
42    if not os.path.exists(lib_dir):
43      continue
44    for dirpath, _, filenames in os.walk(lib_dir):
45      for filename in filenames:
46        if not filename.endswith('.a'):
47          continue
48        entry = libs.get(filename, [])
49        entry.append(os.path.join(dirpath, filename))
50        libs[filename] = entry
51  orphaned_libs = {}
52  valid_libs = {}
53  for library, paths in list(libs.items()):
54    if len(paths) < len(archs):
55      orphaned_libs[library] = paths
56    else:
57      valid_libs[library] = paths
58  for library, paths in list(orphaned_libs.items()):
59    components = library[:-2].split('_')[:-1]
60    found = False
61    # Find directly matching parent libs by stripping suffix.
62    while components and not found:
63      parent_library = '_'.join(components) + '.a'
64      if parent_library in valid_libs:
65        valid_libs[parent_library].extend(paths)
66        found = True
67        break
68      components = components[:-1]
69    # Find next best match by finding parent libs with the same prefix.
70    if not found:
71      base_prefix = library[:-2].split('_')[0]
72      for valid_lib, valid_paths in list(valid_libs.items()):
73        if valid_lib[:len(base_prefix)] == base_prefix:
74          valid_paths.extend(paths)
75          found = True
76          break
77    assert found
78
79  # Create output directory.
80  output_dir_path = os.path.join(lib_base_dir, output_dir_name)
81  if not os.path.exists(output_dir_path):
82    os.mkdir(output_dir_path)
83
84  # Use this so libtool merged binaries are always the same.
85  env = os.environ.copy()
86  env['ZERO_AR_DATE'] = '1'
87
88  # Ignore certain errors.
89  libtool_re = re.compile(r'^.*libtool:.*file: .* has no symbols$')
90
91  # Merge libraries using libtool.
92  libtool_returncode = 0
93  for library, paths in list(valid_libs.items()):
94    cmd_list = [
95        'libtool', '-static', '-v', '-o',
96        os.path.join(output_dir_path, library)
97    ] + paths
98    libtoolout = subprocess.Popen(cmd_list, stderr=subprocess.PIPE, env=env)
99    _, err = libtoolout.communicate()
100    for line in err.splitlines():
101      if not libtool_re.match(line):
102        print(line, file=sys.stderr)
103    # Unconditionally touch the output .a file on the command line if present
104    # and the command succeeded. A bit hacky.
105    libtool_returncode = libtoolout.returncode
106    if not libtool_returncode:
107      for i in range(len(cmd_list) - 1):
108        if cmd_list[i] == '-o' and cmd_list[i + 1].endswith('.a'):
109          os.utime(cmd_list[i + 1], None)
110          break
111  return libtool_returncode
112
113
114def main():
115  parser_description = 'Merge WebRTC libraries.'
116  parser = argparse.ArgumentParser(description=parser_description)
117  parser.add_argument('lib_base_dir',
118                      help='Directory with built libraries. ',
119                      type=str)
120  args = parser.parse_args()
121  lib_base_dir = args.lib_base_dir
122  MergeLibs(lib_base_dir)
123
124
125if __name__ == '__main__':
126  sys.exit(main())
127