#!/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 # Some sublibaries are only present in certain architectures. We merge # them into their parent library so that the library list is consistent # across architectures. libs_copy = dict(libs) for library, paths in libs.items(): if len(paths) < len(archs): # Hacky: we find parent libraries by stripping off each name component. components = library.strip('.a').split('_')[:-1] found = False while components: parent_library = '_'.join(components) + '.a' if (parent_library in libs_copy and len(libs_copy[parent_library]) >= len(archs)): libs[parent_library].extend(paths) del libs[library] found = True break components = components[:-1] 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 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())