2016-11-18 19:26:14 +08:00
|
|
|
#!/usr/bin/env python
|
|
|
|
#===----------------------------------------------------------------------===##
|
|
|
|
#
|
|
|
|
# The LLVM Compiler Infrastructure
|
|
|
|
#
|
|
|
|
# This file is dual licensed under the MIT and the University of Illinois Open
|
|
|
|
# Source Licenses. See LICENSE.TXT for details.
|
|
|
|
#
|
|
|
|
#===----------------------------------------------------------------------===##
|
|
|
|
|
|
|
|
from argparse import ArgumentParser
|
2016-11-19 03:53:45 +08:00
|
|
|
from ctypes.util import find_library
|
2016-11-18 19:26:14 +08:00
|
|
|
import distutils.spawn
|
|
|
|
import glob
|
|
|
|
import tempfile
|
|
|
|
import os
|
|
|
|
import shutil
|
|
|
|
import subprocess
|
|
|
|
import signal
|
|
|
|
import sys
|
|
|
|
|
|
|
|
temp_directory_root = None
|
|
|
|
def exit_with_cleanups(status):
|
|
|
|
if temp_directory_root is not None:
|
|
|
|
shutil.rmtree(temp_directory_root)
|
|
|
|
sys.exit(status)
|
|
|
|
|
|
|
|
def print_and_exit(msg):
|
|
|
|
sys.stderr.write(msg + '\n')
|
|
|
|
exit_with_cleanups(1)
|
|
|
|
|
2016-11-19 03:53:45 +08:00
|
|
|
def find_and_diagnose_missing(lib, search_paths):
|
|
|
|
if os.path.exists(lib):
|
|
|
|
return os.path.abspath(lib)
|
|
|
|
if not lib.startswith('lib') or not lib.endswith('.a'):
|
|
|
|
print_and_exit(("input file '%s' not not name a static library. "
|
|
|
|
"It should start with 'lib' and end with '.a") % lib)
|
|
|
|
for sp in search_paths:
|
|
|
|
assert type(sp) is list and len(sp) == 1
|
|
|
|
path = os.path.join(sp[0], lib)
|
|
|
|
if os.path.exists(path):
|
|
|
|
return os.path.abspath(path)
|
|
|
|
print_and_exit("input '%s' does not exist" % lib)
|
2016-11-18 19:26:14 +08:00
|
|
|
|
|
|
|
|
|
|
|
def execute_command(cmd, cwd=None):
|
|
|
|
"""
|
|
|
|
Execute a command, capture and return its output.
|
|
|
|
"""
|
|
|
|
kwargs = {
|
|
|
|
'stdin': subprocess.PIPE,
|
|
|
|
'stdout': subprocess.PIPE,
|
|
|
|
'stderr': subprocess.PIPE,
|
|
|
|
'cwd': cwd
|
|
|
|
}
|
|
|
|
p = subprocess.Popen(cmd, **kwargs)
|
|
|
|
out, err = p.communicate()
|
|
|
|
exitCode = p.wait()
|
|
|
|
if exitCode == -signal.SIGINT:
|
|
|
|
raise KeyboardInterrupt
|
|
|
|
return out, err, exitCode
|
|
|
|
|
|
|
|
|
|
|
|
def execute_command_verbose(cmd, cwd=None, verbose=False):
|
|
|
|
"""
|
|
|
|
Execute a command and print its output on failure.
|
|
|
|
"""
|
|
|
|
out, err, exitCode = execute_command(cmd, cwd=cwd)
|
|
|
|
if exitCode != 0 or verbose:
|
|
|
|
report = "Command: %s\n" % ' '.join(["'%s'" % a for a in cmd])
|
|
|
|
if exitCode != 0:
|
|
|
|
report += "Exit Code: %d\n" % exitCode
|
|
|
|
if out:
|
|
|
|
report += "Standard Output:\n--\n%s--" % out
|
|
|
|
if err:
|
|
|
|
report += "Standard Error:\n--\n%s--" % err
|
|
|
|
if exitCode != 0:
|
|
|
|
report += "\n\nFailed!"
|
|
|
|
sys.stderr.write('%s\n' % report)
|
|
|
|
if exitCode != 0:
|
|
|
|
exit_with_cleanups(exitCode)
|
|
|
|
|
|
|
|
def main():
|
|
|
|
parser = ArgumentParser(
|
|
|
|
description="Merge multiple archives into a single library")
|
|
|
|
parser.add_argument(
|
|
|
|
'-v', '--verbose', dest='verbose', action='store_true', default=False)
|
|
|
|
parser.add_argument(
|
|
|
|
'-o', '--output', dest='output', required=True,
|
|
|
|
help='The output file. stdout is used if not given',
|
|
|
|
type=str, action='store')
|
2016-11-19 03:53:45 +08:00
|
|
|
parser.add_argument(
|
|
|
|
'-L', dest='search_paths',
|
|
|
|
help='Paths to search for the libraries along', action='append',
|
|
|
|
nargs=1)
|
2017-09-13 14:55:44 +08:00
|
|
|
parser.add_argument(
|
|
|
|
'--ar', dest='ar_exe', required=False,
|
|
|
|
help='The ar executable to use, finds \'ar\' in the path if not given',
|
|
|
|
type=str, action='store')
|
2016-11-18 19:26:14 +08:00
|
|
|
parser.add_argument(
|
|
|
|
'archives', metavar='archives', nargs='+',
|
|
|
|
help='The archives to merge')
|
|
|
|
|
|
|
|
args = parser.parse_args()
|
|
|
|
|
2017-09-13 14:55:44 +08:00
|
|
|
ar_exe = args.ar_exe
|
|
|
|
if not ar_exe:
|
|
|
|
ar_exe = distutils.spawn.find_executable('ar')
|
2016-11-18 19:26:14 +08:00
|
|
|
if not ar_exe:
|
|
|
|
print_and_exit("failed to find 'ar' executable")
|
|
|
|
|
|
|
|
if len(args.archives) < 2:
|
|
|
|
print_and_exit('fewer than 2 inputs provided')
|
2016-11-19 03:53:45 +08:00
|
|
|
archives = [find_and_diagnose_missing(ar, args.search_paths)
|
|
|
|
for ar in args.archives]
|
|
|
|
print ('Merging archives: %s' % archives)
|
2016-11-18 19:26:14 +08:00
|
|
|
if not os.path.exists(os.path.dirname(args.output)):
|
|
|
|
print_and_exit("output path doesn't exist: '%s'" % args.output)
|
|
|
|
|
|
|
|
global temp_directory_root
|
|
|
|
temp_directory_root = tempfile.mkdtemp('.libcxx.merge.archives')
|
|
|
|
|
|
|
|
for arc in archives:
|
2017-09-13 14:55:44 +08:00
|
|
|
execute_command_verbose([ar_exe, 'x', arc], cwd=temp_directory_root,
|
2016-11-18 19:26:14 +08:00
|
|
|
verbose=args.verbose)
|
|
|
|
|
2017-09-13 04:54:15 +08:00
|
|
|
files = glob.glob(os.path.join(temp_directory_root, '*.o*'))
|
2016-11-18 19:26:14 +08:00
|
|
|
if not files:
|
2017-09-13 06:32:02 +08:00
|
|
|
print_and_exit('Failed to glob for %s' % temp_directory_root)
|
2017-09-13 14:55:44 +08:00
|
|
|
cmd = [ar_exe, 'qcs', args.output] + files
|
2016-11-18 19:26:14 +08:00
|
|
|
execute_command_verbose(cmd, cwd=temp_directory_root, verbose=args.verbose)
|
|
|
|
|
|
|
|
|
|
|
|
if __name__ == '__main__':
|
|
|
|
main()
|
|
|
|
exit_with_cleanups(0)
|