Ticket #13505: program-scripts-python-shebang-fixup-V2.patch
File program-scripts-python-shebang-fixup-V2.patch, 14.0 KB (added by , 4 years ago) |
---|
-
new file mythtv/programs/scripts/python_pathfix.py
diff --git a/mythtv/programs/scripts/python_pathfix.py b/mythtv/programs/scripts/python_pathfix.py new file mode 100755 index 0000000000..05029a8c07
- + 1 # 2 # python_pathfix - adjust python shebang paths 3 # 4 5 from __future__ import print_function 6 from __future__ import unicode_literals 7 import sys 8 import os 9 from stat import ST_MODE 10 import getopt 11 12 def main(): 13 14 # Pick up default interpreter (ours) 15 python_interpreter = os.path.normcase(sys.executable).encode() 16 17 # Don't bother reporting 18 verbose = False 19 20 # Set default return code 21 ret = 0 22 23 usage = ('usage: %s -v -i /python_interpreter file-or-directory ...\n' % sys.argv[0]) 24 25 try: 26 opts, args = getopt.getopt(sys.argv[1:], 'i:v') 27 except getopt.GetoptError as msg: 28 sys.stderr.write(str(msg) + '\n') 29 sys.stderr.write(usage) 30 sys.exit(1) 31 for o, a in opts: 32 if o == '-i': 33 python_interpreter = a.encode() 34 if o == '-v': 35 verbose = True 36 37 if not python_interpreter or not python_interpreter.startswith(b'/'): 38 sys.stderr.write('-i interpreter option invalid (must be specified as a full path)\n') 39 sys.stderr.write(usage) 40 sys.exit(1) 41 42 if not args: 43 sys.stderr.write('file or directories not specified\n') 44 sys.stderr.write(usage) 45 sys.exit(1) 46 47 for arg in args: 48 if os.path.islink(arg): 49 pass 50 if os.path.isfile(arg): 51 if pathfix(filename=arg, 52 verbose=verbose, 53 python_interpreter=python_interpreter): 54 ret = 1 55 if os.path.isdir(arg): 56 for root, subdirs, files in os.walk(arg): 57 for f in files: 58 filename = os.path.join(root, f) 59 if os.path.islink(filename): 60 pass 61 if os.path.isfile(filename): 62 if pathfix(filename=filename, 63 verbose=verbose, 64 python_interpreter=python_interpreter): 65 ret = 1 66 67 sys.exit(ret) 68 69 def pathfix(filename=None, verbose=False, python_interpreter='/usr/bin/python'): 70 if not filename: 71 if verbose: 72 sys.stdout.write('filename not provided\n') 73 return 1 74 # open input file 75 try: 76 infile = open(filename, 'rb') 77 except IOError as msg: 78 sys.stderr.write('%s: open failed: %r\n' % (filename, msg)) 79 return 1 80 # process first line 81 try: 82 firstline = infile.readline() 83 except IOError as msg: 84 sys.stderr.write('%s: read failed: %r\n' % (filename, msg)) 85 try: 86 infile.close() 87 except IOError as msg: 88 sys.stderr.write('%s: close failed: %r\n' % (filename, msg)) 89 return 1 90 if firstline.rstrip(b'\n').find(b' -') != -1: 91 existingargs = firstline.rstrip(b'\n')[firstline.rstrip(b'\n').find(b' -'):] 92 else: 93 existingargs = b'' 94 newline = b'#!' + python_interpreter + existingargs + b'\n' 95 if (not firstline.startswith(b'#!')) or (b"python" not in firstline) or (firstline == newline): 96 if verbose: 97 sys.stdout.write('%s: unchanged\n' % (filename)) 98 try: 99 infile.close() 100 except IOError as msg: 101 sys.stderr.write('%s: close failed: %r\n' % (filename, msg)) 102 return 0 103 # create temporary output 104 head, tail = os.path.split(filename) 105 tempname = os.path.join(head, '@' + tail) 106 try: 107 outfile = open(tempname, 'wb') 108 except IOError as msg: 109 sys.stderr.write('%s: create failed: %r\n' % (tempname, msg)) 110 try: 111 infile.close() 112 except IOError as msg: 113 sys.stderr.write('%s: close failed: %r\n' % (filename, msg)) 114 return 1 115 if verbose: 116 sys.stdout.write('%s: updating\n' % (filename)) 117 # write first new line to temporary output 118 try: 119 outfile.write(newline) 120 except IOError as msg: 121 sys.stderr.write('%s: write failed: %r\n' % (tempname, msg)) 122 try: 123 infile.close() 124 except IOError as msg: 125 sys.stderr.write('%s: close failed: %r\n' % (filename, msg)) 126 try: 127 outfile.close() 128 except IOError as msg: 129 sys.stderr.write('%s: close failed: %r\n' % (tempname, msg)) 130 return 1 131 # copy rest of file 132 while True: 133 try: 134 chunk = infile.read(32678) 135 except IOError as msg: 136 sys.stderr.write('%s: read failed: %r\n' % (filename, msg)) 137 try: 138 infile.close() 139 except IOError as msg: 140 sys.stderr.write('%s: close failed: %r\n' % (filename, msg)) 141 try: 142 outfile.close() 143 except IOError as msg: 144 sys.stderr.write('%s: close failed: %r\n' % (tempname, msg)) 145 try: 146 os.remove(tempname) 147 except OSError as msg: 148 sys.stderr.write('%s: remove failed: %r\n' % (tempname, msg)) 149 return 1 150 if not chunk: 151 break 152 try: 153 outfile.write(chunk) 154 except IOError as msg: 155 sys.stderr.write('%s: write failed: %r\n' % (tempname, msg)) 156 try: 157 infile.close() 158 except IOError as msg: 159 sys.stderr.write('%s: close failed: %r\n' % (filename, msg)) 160 try: 161 outfile.close() 162 except IOError as msg: 163 sys.stderr.write('%s: close failed: %r\n' % (tempname, msg)) 164 try: 165 os.remove(tempname) 166 except OSError as msg: 167 sys.stderr.write('%s: remove failed: %r\n' % (tempname, msg)) 168 return 1 169 # close files 170 try: 171 infile.close() 172 except IOError as msg: 173 sys.stderr.write('%s: close failed: %r\n' % (filename, msg)) 174 try: 175 outfile.close() 176 except IOError as msg: 177 sys.stderr.write('%s: close failed: %r\n' % (tempname, msg)) 178 try: 179 os.remove(tempname) 180 except OSError as msg: 181 sys.stderr.write('%s: remove failed: %r\n' % (tempname, msg)) 182 return 1 183 try: 184 outfile.close() 185 except IOError as msg: 186 sys.stderr.write('%s: close failed: %r\n' % (tempname, msg)) 187 try: 188 os.remove(tempname) 189 except OSError as msg: 190 sys.stderr.write('%s: remove failed: %r\n' % (tempname, msg)) 191 return 1 192 # copy the file's mode to the temp file 193 try: 194 statbuf = os.stat(filename) 195 os.chmod(tempname, statbuf[ST_MODE] & 0o7777) 196 except OSError as msg: 197 sys.stderr.write('%s: warning, chmod failed: %r\n' % (tempname, msg)) 198 # Remove original file 199 try: 200 os.remove(filename) 201 except OSError as msg: 202 sys.stderr.write('%s: remove failed: %r\n' % (filename, msg)) 203 try: 204 os.remove(tempname) 205 except OSError as msg: 206 sys.stderr.write('%s: remove failed: %r\n' % (tempname, msg)) 207 return 1 208 # move the temp file to the original file 209 try: 210 os.rename(tempname, filename) 211 except OSError as msg: 212 sys.stderr.write('%s: rename failed: %r\n' % (filename, msg)) 213 # leave the tempname alone (might be used for recovery) 214 return 1 215 return 0 216 217 if __name__ == '__main__': 218 main() -
mythtv/programs/scripts/scripts.pro
diff --git a/mythtv/programs/scripts/scripts.pro b/mythtv/programs/scripts/scripts.pro index 3676d8b997..5656edeeb9 100644
a b include ( ../../settings.pro ) 2 2 3 3 TEMPLATE = aux 4 4 5 installscripts.path = $${PREFIX}/share/mythtv 6 installscripts.files = database/* 5 # 6 # database backup/restore scripts are standalone 7 # 8 database_scripts.path = $${PREFIX}/share/mythtv 9 database_scripts.files = database/* 10 INSTALLS += database_scripts 7 11 8 installinternetscripts.path = $${PREFIX}/share/mythtv 9 installinternetscripts.files = internetcontent metadata hardwareprofile 12 # 13 # We leverage the capability of qmake to invoke an extra 14 # "compiler" to transform the python scripts and adjust 15 # python script shebangs. Since the scripts depend on 16 # the python bindings, and when using_python_bindings is 17 # true we know we have the bindings and a usable python 18 # executable (verified as part of configure), we can use 19 # a python script to modify the shebang on scripts in the 20 # same way that the bindings setup.py will modify the 21 # shebangs for executable scripts (typically something of 22 # the form #!/usr/bin/pythonX). 23 # 24 # However, there is an exception for the Music metadata 25 # scripts, which do not require the python bindings. We 26 # try another way to locate a python executable to be 27 # used to modify the shebangs that may be less accurate. 28 # It is expected that typically one will build the 29 # python bindings, making thise case less likely in 30 # practice. 31 # 10 32 11 INSTALLS += installscripts installinternetscripts 33 using_bindings_python { 34 35 PYTHON_SOURCES += hardwareprofile/* 36 PYTHON_SOURCES += internetcontent/*.py internetcontent/nv_python_libs 37 PYTHON_SOURCES += metadata/* 38 39 python_pathfix.output = $${OBJECTS_DIR}/${QMAKE_FILE_NAME} 40 python_pathfix.commands = $${QMAKE_COPY_DIR} ${QMAKE_FILE_NAME} $${OBJECTS_DIR}/${QMAKE_FILE_NAME} $$escape_expand(\n\t) \ 41 $${PYTHON} ./python_pathfix.py $${OBJECTS_DIR}/${QMAKE_FILE_NAME} 42 python_pathfix.input = PYTHON_SOURCES 43 python_pathfix.variable_out = PYTHON_FIXUPS 44 python_pathfix.CONFIG += no_link target_predeps 45 QMAKE_EXTRA_COMPILERS += python_pathfix 46 47 hardwareprofile_scripts.path = $${PREFIX}/share/mythtv/ 48 hardwareprofile_scripts.files = $${OBJECTS_DIR}/hardwareprofile 49 hardwareprofile_scripts.CONFIG += no_check_exist 50 51 metadata_scripts.path = $${PREFIX}/share/mythtv/ 52 metadata_scripts.files = $${OBJECTS_DIR}/metadata 53 metadata_scripts.CONFIG += no_check_exists 54 55 internetcontent_scripts.path = $${PREFIX}/share/mythtv/ 56 internetcontent_scripts.files = $${OBJECTS_DIR}/internetcontent 57 internetcontent_scripts.CONFIG += no_check_exists 58 59 INSTALLS += hardwareprofile_scripts metadata_scripts internetcontent_scripts 60 61 unix|macx|mingw:QMAKE_CLEAN += -r $${OBJECTS_DIR}/hardwareprofile/distros \ 62 $${OBJECTS_DIR}/internetcontent/nv_python_libs \ 63 $${OBJECTS_DIR}/metadata/Movie \ 64 $${OBJECTS_DIR}/metadata/Music \ 65 $${OBJECTS_DIR}/metadata/Television 66 unix|macx|mingw:QMAKE_DISTCLEAN += -r $${OBJECTS_DIR}/hardwareprofile \ 67 $${OBJECTS_DIR}/internetcontent \ 68 $${OBJECTS_DIR}/metadata 69 win32-msvc*:QMAKE_CLEAN += /s /f /q $${OBJECTS_DIR}/hardwareprofile/distros \ 70 $${OBJECTS_DIR}/internetcontent/nv_python_libs \ 71 $${OBJECTS_DIR}/metadata/Movie \ 72 $${OBJECTS_DIR}/metadata/Music \ 73 $${OBJECTS_DIR}/metadata/Television $$escape_expand(\n\t) \ 74 rd /s /q $${OBJECTS_DIR}/hardwareprofile/distros \ 75 $${OBJECTS_DIR}/internetcontent/nv_python_libs \ 76 $${OBJECTS_DIR}/metadata/Movie \ 77 $${OBJECTS_DIR}/metadata/Music \ 78 $${OBJECTS_DIR}/metadata/Television 79 win32-msvc*:QMAKE_DISTCLEAN += /s /f /q $${OBJECTS_DIR}/*.* $$escape_expand(\n\t) \ 80 rd /s /q $${OBJECTS_DIR}/hardwareprofile \ 81 $${OBJECTS_DIR}/internetcontent \ 82 $${OBJECTS_DIR}/metadata 83 84 } else { 85 86 # Try to verify our python exists 87 win32 { 88 PYTHON_BIN = $$system(where $${PYTHON}) 89 } 90 unix|macx|mingw { 91 PYTHON_BIN = $$system(which $${PYTHON}) 92 } 93 94 PYTHON_SOURCES += metadata/Music/* 95 96 python_pathfix.output = $${OBJECTS_DIR}/${QMAKE_FILE_NAME} 97 python_pathfix.commands = $${QMAKE_COPY_DIR} ${QMAKE_FILE_NAME} $${OBJECTS_DIR}/${QMAKE_FILE_NAME} 98 !isEmpty(PYTHON_BIN) { 99 python_pathfix.commands += $$escape_expand(\n\t) 100 python_pathfix.commands += $${PYTHON} ./python_pathfix.py $${OBJECTS_DIR}/${QMAKE_FILE_NAME} 101 } 102 python_pathfix.input = PYTHON_SOURCES 103 python_pathfix.variable_out = PYTHON_FIXUPS 104 python_pathfix.CONFIG += no_link target_predeps 105 QMAKE_EXTRA_COMPILERS += python_pathfix 106 107 internetcontent_python_scripts.path = $${PREFIX}/share/mythtv/ 108 internetcontent_python_scripts.files = $${OBJECTS_DIR}/metadata 109 internetcontent_python_scripts.CONFIG += no_check_exist 110 111 INSTALLS += internetcontent_python_scripts 112 113 unix|macx|mingw:QMAKE_CLEAN += -r $${OBJECTS_DIR}/metadata/Music/* 114 unix|macx|mingw:QMAKE_DISTCLEAN += -r $${OBJECTS_DIR}/metadata 115 win32-msvc*:QMAKE_CLEAN += /s /f /q $${OBJECTS_DIR}/metadata/Music/*.* $$escape_expand(\n\t) \ 116 rd /s /q $${OBJECTS_DIR}/metadata/Music/*.* 117 win32-msvc*:QMAKE_DISTCLEAN += /s /f /q $${OBJECTS_DIR}/metadata/*.* $$escape_expand(\n\t) \ 118 rd /s /q $${OBJECTS_DIR}/metadata 119 120 121 } 122 123 # 124 # internetcontent perl scripts do not require transformation 125 # but do require the perl bindings to function 126 # 127 using_bindings_perl { 128 129 internetcontent_perl_scripts.path = $${PREFIX}/share/mythtv/internetcontent 130 internetcontent_perl_scripts.files += internetcontent/nv_perl_libs internetcontent/*.pl 131 132 INSTALLS += internetcontent_perl_scripts 133 134 }