| # This Source Code Form is subject to the terms of the Mozilla Public |
| # License, v. 2.0. If a copy of the MPL was not distributed with this |
| # file, You can obtain one at http://mozilla.org/MPL/2.0/. |
| |
| # This is a partial python port of nsinstall. |
| # It's intended to be used when there's no natively compile nsinstall |
| # available, and doesn't intend to be fully equivalent. |
| # Its major use is for l10n repackaging on systems that don't have |
| # a full build environment set up. |
| # The basic limitation is, it doesn't even try to link and ignores |
| # all related options. |
| from __future__ import print_function |
| from optparse import OptionParser |
| import os |
| import os.path |
| import sys |
| import shutil |
| import stat |
| |
| def _nsinstall_internal(argv): |
| usage = "usage: %prog [options] arg1 [arg2 ...] target-directory" |
| p = OptionParser(usage=usage) |
| |
| p.add_option('-D', action="store_true", |
| help="Create a single directory only") |
| p.add_option('-t', action="store_true", |
| help="Preserve time stamp") |
| p.add_option('-m', action="store", |
| help="Set mode", metavar="mode") |
| p.add_option('-d', action="store_true", |
| help="Create directories in target") |
| p.add_option('-R', action="store_true", |
| help="Use relative symbolic links (ignored)") |
| p.add_option('-L', action="store", metavar="linkprefix", |
| help="Link prefix (ignored)") |
| p.add_option('-X', action="append", metavar="file", |
| help="Ignore a file when installing a directory recursively.") |
| |
| # The remaining arguments are not used in our tree, thus they're not |
| # implented. |
| def BadArg(option, opt, value, parser): |
| parser.error('option not supported: {0}'.format(opt)) |
| |
| p.add_option('-C', action="callback", metavar="CWD", |
| callback=BadArg, |
| help="NOT SUPPORTED") |
| p.add_option('-o', action="callback", callback=BadArg, |
| help="Set owner (NOT SUPPORTED)", metavar="owner") |
| p.add_option('-g', action="callback", callback=BadArg, |
| help="Set group (NOT SUPPORTED)", metavar="group") |
| |
| (options, args) = p.parse_args(argv) |
| |
| if options.m: |
| # mode is specified |
| try: |
| options.m = int(options.m, 8) |
| except: |
| sys.stderr.write('nsinstall: {0} is not a valid mode\n' |
| .format(options.m)) |
| return 1 |
| |
| # just create one directory? |
| def maybe_create_dir(dir, mode, try_again): |
| dir = os.path.abspath(dir) |
| if os.path.exists(dir): |
| if not os.path.isdir(dir): |
| print('nsinstall: {0} is not a directory'.format(dir), file=sys.stderr) |
| return 1 |
| if mode: |
| os.chmod(dir, mode) |
| return 0 |
| |
| try: |
| if mode: |
| os.makedirs(dir, mode) |
| else: |
| os.makedirs(dir) |
| except Exception as e: |
| # We might have hit EEXIST due to a race condition (see bug 463411) -- try again once |
| if try_again: |
| return maybe_create_dir(dir, mode, False) |
| print("nsinstall: failed to create directory {0}: {1}".format(dir, e)) |
| return 1 |
| else: |
| return 0 |
| |
| if options.X: |
| options.X = [os.path.abspath(p) for p in options.X] |
| |
| if options.D: |
| return maybe_create_dir(args[0], options.m, True) |
| |
| # nsinstall arg1 [...] directory |
| if len(args) < 2: |
| p.error('not enough arguments') |
| |
| def copy_all_entries(entries, target): |
| for e in entries: |
| e = os.path.abspath(e) |
| if options.X and e in options.X: |
| continue |
| |
| dest = os.path.join(target, os.path.basename(e)) |
| dest = os.path.abspath(dest) |
| handleTarget(e, dest) |
| if options.m: |
| os.chmod(dest, options.m) |
| |
| # set up handler |
| if options.d: |
| # we're supposed to create directories |
| def handleTarget(srcpath, targetpath): |
| # target directory was already created, just use mkdir |
| os.mkdir(targetpath) |
| else: |
| # we're supposed to copy files |
| def handleTarget(srcpath, targetpath): |
| if os.path.isdir(srcpath): |
| if not os.path.exists(targetpath): |
| os.mkdir(targetpath) |
| entries = [os.path.join(srcpath, e) for e in os.listdir(srcpath)] |
| copy_all_entries(entries, targetpath) |
| # options.t is not relevant for directories |
| if options.m: |
| os.chmod(targetpath, options.m) |
| else: |
| if os.path.exists(targetpath): |
| # On Windows, read-only files can't be deleted |
| if sys.platform == "win32": |
| os.chmod(targetpath, stat.S_IWUSR) |
| os.remove(targetpath) |
| if options.t: |
| shutil.copy2(srcpath, targetpath) |
| else: |
| shutil.copy(srcpath, targetpath) |
| |
| # the last argument is the target directory |
| target = args.pop() |
| # ensure target directory (importantly, we do not apply a mode to the directory |
| # because we want to copy files into it and the mode might be read-only) |
| rv = maybe_create_dir(target, None, True) |
| if rv != 0: |
| return rv |
| |
| copy_all_entries(args, target) |
| return 0 |
| |
| # nsinstall as a native command is always UTF-8 |
| def nsinstall(argv): |
| return _nsinstall_internal([unicode(arg, "utf-8") for arg in argv]) |
| |
| if __name__ == '__main__': |
| # sys.argv corrupts characters outside the system code page on Windows |
| # <http://bugs.python.org/issue2128>. Use ctypes instead. This is also |
| # useful because switching to Unicode strings makes python use the wide |
| # Windows APIs, which is what we want here since the wide APIs normally do a |
| # better job at handling long paths and such. |
| if sys.platform == "win32": |
| import ctypes |
| from ctypes import wintypes |
| GetCommandLine = ctypes.windll.kernel32.GetCommandLineW |
| GetCommandLine.argtypes = [] |
| GetCommandLine.restype = wintypes.LPWSTR |
| |
| CommandLineToArgv = ctypes.windll.shell32.CommandLineToArgvW |
| CommandLineToArgv.argtypes = [wintypes.LPWSTR, ctypes.POINTER(ctypes.c_int)] |
| CommandLineToArgv.restype = ctypes.POINTER(wintypes.LPWSTR) |
| |
| argc = ctypes.c_int(0) |
| argv_arr = CommandLineToArgv(GetCommandLine(), ctypes.byref(argc)) |
| # The first argv will be "python", the second will be the .py file |
| argv = argv_arr[1:argc.value] |
| else: |
| # For consistency, do it on Unix as well |
| if sys.stdin.encoding is not None: |
| argv = [unicode(arg, sys.stdin.encoding) for arg in sys.argv] |
| else: |
| argv = [unicode(arg) for arg in sys.argv] |
| |
| sys.exit(_nsinstall_internal(argv[1:])) |