#!/usr/bin/python # # libjingle # Copyright 2015 Google Inc. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are met: # # 1. Redistributions of source code must retain the above copyright notice, # this list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the above copyright notice, # this list of conditions and the following disclaimer in the documentation # and/or other materials provided with the distribution. # 3. The name of the author may not be used to endorse or promote products # derived from this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED # WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO # EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; # OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, # WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR # OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF # ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. """Script for merging generated iOS libraries.""" import optparse import os import re import subprocess import sys def MergeLibs(lib_base_dir): """Merges generated iOS libraries for different archs. Uses libtool to generate FAT archive files for each generated library. Args: lib_base_dir: directory whose subdirectories are named by architecture and contain the built libraries for that architecture Returns: Exit code of libtool. """ output_dir_name = 'fat' archs = [arch for arch in os.listdir(lib_base_dir) if arch[:1] != '.' and arch != output_dir_name] # For each arch, find (library name, libary path) for arch. We will merge # all libraries with the same name. libs = {} for dirpath, _, filenames in os.walk(lib_base_dir): if dirpath.endswith(output_dir_name): continue for filename in filenames: if not filename.endswith('.a'): continue entry = libs.get(filename, []) entry.append(os.path.join(dirpath, filename)) libs[filename] = entry orphaned_libs = {} valid_libs = {} for library, paths in libs.items(): if len(paths) < len(archs): orphaned_libs[library] = paths else: valid_libs[library] = paths for library, paths in orphaned_libs.items(): components = library[:-2].split('_')[:-1] found = False # Find directly matching parent libs by stripping suffix. while components and not found: parent_library = '_'.join(components) + '.a' if parent_library in valid_libs: valid_libs[parent_library].extend(paths) found = True break components = components[:-1] # Find next best match by finding parent libs with the same prefix. if not found: base_prefix = library[:-2].split('_')[0] for valid_lib, valid_paths in valid_libs.items(): prefix = '_'.join(components) if valid_lib[:len(base_prefix)] == base_prefix: valid_paths.extend(paths) found = True break assert found # Create output directory. output_dir_path = os.path.join(lib_base_dir, output_dir_name) if not os.path.exists(output_dir_path): os.mkdir(output_dir_path) # Use this so libtool merged binaries are always the same. env = os.environ.copy() env['ZERO_AR_DATE'] = '1' # Ignore certain errors. libtool_re = re.compile(r'^.*libtool:.*file: .* has no symbols$') # Merge libraries using libtool. for library, paths in valid_libs.items(): cmd_list = ['libtool', '-static', '-v', '-o', os.path.join(output_dir_path, library)] + paths libtoolout = subprocess.Popen(cmd_list, stderr=subprocess.PIPE, env=env) _, err = libtoolout.communicate() for line in err.splitlines(): if not libtool_re.match(line): print >>sys.stderr, line # Unconditionally touch the output .a file on the command line if present # and the command succeeded. A bit hacky. if not libtoolout.returncode: for i in range(len(cmd_list) - 1): if cmd_list[i] == '-o' and cmd_list[i+1].endswith('.a'): os.utime(cmd_list[i+1], None) break else: return libtoolout.returncode return libtoolout.returncode def Main(): parser = optparse.OptionParser() _, args = parser.parse_args() if len(args) != 1: parser.error('Error: Exactly 1 argument required.') lib_base_dir = args[0] MergeLibs(lib_base_dir) if __name__ == '__main__': sys.exit(Main())