nsinstall.py 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183
  1. # This Source Code Form is subject to the terms of the Mozilla Public
  2. # License, v. 2.0. If a copy of the MPL was not distributed with this
  3. # file, You can obtain one at http://mozilla.org/MPL/2.0/.
  4. # This is a partial python port of nsinstall.
  5. # It's intended to be used when there's no natively compile nsinstall
  6. # available, and doesn't intend to be fully equivalent.
  7. # Its major use is for l10n repackaging on systems that don't have
  8. # a full build environment set up.
  9. # The basic limitation is, it doesn't even try to link and ignores
  10. # all related options.
  11. from __future__ import print_function
  12. from optparse import OptionParser
  13. import mozfile
  14. import os
  15. import os.path
  16. import sys
  17. import shutil
  18. import stat
  19. def _nsinstall_internal(argv):
  20. usage = "usage: %prog [options] arg1 [arg2 ...] target-directory"
  21. p = OptionParser(usage=usage)
  22. p.add_option('-D', action="store_true",
  23. help="Create a single directory only")
  24. p.add_option('-t', action="store_true",
  25. help="Preserve time stamp")
  26. p.add_option('-m', action="store",
  27. help="Set mode", metavar="mode")
  28. p.add_option('-d', action="store_true",
  29. help="Create directories in target")
  30. p.add_option('-R', action="store_true",
  31. help="Use relative symbolic links (ignored)")
  32. p.add_option('-L', action="store", metavar="linkprefix",
  33. help="Link prefix (ignored)")
  34. p.add_option('-X', action="append", metavar="file",
  35. help="Ignore a file when installing a directory recursively.")
  36. # The remaining arguments are not used in our tree, thus they're not
  37. # implented.
  38. def BadArg(option, opt, value, parser):
  39. parser.error('option not supported: {0}'.format(opt))
  40. p.add_option('-C', action="callback", metavar="CWD",
  41. callback=BadArg,
  42. help="NOT SUPPORTED")
  43. p.add_option('-o', action="callback", callback=BadArg,
  44. help="Set owner (NOT SUPPORTED)", metavar="owner")
  45. p.add_option('-g', action="callback", callback=BadArg,
  46. help="Set group (NOT SUPPORTED)", metavar="group")
  47. (options, args) = p.parse_args(argv)
  48. if options.m:
  49. # mode is specified
  50. try:
  51. options.m = int(options.m, 8)
  52. except:
  53. sys.stderr.write('nsinstall: {0} is not a valid mode\n'
  54. .format(options.m))
  55. return 1
  56. # just create one directory?
  57. def maybe_create_dir(dir, mode, try_again):
  58. dir = os.path.abspath(dir)
  59. if os.path.exists(dir):
  60. if not os.path.isdir(dir):
  61. print('nsinstall: {0} is not a directory'.format(dir), file=sys.stderr)
  62. return 1
  63. if mode:
  64. os.chmod(dir, mode)
  65. return 0
  66. try:
  67. if mode:
  68. os.makedirs(dir, mode)
  69. else:
  70. os.makedirs(dir)
  71. except Exception as e:
  72. # We might have hit EEXIST due to a race condition (see bug 463411) -- try again once
  73. if try_again:
  74. return maybe_create_dir(dir, mode, False)
  75. print("nsinstall: failed to create directory {0}: {1}".format(dir, e))
  76. return 1
  77. else:
  78. return 0
  79. if options.X:
  80. options.X = [os.path.abspath(p) for p in options.X]
  81. if options.D:
  82. return maybe_create_dir(args[0], options.m, True)
  83. # nsinstall arg1 [...] directory
  84. if len(args) < 2:
  85. p.error('not enough arguments')
  86. def copy_all_entries(entries, target):
  87. for e in entries:
  88. e = os.path.abspath(e)
  89. if options.X and e in options.X:
  90. continue
  91. dest = os.path.join(target, os.path.basename(e))
  92. dest = os.path.abspath(dest)
  93. handleTarget(e, dest)
  94. if options.m:
  95. os.chmod(dest, options.m)
  96. # set up handler
  97. if options.d:
  98. # we're supposed to create directories
  99. def handleTarget(srcpath, targetpath):
  100. # target directory was already created, just use mkdir
  101. os.mkdir(targetpath)
  102. else:
  103. # we're supposed to copy files
  104. def handleTarget(srcpath, targetpath):
  105. if os.path.isdir(srcpath):
  106. if not os.path.exists(targetpath):
  107. os.mkdir(targetpath)
  108. entries = [os.path.join(srcpath, e) for e in os.listdir(srcpath)]
  109. copy_all_entries(entries, targetpath)
  110. # options.t is not relevant for directories
  111. if options.m:
  112. os.chmod(targetpath, options.m)
  113. else:
  114. if os.path.exists(targetpath):
  115. if sys.platform == "win32":
  116. mozfile.remove(targetpath)
  117. else:
  118. os.remove(targetpath)
  119. if options.t:
  120. shutil.copy2(srcpath, targetpath)
  121. else:
  122. shutil.copy(srcpath, targetpath)
  123. # the last argument is the target directory
  124. target = args.pop()
  125. # ensure target directory (importantly, we do not apply a mode to the directory
  126. # because we want to copy files into it and the mode might be read-only)
  127. rv = maybe_create_dir(target, None, True)
  128. if rv != 0:
  129. return rv
  130. copy_all_entries(args, target)
  131. return 0
  132. # nsinstall as a native command is always UTF-8
  133. def nsinstall(argv):
  134. return _nsinstall_internal([unicode(arg, "utf-8") for arg in argv])
  135. if __name__ == '__main__':
  136. # sys.argv corrupts characters outside the system code page on Windows
  137. # <http://bugs.python.org/issue2128>. Use ctypes instead. This is also
  138. # useful because switching to Unicode strings makes python use the wide
  139. # Windows APIs, which is what we want here since the wide APIs normally do a
  140. # better job at handling long paths and such.
  141. if sys.platform == "win32":
  142. import ctypes
  143. from ctypes import wintypes
  144. GetCommandLine = ctypes.windll.kernel32.GetCommandLineW
  145. GetCommandLine.argtypes = []
  146. GetCommandLine.restype = wintypes.LPWSTR
  147. CommandLineToArgv = ctypes.windll.shell32.CommandLineToArgvW
  148. CommandLineToArgv.argtypes = [wintypes.LPWSTR, ctypes.POINTER(ctypes.c_int)]
  149. CommandLineToArgv.restype = ctypes.POINTER(wintypes.LPWSTR)
  150. argc = ctypes.c_int(0)
  151. argv_arr = CommandLineToArgv(GetCommandLine(), ctypes.byref(argc))
  152. # The first argv will be "python", the second will be the .py file
  153. argv = argv_arr[1:argc.value]
  154. else:
  155. # For consistency, do it on Unix as well
  156. if sys.stdin.encoding is not None:
  157. argv = [unicode(arg, sys.stdin.encoding) for arg in sys.argv]
  158. else:
  159. argv = [unicode(arg) for arg in sys.argv]
  160. sys.exit(_nsinstall_internal(argv[1:]))