Home | History | Annotate | Download | only in utils
      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 
     11 from argparse import ArgumentParser
     12 from ctypes.util import find_library
     13 import distutils.spawn
     14 import glob
     15 import tempfile
     16 import os
     17 import shutil
     18 import subprocess
     19 import signal
     20 import sys
     21 
     22 temp_directory_root = None
     23 def exit_with_cleanups(status):
     24     if temp_directory_root is not None:
     25         shutil.rmtree(temp_directory_root)
     26     sys.exit(status)
     27 
     28 def print_and_exit(msg):
     29     sys.stderr.write(msg + '\n')
     30     exit_with_cleanups(1)
     31 
     32 def 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 
     46 def 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 
     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 
     83 def 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 
    128 if __name__ == '__main__':
    129     main()
    130     exit_with_cleanups(0)
    131