• Home
  • History
  • Annotate
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/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
12from ctypes.util import find_library
13import distutils.spawn
14import glob
15import tempfile
16import os
17import shutil
18import subprocess
19import signal
20import sys
21
22temp_directory_root = None
23def exit_with_cleanups(status):
24    if temp_directory_root is not None:
25        shutil.rmtree(temp_directory_root)
26    sys.exit(status)
27
28def print_and_exit(msg):
29    sys.stderr.write(msg + '\n')
30    exit_with_cleanups(1)
31
32def find_and_diagnose_missing(lib, search_paths):
33    if os.path.exists(lib):
34        return os.path.abspath(lib)
35    if not lib.startswith('lib') or not lib.endswith('.a'):
36        print_and_exit(("input file '%s' not not name a static library. "
37                       "It should start with 'lib' and end with '.a") % lib)
38    for sp in search_paths:
39        assert type(sp) is list and len(sp) == 1
40        path = os.path.join(sp[0], lib)
41        if os.path.exists(path):
42            return os.path.abspath(path)
43    print_and_exit("input '%s' does not exist" % lib)
44
45
46def execute_command(cmd, cwd=None):
47    """
48    Execute a command, capture and return its output.
49    """
50    kwargs = {
51        'stdin': subprocess.PIPE,
52        'stdout': subprocess.PIPE,
53        'stderr': subprocess.PIPE,
54        'cwd': cwd
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
64def 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
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(
93        '-L', dest='search_paths',
94        help='Paths to search for the libraries along', action='append',
95        nargs=1)
96    parser.add_argument(
97        'archives', metavar='archives',  nargs='+',
98        help='The archives to merge')
99
100    args = parser.parse_args()
101
102    ar_exe = distutils.spawn.find_executable('ar')
103    if not ar_exe:
104        print_and_exit("failed to find 'ar' executable")
105
106    if len(args.archives) < 2:
107        print_and_exit('fewer than 2 inputs provided')
108    archives = [find_and_diagnose_missing(ar, args.search_paths)
109                for ar in args.archives]
110    print ('Merging archives: %s' % archives)
111    if not os.path.exists(os.path.dirname(args.output)):
112        print_and_exit("output path doesn't exist: '%s'" % args.output)
113
114    global temp_directory_root
115    temp_directory_root = tempfile.mkdtemp('.libcxx.merge.archives')
116
117    for arc in archives:
118        execute_command_verbose([ar_exe, '-x', arc], cwd=temp_directory_root,
119                                verbose=args.verbose)
120
121    files = glob.glob(os.path.join(temp_directory_root, '*.o'))
122    if not files:
123        print_and_exit('Failed to glob for %s' % glob_path)
124    cmd = [ar_exe, '-qc', args.output] + files
125    execute_command_verbose(cmd, cwd=temp_directory_root, verbose=args.verbose)
126
127
128if __name__ == '__main__':
129    main()
130    exit_with_cleanups(0)
131