pycompile.py 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293
  1. #!/usr/bin/env python
  2. """
  3. Byte compile all .py files from provided directories. This script is an
  4. alternative implementation of compileall.compile_dir written with
  5. cross-compilation in mind.
  6. """
  7. from __future__ import print_function
  8. import argparse
  9. import os
  10. import py_compile
  11. import re
  12. import sys
  13. def compile_one(host_path, strip_root=None, verbose=False):
  14. """
  15. Compile a .py file into a .pyc file located next to it.
  16. :arg host_path:
  17. Absolute path to the file to compile on the host running the build.
  18. :arg strip_root:
  19. Prefix to remove from the original source paths encoded in compiled
  20. files.
  21. :arg verbose:
  22. Print compiled file paths.
  23. """
  24. if os.path.islink(host_path) or not os.path.isfile(host_path):
  25. return # only compile real files
  26. if not re.match(r"^[_A-Za-z][_A-Za-z0-9]+\.py$",
  27. os.path.basename(host_path)):
  28. return # only compile "importable" python modules
  29. if strip_root is not None:
  30. # determine the runtime path of the file (i.e.: relative path to root
  31. # dir prepended with "/").
  32. runtime_path = os.path.join("/", os.path.relpath(host_path, strip_root))
  33. else:
  34. runtime_path = host_path
  35. if verbose:
  36. print(" PYC {}".format(runtime_path))
  37. # will raise an error if the file cannot be compiled
  38. py_compile.compile(host_path, cfile=host_path + "c",
  39. dfile=runtime_path, doraise=True)
  40. def existing_dir_abs(arg):
  41. """
  42. argparse type callback that checks that argument is a directory and returns
  43. its absolute path.
  44. """
  45. if not os.path.isdir(arg):
  46. raise argparse.ArgumentTypeError('no such directory: {!r}'.format(arg))
  47. return os.path.abspath(arg)
  48. def main():
  49. parser = argparse.ArgumentParser(description=__doc__)
  50. parser.add_argument("dirs", metavar="DIR", nargs="+", type=existing_dir_abs,
  51. help="Directory to recursively scan and compile")
  52. parser.add_argument("--strip-root", metavar="ROOT", type=existing_dir_abs,
  53. help="""
  54. Prefix to remove from the original source paths encoded
  55. in compiled files
  56. """)
  57. parser.add_argument("--verbose", action="store_true",
  58. help="Print compiled files")
  59. args = parser.parse_args()
  60. try:
  61. for d in args.dirs:
  62. if args.strip_root and ".." in os.path.relpath(d, args.strip_root):
  63. parser.error("DIR: not inside ROOT dir: {!r}".format(d))
  64. for parent, _, files in os.walk(d):
  65. for f in files:
  66. compile_one(os.path.join(parent, f), args.strip_root,
  67. args.verbose)
  68. except Exception as e:
  69. print("error: {}".format(e))
  70. return 1
  71. return 0
  72. if __name__ == "__main__":
  73. sys.exit(main())