1 #!/usr/bin/env python 2 #===----------------------------------------------------------------------===## 3 # 4 # The LLVM Compiler Infrastructure 5 # 6 # This file is dual licensed under the MIT and the University of Illinois Open 7 # Source Licenses. See LICENSE.TXT for details. 8 # 9 #===----------------------------------------------------------------------===## 10 11 from argparse import ArgumentParser 12 from ctypes.util import find_library 13 import distutils.spawn 14 import glob 15 import tempfile 16 import os 17 import shutil 18 import subprocess 19 import signal 20 import sys 21 22 temp_directory_root = None 23 def exit_with_cleanups(status): 24 if temp_directory_root is not None: 25 shutil.rmtree(temp_directory_root) 26 sys.exit(status) 27 28 def print_and_exit(msg): 29 sys.stderr.write(msg + '\n') 30 exit_with_cleanups(1) 31 32 def find_and_diagnose_missing(lib, search_paths): 33 if os.path.exists(lib): 34 return os.path.abspath(lib) 35 if not lib.startswith('lib') or not lib.endswith('.a'): 36 print_and_exit(("input file '%s' not not name a static library. " 37 "It should start with 'lib' and end with '.a") % lib) 38 for sp in search_paths: 39 assert type(sp) is list and len(sp) == 1 40 path = os.path.join(sp[0], lib) 41 if os.path.exists(path): 42 return os.path.abspath(path) 43 print_and_exit("input '%s' does not exist" % lib) 44 45 46 def execute_command(cmd, cwd=None): 47 """ 48 Execute a command, capture and return its output. 49 """ 50 kwargs = { 51 'stdin': subprocess.PIPE, 52 'stdout': subprocess.PIPE, 53 'stderr': subprocess.PIPE, 54 'cwd': cwd 55 } 56 p = subprocess.Popen(cmd, **kwargs) 57 out, err = p.communicate() 58 exitCode = p.wait() 59 if exitCode == -signal.SIGINT: 60 raise KeyboardInterrupt 61 return out, err, exitCode 62 63 64 def execute_command_verbose(cmd, cwd=None, verbose=False): 65 """ 66 Execute a command and print its output on failure. 67 """ 68 out, err, exitCode = execute_command(cmd, cwd=cwd) 69 if exitCode != 0 or verbose: 70 report = "Command: %s\n" % ' '.join(["'%s'" % a for a in cmd]) 71 if exitCode != 0: 72 report += "Exit Code: %d\n" % exitCode 73 if out: 74 report += "Standard Output:\n--\n%s--" % out 75 if err: 76 report += "Standard Error:\n--\n%s--" % err 77 if exitCode != 0: 78 report += "\n\nFailed!" 79 sys.stderr.write('%s\n' % report) 80 if exitCode != 0: 81 exit_with_cleanups(exitCode) 82 83 def main(): 84 parser = ArgumentParser( 85 description="Merge multiple archives into a single library") 86 parser.add_argument( 87 '-v', '--verbose', dest='verbose', action='store_true', default=False) 88 parser.add_argument( 89 '-o', '--output', dest='output', required=True, 90 help='The output file. stdout is used if not given', 91 type=str, action='store') 92 parser.add_argument( 93 '-L', dest='search_paths', 94 help='Paths to search for the libraries along', action='append', 95 nargs=1) 96 parser.add_argument( 97 '--ar', dest='ar_exe', required=False, 98 help='The ar executable to use, finds \'ar\' in the path if not given', 99 type=str, action='store') 100 parser.add_argument( 101 'archives', metavar='archives', nargs='+', 102 help='The archives to merge') 103 104 args = parser.parse_args() 105 106 ar_exe = args.ar_exe 107 if not ar_exe: 108 ar_exe = distutils.spawn.find_executable('ar') 109 if not ar_exe: 110 print_and_exit("failed to find 'ar' executable") 111 112 if len(args.archives) < 2: 113 print_and_exit('fewer than 2 inputs provided') 114 archives = [find_and_diagnose_missing(ar, args.search_paths) 115 for ar in args.archives] 116 print ('Merging archives: %s' % archives) 117 if not os.path.exists(os.path.dirname(args.output)): 118 print_and_exit("output path doesn't exist: '%s'" % args.output) 119 120 global temp_directory_root 121 temp_directory_root = tempfile.mkdtemp('.libcxx.merge.archives') 122 123 for arc in archives: 124 execute_command_verbose([ar_exe, 'x', arc], cwd=temp_directory_root, 125 verbose=args.verbose) 126 127 files = glob.glob(os.path.join(temp_directory_root, '*.o*')) 128 if not files: 129 print_and_exit('Failed to glob for %s' % temp_directory_root) 130 cmd = [ar_exe, 'qcs', args.output] + files 131 execute_command_verbose(cmd, cwd=temp_directory_root, verbose=args.verbose) 132 133 134 if __name__ == '__main__': 135 main() 136 exit_with_cleanups(0) 137