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