blob: fa0b2ee6817132937fa757e8b65ed300e0438661 [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
11import distutils.spawn
Eric Fiselier1ae2fd42016-11-18 11:26:14 +000012import tempfile
13import os
14import shutil
15import subprocess
16import signal
17import sys
18
19temp_directory_root = None
20def exit_with_cleanups(status):
21 if temp_directory_root is not None:
22 shutil.rmtree(temp_directory_root)
23 sys.exit(status)
24
25def print_and_exit(msg):
26 sys.stderr.write(msg + '\n')
27 exit_with_cleanups(1)
28
Eric Fiselier55c11722016-11-18 19:53:45 +000029def find_and_diagnose_missing(lib, search_paths):
30 if os.path.exists(lib):
31 return os.path.abspath(lib)
32 if not lib.startswith('lib') or not lib.endswith('.a'):
33 print_and_exit(("input file '%s' not not name a static library. "
34 "It should start with 'lib' and end with '.a") % lib)
35 for sp in search_paths:
36 assert type(sp) is list and len(sp) == 1
37 path = os.path.join(sp[0], lib)
38 if os.path.exists(path):
39 return os.path.abspath(path)
40 print_and_exit("input '%s' does not exist" % lib)
Eric Fiselier1ae2fd42016-11-18 11:26:14 +000041
42
43def execute_command(cmd, cwd=None):
44 """
45 Execute a command, capture and return its output.
46 """
47 kwargs = {
48 'stdin': subprocess.PIPE,
49 'stdout': subprocess.PIPE,
50 'stderr': subprocess.PIPE,
Louis Dionne0d10b132019-08-23 15:05:54 +000051 'cwd': cwd,
52 'universal_newlines': True
Eric Fiselier1ae2fd42016-11-18 11:26:14 +000053 }
54 p = subprocess.Popen(cmd, **kwargs)
55 out, err = p.communicate()
56 exitCode = p.wait()
57 if exitCode == -signal.SIGINT:
58 raise KeyboardInterrupt
59 return out, err, exitCode
60
61
62def execute_command_verbose(cmd, cwd=None, verbose=False):
63 """
64 Execute a command and print its output on failure.
65 """
66 out, err, exitCode = execute_command(cmd, cwd=cwd)
67 if exitCode != 0 or verbose:
68 report = "Command: %s\n" % ' '.join(["'%s'" % a for a in cmd])
69 if exitCode != 0:
70 report += "Exit Code: %d\n" % exitCode
71 if out:
72 report += "Standard Output:\n--\n%s--" % out
73 if err:
74 report += "Standard Error:\n--\n%s--" % err
75 if exitCode != 0:
76 report += "\n\nFailed!"
77 sys.stderr.write('%s\n' % report)
78 if exitCode != 0:
79 exit_with_cleanups(exitCode)
Petr Hosek00ad0222019-02-11 08:48:47 +000080 return out
Eric Fiselier1ae2fd42016-11-18 11:26:14 +000081
82def main():
83 parser = ArgumentParser(
84 description="Merge multiple archives into a single library")
85 parser.add_argument(
86 '-v', '--verbose', dest='verbose', action='store_true', default=False)
87 parser.add_argument(
88 '-o', '--output', dest='output', required=True,
89 help='The output file. stdout is used if not given',
90 type=str, action='store')
91 parser.add_argument(
Eric Fiselier55c11722016-11-18 19:53:45 +000092 '-L', dest='search_paths',
93 help='Paths to search for the libraries along', action='append',
Alex Richardsond8a04172020-07-22 18:32:34 +010094 nargs=1, default=[])
Eric Fiselier55c11722016-11-18 19:53:45 +000095 parser.add_argument(
Martin Storsjo37f4e5a2017-09-13 06:55:44 +000096 '--ar', dest='ar_exe', required=False,
97 help='The ar executable to use, finds \'ar\' in the path if not given',
98 type=str, action='store')
99 parser.add_argument(
Petr Hosekc24b9242019-06-02 01:14:31 +0000100 '--use-libtool', dest='use_libtool', action='store_true', default=False)
101 parser.add_argument(
102 '--libtool', dest='libtool_exe', required=False,
103 help='The libtool executable to use, finds \'libtool\' in the path if not given',
104 type=str, action='store')
105 parser.add_argument(
Eric Fiselier1ae2fd42016-11-18 11:26:14 +0000106 'archives', metavar='archives', nargs='+',
107 help='The archives to merge')
108
109 args = parser.parse_args()
110
Martin Storsjo37f4e5a2017-09-13 06:55:44 +0000111 ar_exe = args.ar_exe
112 if not ar_exe:
113 ar_exe = distutils.spawn.find_executable('ar')
Eric Fiselier1ae2fd42016-11-18 11:26:14 +0000114 if not ar_exe:
115 print_and_exit("failed to find 'ar' executable")
116
Petr Hosekc24b9242019-06-02 01:14:31 +0000117 if args.use_libtool:
118 libtool_exe = args.libtool_exe
119 if not libtool_exe:
120 libtool_exe = distutils.spawn.find_executable('libtool')
121 if not libtool_exe:
122 print_and_exit("failed to find 'libtool' executable")
123
Eric Fiselier1ae2fd42016-11-18 11:26:14 +0000124 if len(args.archives) < 2:
125 print_and_exit('fewer than 2 inputs provided')
Eric Fiselier55c11722016-11-18 19:53:45 +0000126 archives = [find_and_diagnose_missing(ar, args.search_paths)
127 for ar in args.archives]
128 print ('Merging archives: %s' % archives)
Eric Fiselier1ae2fd42016-11-18 11:26:14 +0000129 if not os.path.exists(os.path.dirname(args.output)):
130 print_and_exit("output path doesn't exist: '%s'" % args.output)
131
132 global temp_directory_root
133 temp_directory_root = tempfile.mkdtemp('.libcxx.merge.archives')
134
Petr Hosek00ad0222019-02-11 08:48:47 +0000135 files = []
Eric Fiselier1ae2fd42016-11-18 11:26:14 +0000136 for arc in archives:
Petr Hosek00ad0222019-02-11 08:48:47 +0000137 execute_command_verbose([ar_exe, 'x', arc],
138 cwd=temp_directory_root, verbose=args.verbose)
139 out = execute_command_verbose([ar_exe, 't', arc])
140 files.extend(out.splitlines())
Eric Fiselier1ae2fd42016-11-18 11:26:14 +0000141
Petr Hosekc24b9242019-06-02 01:14:31 +0000142 if args.use_libtool:
143 files = [f for f in files if not f.startswith('__.SYMDEF')]
Petr Hosek766afb52020-09-28 14:18:55 -0700144 execute_command_verbose([libtool_exe, '-static', '-o', args.output] + files,
Petr Hosekc24b9242019-06-02 01:14:31 +0000145 cwd=temp_directory_root, verbose=args.verbose)
146 else:
Petr Hosek6ef35b82020-03-02 16:07:18 -0800147 execute_command_verbose([ar_exe, 'rcs', args.output] + files,
Petr Hosekc24b9242019-06-02 01:14:31 +0000148 cwd=temp_directory_root, verbose=args.verbose)
Eric Fiselier1ae2fd42016-11-18 11:26:14 +0000149
150
151if __name__ == '__main__':
152 main()
153 exit_with_cleanups(0)