150
|
1 #!/usr/bin/env python
|
|
2 #===----------------------------------------------------------------------===##
|
|
3 #
|
|
4 # Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
5 # See https://llvm.org/LICENSE.txt for license information.
|
|
6 # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
7 #
|
|
8 #===----------------------------------------------------------------------===##
|
|
9
|
|
10 from argparse import ArgumentParser
|
|
11 from ctypes.util import find_library
|
|
12 import distutils.spawn
|
|
13 import glob
|
|
14 import tempfile
|
|
15 import os
|
|
16 import shutil
|
|
17 import subprocess
|
|
18 import signal
|
|
19 import sys
|
|
20
|
|
21 temp_directory_root = None
|
|
22 def exit_with_cleanups(status):
|
|
23 if temp_directory_root is not None:
|
|
24 shutil.rmtree(temp_directory_root)
|
|
25 sys.exit(status)
|
|
26
|
|
27 def print_and_exit(msg):
|
|
28 sys.stderr.write(msg + '\n')
|
|
29 exit_with_cleanups(1)
|
|
30
|
|
31 def find_and_diagnose_missing(lib, search_paths):
|
|
32 if os.path.exists(lib):
|
|
33 return os.path.abspath(lib)
|
|
34 if not lib.startswith('lib') or not lib.endswith('.a'):
|
|
35 print_and_exit(("input file '%s' not not name a static library. "
|
|
36 "It should start with 'lib' and end with '.a") % lib)
|
|
37 for sp in search_paths:
|
|
38 assert type(sp) is list and len(sp) == 1
|
|
39 path = os.path.join(sp[0], lib)
|
|
40 if os.path.exists(path):
|
|
41 return os.path.abspath(path)
|
|
42 print_and_exit("input '%s' does not exist" % lib)
|
|
43
|
|
44
|
|
45 def execute_command(cmd, cwd=None):
|
|
46 """
|
|
47 Execute a command, capture and return its output.
|
|
48 """
|
|
49 kwargs = {
|
|
50 'stdin': subprocess.PIPE,
|
|
51 'stdout': subprocess.PIPE,
|
|
52 'stderr': subprocess.PIPE,
|
|
53 'cwd': cwd,
|
|
54 'universal_newlines': True
|
|
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 return out
|
|
83
|
|
84 def 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)
|
|
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')]
|
173
|
146 execute_command_verbose([libtool_exe, '-static', '-o', args.output, '-s'] + files,
|
150
|
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
|
|
153 if __name__ == '__main__':
|
|
154 main()
|
|
155 exit_with_cleanups(0)
|