merge_archives.py 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130
  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. from argparse import ArgumentParser
  11. from ctypes.util import find_library
  12. import distutils.spawn
  13. import glob
  14. import tempfile
  15. import os
  16. import shutil
  17. import subprocess
  18. import signal
  19. import sys
  20. temp_directory_root = None
  21. def exit_with_cleanups(status):
  22. if temp_directory_root is not None:
  23. shutil.rmtree(temp_directory_root)
  24. sys.exit(status)
  25. def print_and_exit(msg):
  26. sys.stderr.write(msg + '\n')
  27. exit_with_cleanups(1)
  28. def find_and_diagnose_missing(lib, search_paths):
  29. if os.path.exists(lib):
  30. return os.path.abspath(lib)
  31. if not lib.startswith('lib') or not lib.endswith('.a'):
  32. print_and_exit(("input file '%s' not not name a static library. "
  33. "It should start with 'lib' and end with '.a") % lib)
  34. for sp in search_paths:
  35. assert type(sp) is list and len(sp) == 1
  36. path = os.path.join(sp[0], lib)
  37. if os.path.exists(path):
  38. return os.path.abspath(path)
  39. print_and_exit("input '%s' does not exist" % lib)
  40. def execute_command(cmd, cwd=None):
  41. """
  42. Execute a command, capture and return its output.
  43. """
  44. kwargs = {
  45. 'stdin': subprocess.PIPE,
  46. 'stdout': subprocess.PIPE,
  47. 'stderr': subprocess.PIPE,
  48. 'cwd': cwd
  49. }
  50. p = subprocess.Popen(cmd, **kwargs)
  51. out, err = p.communicate()
  52. exitCode = p.wait()
  53. if exitCode == -signal.SIGINT:
  54. raise KeyboardInterrupt
  55. return out, err, exitCode
  56. def execute_command_verbose(cmd, cwd=None, verbose=False):
  57. """
  58. Execute a command and print its output on failure.
  59. """
  60. out, err, exitCode = execute_command(cmd, cwd=cwd)
  61. if exitCode != 0 or verbose:
  62. report = "Command: %s\n" % ' '.join(["'%s'" % a for a in cmd])
  63. if exitCode != 0:
  64. report += "Exit Code: %d\n" % exitCode
  65. if out:
  66. report += "Standard Output:\n--\n%s--" % out
  67. if err:
  68. report += "Standard Error:\n--\n%s--" % err
  69. if exitCode != 0:
  70. report += "\n\nFailed!"
  71. sys.stderr.write('%s\n' % report)
  72. if exitCode != 0:
  73. exit_with_cleanups(exitCode)
  74. def main():
  75. parser = ArgumentParser(
  76. description="Merge multiple archives into a single library")
  77. parser.add_argument(
  78. '-v', '--verbose', dest='verbose', action='store_true', default=False)
  79. parser.add_argument(
  80. '-o', '--output', dest='output', required=True,
  81. help='The output file. stdout is used if not given',
  82. type=str, action='store')
  83. parser.add_argument(
  84. '-L', dest='search_paths',
  85. help='Paths to search for the libraries along', action='append',
  86. nargs=1)
  87. parser.add_argument(
  88. 'archives', metavar='archives', nargs='+',
  89. help='The archives to merge')
  90. args = parser.parse_args()
  91. ar_exe = distutils.spawn.find_executable('ar')
  92. if not ar_exe:
  93. print_and_exit("failed to find 'ar' executable")
  94. if len(args.archives) < 2:
  95. print_and_exit('fewer than 2 inputs provided')
  96. archives = [find_and_diagnose_missing(ar, args.search_paths)
  97. for ar in args.archives]
  98. print ('Merging archives: %s' % archives)
  99. if not os.path.exists(os.path.dirname(args.output)):
  100. print_and_exit("output path doesn't exist: '%s'" % args.output)
  101. global temp_directory_root
  102. temp_directory_root = tempfile.mkdtemp('.libcxx.merge.archives')
  103. for arc in archives:
  104. execute_command_verbose([ar_exe, '-x', arc], cwd=temp_directory_root,
  105. verbose=args.verbose)
  106. files = glob.glob(os.path.join(temp_directory_root, '*.o*'))
  107. if not files:
  108. print_and_exit('Failed to glob for %s' % glob_path)
  109. cmd = [ar_exe, '-qc', args.output] + files
  110. execute_command_verbose(cmd, cwd=temp_directory_root, verbose=args.verbose)
  111. if __name__ == '__main__':
  112. main()
  113. exit_with_cleanups(0)