extract_from_cab.py 2.1 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465
  1. #!/usr/bin/env python
  2. # Copyright (c) 2012 The Chromium Authors. All rights reserved.
  3. # Use of this source code is governed by a BSD-style license that can be
  4. # found in the LICENSE file.
  5. """Extracts a single file from a CAB archive."""
  6. from __future__ import print_function
  7. import os
  8. import shutil
  9. import subprocess
  10. import sys
  11. import tempfile
  12. def run_quiet(*args):
  13. """Run 'expand' suppressing noisy output. Returns returncode from process."""
  14. popen = subprocess.Popen(args, stdout=subprocess.PIPE)
  15. out, _ = popen.communicate()
  16. if popen.returncode:
  17. # expand emits errors to stdout, so if we fail, then print that out.
  18. print(out)
  19. return popen.returncode
  20. def main():
  21. if len(sys.argv) != 4:
  22. print('Usage: extract_from_cab.py cab_path archived_file output_dir')
  23. return 1
  24. [cab_path, archived_file, output_dir] = sys.argv[1:]
  25. # Expand.exe does its work in a fixed-named temporary directory created within
  26. # the given output directory. This is a problem for concurrent extractions, so
  27. # create a unique temp dir within the desired output directory to work around
  28. # this limitation.
  29. temp_dir = tempfile.mkdtemp(dir=output_dir)
  30. try:
  31. # Invoke the Windows expand utility to extract the file.
  32. level = run_quiet('expand', cab_path, '-F:' + archived_file, temp_dir)
  33. if level == 0:
  34. # Move the output file into place, preserving expand.exe's behavior of
  35. # paving over any preexisting file.
  36. output_file = os.path.join(output_dir, archived_file)
  37. try:
  38. os.remove(output_file)
  39. except OSError:
  40. pass
  41. os.rename(os.path.join(temp_dir, archived_file), output_file)
  42. finally:
  43. shutil.rmtree(temp_dir, True)
  44. if level != 0:
  45. return level
  46. # The expand utility preserves the modification date and time of the archived
  47. # file. Touch the extracted file. This helps build systems that compare the
  48. # modification times of input and output files to determine whether to do an
  49. # action.
  50. os.utime(os.path.join(output_dir, archived_file), None)
  51. return 0
  52. if __name__ == '__main__':
  53. sys.exit(main())