blob: b8ba316f842d9ecc84e4212ed23fce1c5b97cad5 [file] [log] [blame]
Eric Fiselier1ae2fd42016-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
12import distutils.spawn
13import glob
14import tempfile
15import os
16import shutil
17import subprocess
18import signal
19import sys
20
21temp_directory_root = None
22def exit_with_cleanups(status):
23 if temp_directory_root is not None:
24 shutil.rmtree(temp_directory_root)
25 sys.exit(status)
26
27def print_and_exit(msg):
28 sys.stderr.write(msg + '\n')
29 exit_with_cleanups(1)
30
31def diagnose_missing(file):
32 if not os.path.exists(file):
33 print_and_exit("input '%s' does not exist" % file)
34
35
36def execute_command(cmd, cwd=None):
37 """
38 Execute a command, capture and return its output.
39 """
40 kwargs = {
41 'stdin': subprocess.PIPE,
42 'stdout': subprocess.PIPE,
43 'stderr': subprocess.PIPE,
44 'cwd': cwd
45 }
46 p = subprocess.Popen(cmd, **kwargs)
47 out, err = p.communicate()
48 exitCode = p.wait()
49 if exitCode == -signal.SIGINT:
50 raise KeyboardInterrupt
51 return out, err, exitCode
52
53
54def execute_command_verbose(cmd, cwd=None, verbose=False):
55 """
56 Execute a command and print its output on failure.
57 """
58 out, err, exitCode = execute_command(cmd, cwd=cwd)
59 if exitCode != 0 or verbose:
60 report = "Command: %s\n" % ' '.join(["'%s'" % a for a in cmd])
61 if exitCode != 0:
62 report += "Exit Code: %d\n" % exitCode
63 if out:
64 report += "Standard Output:\n--\n%s--" % out
65 if err:
66 report += "Standard Error:\n--\n%s--" % err
67 if exitCode != 0:
68 report += "\n\nFailed!"
69 sys.stderr.write('%s\n' % report)
70 if exitCode != 0:
71 exit_with_cleanups(exitCode)
72
73def main():
74 parser = ArgumentParser(
75 description="Merge multiple archives into a single library")
76 parser.add_argument(
77 '-v', '--verbose', dest='verbose', action='store_true', default=False)
78 parser.add_argument(
79 '-o', '--output', dest='output', required=True,
80 help='The output file. stdout is used if not given',
81 type=str, action='store')
82 parser.add_argument(
83 'archives', metavar='archives', nargs='+',
84 help='The archives to merge')
85
86 args = parser.parse_args()
87
88 ar_exe = distutils.spawn.find_executable('ar')
89 if not ar_exe:
90 print_and_exit("failed to find 'ar' executable")
91
92 if len(args.archives) < 2:
93 print_and_exit('fewer than 2 inputs provided')
94 archives = []
95 for ar in args.archives:
96 diagnose_missing(ar)
97 # Make the path absolute so it isn't affected when we change the PWD.
98 archives += [os.path.abspath(ar)]
99
100 if not os.path.exists(os.path.dirname(args.output)):
101 print_and_exit("output path doesn't exist: '%s'" % args.output)
102
103 global temp_directory_root
104 temp_directory_root = tempfile.mkdtemp('.libcxx.merge.archives')
105
106 for arc in archives:
107 execute_command_verbose([ar_exe, '-x', arc], cwd=temp_directory_root,
108 verbose=args.verbose)
109
110 files = glob.glob(os.path.join(temp_directory_root, '*.o'))
111 if not files:
112 print_and_exit('Failed to glob for %s' % glob_path)
113 cmd = [ar_exe, '-qc', args.output] + files
114 execute_command_verbose(cmd, cwd=temp_directory_root, verbose=args.verbose)
115
116
117if __name__ == '__main__':
118 main()
119 exit_with_cleanups(0)