blob: 58d92f0e21e1e15cdcf01b641ceaa00be4280a7c [file] [log] [blame]
Eric Fiselierfa054d22016-11-18 11:26:14 +00001#!/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
11from argparse import ArgumentParser
Eric Fiselier60479ea2016-11-18 19:53:45 +000012from ctypes.util import find_library
Eric Fiselierfa054d22016-11-18 11:26:14 +000013import distutils.spawn
14import glob
15import tempfile
16import os
17import shutil
18import subprocess
19import signal
20import sys
21
22temp_directory_root = None
23def exit_with_cleanups(status):
24 if temp_directory_root is not None:
25 shutil.rmtree(temp_directory_root)
26 sys.exit(status)
27
28def print_and_exit(msg):
29 sys.stderr.write(msg + '\n')
30 exit_with_cleanups(1)
31
Eric Fiselier60479ea2016-11-18 19:53:45 +000032def 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)
Eric Fiselierfa054d22016-11-18 11:26:14 +000044
45
46def 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
64def 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
83def 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(
Eric Fiselier60479ea2016-11-18 19:53:45 +000093 '-L', dest='search_paths',
94 help='Paths to search for the libraries along', action='append',
95 nargs=1)
96 parser.add_argument(
Martin Storsjo5f919fe2017-09-13 06:55:44 +000097 '--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(
Eric Fiselierfa054d22016-11-18 11:26:14 +0000101 'archives', metavar='archives', nargs='+',
102 help='The archives to merge')
103
104 args = parser.parse_args()
105
Martin Storsjo5f919fe2017-09-13 06:55:44 +0000106 ar_exe = args.ar_exe
107 if not ar_exe:
108 ar_exe = distutils.spawn.find_executable('ar')
Eric Fiselierfa054d22016-11-18 11:26:14 +0000109 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')
Eric Fiselier60479ea2016-11-18 19:53:45 +0000114 archives = [find_and_diagnose_missing(ar, args.search_paths)
115 for ar in args.archives]
116 print ('Merging archives: %s' % archives)
Eric Fiselierfa054d22016-11-18 11:26:14 +0000117 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:
Martin Storsjo5f919fe2017-09-13 06:55:44 +0000124 execute_command_verbose([ar_exe, 'x', arc], cwd=temp_directory_root,
Eric Fiselierfa054d22016-11-18 11:26:14 +0000125 verbose=args.verbose)
126
Martin Storsjo2c607f42017-09-12 20:54:15 +0000127 files = glob.glob(os.path.join(temp_directory_root, '*.o*'))
Eric Fiselierfa054d22016-11-18 11:26:14 +0000128 if not files:
Martell Malone8122e242017-09-12 22:32:02 +0000129 print_and_exit('Failed to glob for %s' % temp_directory_root)
Martin Storsjo5f919fe2017-09-13 06:55:44 +0000130 cmd = [ar_exe, 'qcs', args.output] + files
Eric Fiselierfa054d22016-11-18 11:26:14 +0000131 execute_command_verbose(cmd, cwd=temp_directory_root, verbose=args.verbose)
132
133
134if __name__ == '__main__':
135 main()
136 exit_with_cleanups(0)