blob: 5c04bc915a4ad052a93d085ebd384817778b45f6 [file] [log] [blame]
Eric Fiselier1ae2fd42016-11-18 11:26:14 +00001#!/usr/bin/env python
2#===----------------------------------------------------------------------===##
3#
Chandler Carruthd2012102019-01-19 10:56:40 +00004# 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
Eric Fiselier1ae2fd42016-11-18 11:26:14 +00007#
8#===----------------------------------------------------------------------===##
9
10from argparse import ArgumentParser
Eric Fiselier55c11722016-11-18 19:53:45 +000011from ctypes.util import find_library
Eric Fiselier1ae2fd42016-11-18 11:26:14 +000012import 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
Eric Fiselier55c11722016-11-18 19:53:45 +000031def 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)
Eric Fiselier1ae2fd42016-11-18 11:26:14 +000043
44
45def 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 }
55 p = subprocess.Popen(cmd, **kwargs)
56 out, err = p.communicate()
57 exitCode = p.wait()
58 if exitCode == -signal.SIGINT:
59 raise KeyboardInterrupt
60 return out, err, exitCode
61
62
63def execute_command_verbose(cmd, cwd=None, verbose=False):
64 """
65 Execute a command and print its output on failure.
66 """
67 out, err, exitCode = execute_command(cmd, cwd=cwd)
68 if exitCode != 0 or verbose:
69 report = "Command: %s\n" % ' '.join(["'%s'" % a for a in cmd])
70 if exitCode != 0:
71 report += "Exit Code: %d\n" % exitCode
72 if out:
73 report += "Standard Output:\n--\n%s--" % out
74 if err:
75 report += "Standard Error:\n--\n%s--" % err
76 if exitCode != 0:
77 report += "\n\nFailed!"
78 sys.stderr.write('%s\n' % report)
79 if exitCode != 0:
80 exit_with_cleanups(exitCode)
Petr Hosek00ad0222019-02-11 08:48:47 +000081 return out
Eric Fiselier1ae2fd42016-11-18 11:26:14 +000082
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 Fiselier55c11722016-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 Storsjo37f4e5a2017-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(
Petr Hosekc24b9242019-06-02 01:14:31 +0000101 '--use-libtool', dest='use_libtool', action='store_true', default=False)
102 parser.add_argument(
103 '--libtool', dest='libtool_exe', required=False,
104 help='The libtool executable to use, finds \'libtool\' in the path if not given',
105 type=str, action='store')
106 parser.add_argument(
Eric Fiselier1ae2fd42016-11-18 11:26:14 +0000107 'archives', metavar='archives', nargs='+',
108 help='The archives to merge')
109
110 args = parser.parse_args()
111
Martin Storsjo37f4e5a2017-09-13 06:55:44 +0000112 ar_exe = args.ar_exe
113 if not ar_exe:
114 ar_exe = distutils.spawn.find_executable('ar')
Eric Fiselier1ae2fd42016-11-18 11:26:14 +0000115 if not ar_exe:
116 print_and_exit("failed to find 'ar' executable")
117
Petr Hosekc24b9242019-06-02 01:14:31 +0000118 if args.use_libtool:
119 libtool_exe = args.libtool_exe
120 if not libtool_exe:
121 libtool_exe = distutils.spawn.find_executable('libtool')
122 if not libtool_exe:
123 print_and_exit("failed to find 'libtool' executable")
124
Eric Fiselier1ae2fd42016-11-18 11:26:14 +0000125 if len(args.archives) < 2:
126 print_and_exit('fewer than 2 inputs provided')
Eric Fiselier55c11722016-11-18 19:53:45 +0000127 archives = [find_and_diagnose_missing(ar, args.search_paths)
128 for ar in args.archives]
129 print ('Merging archives: %s' % archives)
Eric Fiselier1ae2fd42016-11-18 11:26:14 +0000130 if not os.path.exists(os.path.dirname(args.output)):
131 print_and_exit("output path doesn't exist: '%s'" % args.output)
132
133 global temp_directory_root
134 temp_directory_root = tempfile.mkdtemp('.libcxx.merge.archives')
135
Petr Hosek00ad0222019-02-11 08:48:47 +0000136 files = []
Eric Fiselier1ae2fd42016-11-18 11:26:14 +0000137 for arc in archives:
Petr Hosek00ad0222019-02-11 08:48:47 +0000138 execute_command_verbose([ar_exe, 'x', arc],
139 cwd=temp_directory_root, verbose=args.verbose)
140 out = execute_command_verbose([ar_exe, 't', arc])
141 files.extend(out.splitlines())
Eric Fiselier1ae2fd42016-11-18 11:26:14 +0000142
Petr Hosekc24b9242019-06-02 01:14:31 +0000143 if args.use_libtool:
144 files = [f for f in files if not f.startswith('__.SYMDEF')]
145 execute_command_verbose([libtool_exe, '-static', '-o', args.output] + files,
146 cwd=temp_directory_root, verbose=args.verbose)
147 else:
148 execute_command_verbose([ar_exe, 'rcs', args.output] + files,
149 cwd=temp_directory_root, verbose=args.verbose)
Eric Fiselier1ae2fd42016-11-18 11:26:14 +0000150
151
152if __name__ == '__main__':
153 main()
154 exit_with_cleanups(0)