xref: /aosp_15_r20/external/libcxx/utils/merge_archives.py (revision 58b9f456b02922dfdb1fad8a988d5fd8765ecb80)
1*58b9f456SAndroid Build Coastguard Worker#!/usr/bin/env python
2*58b9f456SAndroid Build Coastguard Worker#===----------------------------------------------------------------------===##
3*58b9f456SAndroid Build Coastguard Worker#
4*58b9f456SAndroid Build Coastguard Worker#                     The LLVM Compiler Infrastructure
5*58b9f456SAndroid Build Coastguard Worker#
6*58b9f456SAndroid Build Coastguard Worker# This file is dual licensed under the MIT and the University of Illinois Open
7*58b9f456SAndroid Build Coastguard Worker# Source Licenses. See LICENSE.TXT for details.
8*58b9f456SAndroid Build Coastguard Worker#
9*58b9f456SAndroid Build Coastguard Worker#===----------------------------------------------------------------------===##
10*58b9f456SAndroid Build Coastguard Worker
11*58b9f456SAndroid Build Coastguard Workerfrom argparse import ArgumentParser
12*58b9f456SAndroid Build Coastguard Workerfrom ctypes.util import find_library
13*58b9f456SAndroid Build Coastguard Workerimport distutils.spawn
14*58b9f456SAndroid Build Coastguard Workerimport glob
15*58b9f456SAndroid Build Coastguard Workerimport tempfile
16*58b9f456SAndroid Build Coastguard Workerimport os
17*58b9f456SAndroid Build Coastguard Workerimport shutil
18*58b9f456SAndroid Build Coastguard Workerimport subprocess
19*58b9f456SAndroid Build Coastguard Workerimport signal
20*58b9f456SAndroid Build Coastguard Workerimport sys
21*58b9f456SAndroid Build Coastguard Worker
22*58b9f456SAndroid Build Coastguard Workertemp_directory_root = None
23*58b9f456SAndroid Build Coastguard Workerdef exit_with_cleanups(status):
24*58b9f456SAndroid Build Coastguard Worker    if temp_directory_root is not None:
25*58b9f456SAndroid Build Coastguard Worker        shutil.rmtree(temp_directory_root)
26*58b9f456SAndroid Build Coastguard Worker    sys.exit(status)
27*58b9f456SAndroid Build Coastguard Worker
28*58b9f456SAndroid Build Coastguard Workerdef print_and_exit(msg):
29*58b9f456SAndroid Build Coastguard Worker    sys.stderr.write(msg + '\n')
30*58b9f456SAndroid Build Coastguard Worker    exit_with_cleanups(1)
31*58b9f456SAndroid Build Coastguard Worker
32*58b9f456SAndroid Build Coastguard Workerdef find_and_diagnose_missing(lib, search_paths):
33*58b9f456SAndroid Build Coastguard Worker    if os.path.exists(lib):
34*58b9f456SAndroid Build Coastguard Worker        return os.path.abspath(lib)
35*58b9f456SAndroid Build Coastguard Worker    if not lib.startswith('lib') or not lib.endswith('.a'):
36*58b9f456SAndroid Build Coastguard Worker        print_and_exit(("input file '%s' not not name a static library. "
37*58b9f456SAndroid Build Coastguard Worker                       "It should start with 'lib' and end with '.a") % lib)
38*58b9f456SAndroid Build Coastguard Worker    for sp in search_paths:
39*58b9f456SAndroid Build Coastguard Worker        assert type(sp) is list and len(sp) == 1
40*58b9f456SAndroid Build Coastguard Worker        path = os.path.join(sp[0], lib)
41*58b9f456SAndroid Build Coastguard Worker        if os.path.exists(path):
42*58b9f456SAndroid Build Coastguard Worker            return os.path.abspath(path)
43*58b9f456SAndroid Build Coastguard Worker    print_and_exit("input '%s' does not exist" % lib)
44*58b9f456SAndroid Build Coastguard Worker
45*58b9f456SAndroid Build Coastguard Worker
46*58b9f456SAndroid Build Coastguard Workerdef execute_command(cmd, cwd=None):
47*58b9f456SAndroid Build Coastguard Worker    """
48*58b9f456SAndroid Build Coastguard Worker    Execute a command, capture and return its output.
49*58b9f456SAndroid Build Coastguard Worker    """
50*58b9f456SAndroid Build Coastguard Worker    kwargs = {
51*58b9f456SAndroid Build Coastguard Worker        'stdin': subprocess.PIPE,
52*58b9f456SAndroid Build Coastguard Worker        'stdout': subprocess.PIPE,
53*58b9f456SAndroid Build Coastguard Worker        'stderr': subprocess.PIPE,
54*58b9f456SAndroid Build Coastguard Worker        'cwd': cwd
55*58b9f456SAndroid Build Coastguard Worker    }
56*58b9f456SAndroid Build Coastguard Worker    p = subprocess.Popen(cmd, **kwargs)
57*58b9f456SAndroid Build Coastguard Worker    out, err = p.communicate()
58*58b9f456SAndroid Build Coastguard Worker    exitCode = p.wait()
59*58b9f456SAndroid Build Coastguard Worker    if exitCode == -signal.SIGINT:
60*58b9f456SAndroid Build Coastguard Worker        raise KeyboardInterrupt
61*58b9f456SAndroid Build Coastguard Worker    return out, err, exitCode
62*58b9f456SAndroid Build Coastguard Worker
63*58b9f456SAndroid Build Coastguard Worker
64*58b9f456SAndroid Build Coastguard Workerdef execute_command_verbose(cmd, cwd=None, verbose=False):
65*58b9f456SAndroid Build Coastguard Worker    """
66*58b9f456SAndroid Build Coastguard Worker    Execute a command and print its output on failure.
67*58b9f456SAndroid Build Coastguard Worker    """
68*58b9f456SAndroid Build Coastguard Worker    out, err, exitCode = execute_command(cmd, cwd=cwd)
69*58b9f456SAndroid Build Coastguard Worker    if exitCode != 0 or verbose:
70*58b9f456SAndroid Build Coastguard Worker        report = "Command: %s\n" % ' '.join(["'%s'" % a for a in cmd])
71*58b9f456SAndroid Build Coastguard Worker        if exitCode != 0:
72*58b9f456SAndroid Build Coastguard Worker            report += "Exit Code: %d\n" % exitCode
73*58b9f456SAndroid Build Coastguard Worker        if out:
74*58b9f456SAndroid Build Coastguard Worker            report += "Standard Output:\n--\n%s--" % out
75*58b9f456SAndroid Build Coastguard Worker        if err:
76*58b9f456SAndroid Build Coastguard Worker            report += "Standard Error:\n--\n%s--" % err
77*58b9f456SAndroid Build Coastguard Worker        if exitCode != 0:
78*58b9f456SAndroid Build Coastguard Worker            report += "\n\nFailed!"
79*58b9f456SAndroid Build Coastguard Worker        sys.stderr.write('%s\n' % report)
80*58b9f456SAndroid Build Coastguard Worker        if exitCode != 0:
81*58b9f456SAndroid Build Coastguard Worker            exit_with_cleanups(exitCode)
82*58b9f456SAndroid Build Coastguard Worker
83*58b9f456SAndroid Build Coastguard Workerdef main():
84*58b9f456SAndroid Build Coastguard Worker    parser = ArgumentParser(
85*58b9f456SAndroid Build Coastguard Worker        description="Merge multiple archives into a single library")
86*58b9f456SAndroid Build Coastguard Worker    parser.add_argument(
87*58b9f456SAndroid Build Coastguard Worker        '-v', '--verbose', dest='verbose', action='store_true', default=False)
88*58b9f456SAndroid Build Coastguard Worker    parser.add_argument(
89*58b9f456SAndroid Build Coastguard Worker        '-o', '--output', dest='output', required=True,
90*58b9f456SAndroid Build Coastguard Worker        help='The output file. stdout is used if not given',
91*58b9f456SAndroid Build Coastguard Worker        type=str, action='store')
92*58b9f456SAndroid Build Coastguard Worker    parser.add_argument(
93*58b9f456SAndroid Build Coastguard Worker        '-L', dest='search_paths',
94*58b9f456SAndroid Build Coastguard Worker        help='Paths to search for the libraries along', action='append',
95*58b9f456SAndroid Build Coastguard Worker        nargs=1)
96*58b9f456SAndroid Build Coastguard Worker    parser.add_argument(
97*58b9f456SAndroid Build Coastguard Worker        '--ar', dest='ar_exe', required=False,
98*58b9f456SAndroid Build Coastguard Worker        help='The ar executable to use, finds \'ar\' in the path if not given',
99*58b9f456SAndroid Build Coastguard Worker        type=str, action='store')
100*58b9f456SAndroid Build Coastguard Worker    parser.add_argument(
101*58b9f456SAndroid Build Coastguard Worker        'archives', metavar='archives',  nargs='+',
102*58b9f456SAndroid Build Coastguard Worker        help='The archives to merge')
103*58b9f456SAndroid Build Coastguard Worker
104*58b9f456SAndroid Build Coastguard Worker    args = parser.parse_args()
105*58b9f456SAndroid Build Coastguard Worker
106*58b9f456SAndroid Build Coastguard Worker    ar_exe = args.ar_exe
107*58b9f456SAndroid Build Coastguard Worker    if not ar_exe:
108*58b9f456SAndroid Build Coastguard Worker        ar_exe = distutils.spawn.find_executable('ar')
109*58b9f456SAndroid Build Coastguard Worker    if not ar_exe:
110*58b9f456SAndroid Build Coastguard Worker        print_and_exit("failed to find 'ar' executable")
111*58b9f456SAndroid Build Coastguard Worker
112*58b9f456SAndroid Build Coastguard Worker    if len(args.archives) < 2:
113*58b9f456SAndroid Build Coastguard Worker        print_and_exit('fewer than 2 inputs provided')
114*58b9f456SAndroid Build Coastguard Worker    archives = [find_and_diagnose_missing(ar, args.search_paths)
115*58b9f456SAndroid Build Coastguard Worker                for ar in args.archives]
116*58b9f456SAndroid Build Coastguard Worker    print ('Merging archives: %s' % archives)
117*58b9f456SAndroid Build Coastguard Worker    if not os.path.exists(os.path.dirname(args.output)):
118*58b9f456SAndroid Build Coastguard Worker        print_and_exit("output path doesn't exist: '%s'" % args.output)
119*58b9f456SAndroid Build Coastguard Worker
120*58b9f456SAndroid Build Coastguard Worker    global temp_directory_root
121*58b9f456SAndroid Build Coastguard Worker    temp_directory_root = tempfile.mkdtemp('.libcxx.merge.archives')
122*58b9f456SAndroid Build Coastguard Worker
123*58b9f456SAndroid Build Coastguard Worker    for arc in archives:
124*58b9f456SAndroid Build Coastguard Worker        execute_command_verbose([ar_exe, 'x', arc], cwd=temp_directory_root,
125*58b9f456SAndroid Build Coastguard Worker                                verbose=args.verbose)
126*58b9f456SAndroid Build Coastguard Worker
127*58b9f456SAndroid Build Coastguard Worker    files = glob.glob(os.path.join(temp_directory_root, '*.o*'))
128*58b9f456SAndroid Build Coastguard Worker    if not files:
129*58b9f456SAndroid Build Coastguard Worker        print_and_exit('Failed to glob for %s' % temp_directory_root)
130*58b9f456SAndroid Build Coastguard Worker    cmd = [ar_exe, 'qcs', args.output] + files
131*58b9f456SAndroid Build Coastguard Worker    execute_command_verbose(cmd, cwd=temp_directory_root, verbose=args.verbose)
132*58b9f456SAndroid Build Coastguard Worker
133*58b9f456SAndroid Build Coastguard Worker
134*58b9f456SAndroid Build Coastguard Workerif __name__ == '__main__':
135*58b9f456SAndroid Build Coastguard Worker    main()
136*58b9f456SAndroid Build Coastguard Worker    exit_with_cleanups(0)
137