Ticket #13505: program-scripts-python-shebang-fixup-V2.patch

File program-scripts-python-shebang-fixup-V2.patch, 14.0 KB (added by Gary Buhrmaster, 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
     5from __future__ import print_function
     6from __future__ import unicode_literals
     7import sys
     8import os
     9from stat import ST_MODE
     10import getopt
     11
     12def 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
     69def 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
     217if __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 ) 
    22
    33TEMPLATE = aux
    44
    5 installscripts.path = $${PREFIX}/share/mythtv
    6 installscripts.files = database/*
     5#
     6# database backup/restore scripts are standalone
     7#
     8database_scripts.path = $${PREFIX}/share/mythtv
     9database_scripts.files = database/*
     10INSTALLS += database_scripts
    711
    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#
    1032
    11 INSTALLS += installscripts installinternetscripts
     33using_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#
     127using_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}