1271294Sngie#!/usr/bin/env python 2271294Sngie#===----------------------------------------------------------------------===## 3271294Sngie# 4271294Sngie# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 5271294Sngie# See https://llvm.org/LICENSE.txt for license information. 6271294Sngie# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 7271294Sngie# 8271294Sngie#===----------------------------------------------------------------------===## 9271294Sngie 10271294Sngiefrom argparse import ArgumentParser 11271294Sngiefrom ctypes.util import find_library 12271294Sngieimport distutils.spawn 13271294Sngieimport glob 14271294Sngieimport tempfile 15271294Sngieimport os 16271294Sngieimport shutil 17271294Sngieimport subprocess 18271294Sngieimport signal 19271294Sngieimport sys 20271294Sngie 21271294Sngietemp_directory_root = None 22271294Sngiedef exit_with_cleanups(status): 23271294Sngie if temp_directory_root is not None: 24271294Sngie shutil.rmtree(temp_directory_root) 25271294Sngie sys.exit(status) 26271294Sngie 27271294Sngiedef print_and_exit(msg): 28271294Sngie sys.stderr.write(msg + '\n') 29271294Sngie exit_with_cleanups(1) 30271294Sngie 31271294Sngiedef find_and_diagnose_missing(lib, search_paths): 32271294Sngie if os.path.exists(lib): 33271294Sngie return os.path.abspath(lib) 34271294Sngie if not lib.startswith('lib') or not lib.endswith('.a'): 35271294Sngie print_and_exit(("input file '%s' not not name a static library. " 36271294Sngie "It should start with 'lib' and end with '.a") % lib) 37271294Sngie for sp in search_paths: 38271294Sngie assert type(sp) is list and len(sp) == 1 39271294Sngie path = os.path.join(sp[0], lib) 40271294Sngie if os.path.exists(path): 41271294Sngie return os.path.abspath(path) 42271294Sngie print_and_exit("input '%s' does not exist" % lib) 43271294Sngie 44271294Sngie 45271294Sngiedef execute_command(cmd, cwd=None): 46271294Sngie """ 47271294Sngie Execute a command, capture and return its output. 48271294Sngie """ 49271294Sngie kwargs = { 50271294Sngie 'stdin': subprocess.PIPE, 51271294Sngie 'stdout': subprocess.PIPE, 52271294Sngie 'stderr': subprocess.PIPE, 53271294Sngie 'cwd': cwd, 54271294Sngie 'universal_newlines': True 55271294Sngie } 56271294Sngie p = subprocess.Popen(cmd, **kwargs) 57271294Sngie out, err = p.communicate() 58271294Sngie exitCode = p.wait() 59271294Sngie if exitCode == -signal.SIGINT: 60271294Sngie raise KeyboardInterrupt 61271294Sngie return out, err, exitCode 62271294Sngie 63271294Sngie 64271294Sngiedef execute_command_verbose(cmd, cwd=None, verbose=False): 65271294Sngie """ 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 return out 83 84def main(): 85 parser = ArgumentParser( 86 description="Merge multiple archives into a single library") 87 parser.add_argument( 88 '-v', '--verbose', dest='verbose', action='store_true', default=False) 89 parser.add_argument( 90 '-o', '--output', dest='output', required=True, 91 help='The output file. stdout is used if not given', 92 type=str, action='store') 93 parser.add_argument( 94 '-L', dest='search_paths', 95 help='Paths to search for the libraries along', action='append', 96 nargs=1, default=[]) 97 parser.add_argument( 98 '--ar', dest='ar_exe', required=False, 99 help='The ar executable to use, finds \'ar\' in the path if not given', 100 type=str, action='store') 101 parser.add_argument( 102 '--use-libtool', dest='use_libtool', action='store_true', default=False) 103 parser.add_argument( 104 '--libtool', dest='libtool_exe', required=False, 105 help='The libtool executable to use, finds \'libtool\' in the path if not given', 106 type=str, action='store') 107 parser.add_argument( 108 'archives', metavar='archives', nargs='+', 109 help='The archives to merge') 110 111 args = parser.parse_args() 112 113 ar_exe = args.ar_exe 114 if not ar_exe: 115 ar_exe = distutils.spawn.find_executable('ar') 116 if not ar_exe: 117 print_and_exit("failed to find 'ar' executable") 118 119 if args.use_libtool: 120 libtool_exe = args.libtool_exe 121 if not libtool_exe: 122 libtool_exe = distutils.spawn.find_executable('libtool') 123 if not libtool_exe: 124 print_and_exit("failed to find 'libtool' executable") 125 126 if len(args.archives) < 2: 127 print_and_exit('fewer than 2 inputs provided') 128 archives = [find_and_diagnose_missing(ar, args.search_paths) 129 for ar in args.archives] 130 print ('Merging archives: %s' % archives) 131 if not os.path.exists(os.path.dirname(args.output)): 132 print_and_exit("output path doesn't exist: '%s'" % args.output) 133 134 global temp_directory_root 135 temp_directory_root = tempfile.mkdtemp('.libcxx.merge.archives') 136 137 files = [] 138 for arc in archives: 139 execute_command_verbose([ar_exe, 'x', arc], 140 cwd=temp_directory_root, verbose=args.verbose) 141 out = execute_command_verbose([ar_exe, 't', arc]) 142 files.extend(out.splitlines()) 143 144 if args.use_libtool: 145 files = [f for f in files if not f.startswith('__.SYMDEF')] 146 execute_command_verbose([libtool_exe, '-static', '-o', args.output] + files, 147 cwd=temp_directory_root, verbose=args.verbose) 148 else: 149 execute_command_verbose([ar_exe, 'rcs', args.output] + files, 150 cwd=temp_directory_root, verbose=args.verbose) 151 152 153if __name__ == '__main__': 154 main() 155 exit_with_cleanups(0) 156