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

File program-scripts-python-shebang-fixup.patch, 11.8 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 100644
    index 0000000000..9533c5b6fc
    - +  
     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            continue
     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                        continue
     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..e32a5943ea 100644
    a b include ( ../../settings.pro ) 
    22
    33TEMPLATE = aux
    44
    5 installscripts.path = $${PREFIX}/share/mythtv
    6 installscripts.files = database/*
     5# database backup/restore scripts are standalone
     6database.path = $${PREFIX}/share/mythtv
     7database.files = database/*
     8INSTALLS += database
    79
    8 installinternetscripts.path = $${PREFIX}/share/mythtv
    9 installinternetscripts.files = internetcontent metadata hardwareprofile
     10#
     11# We exploit the .extra member to modify the shebangs on
     12# python scripts.  Since most of the python scripts depend
     13# on the python bindings, and when using_bindings_python
     14# is true we know we have the bindings and a python
     15# executable (verified as part of configure), we can use
     16# a python script to modify the shebang on scripts in the
     17# same way that the bindings setup.py will modify the
     18# shebangs for executable scripts (typically something of
     19# the form #!/usr/bin/pythonX).  An exception (there is
     20# always an exception) is that the music metadata python
     21# scripts can run without the python bindings, so we try
     22# another way that may be less accurate (it is expected
     23# that typically one will build the python bindings,
     24# making this case less likely).
     25#
     26
     27# hardwareprofile
     28using_bindings_python {
     29    hardwareprofile.extra = $${QMAKE_COPY_DIR} hardwareprofile $${OBJECTS_DIR}/ $$escape_expand(\n\t) \
     30                            $${PYTHON} ./python_pathfix.py $${OBJECTS_DIR}/hardwareprofile
     31    hardwareprofile.path = $${PREFIX}/share/mythtv
     32    hardwareprofile.files = $${OBJECTS_DIR}/hardwareprofile
     33    hardwareprofile.CONFIG += no_check_exist
     34    INSTALLS += hardwareprofile
     35}
     36
     37# metadata
     38using_bindings_python {
     39    metadata.extra = $${QMAKE_COPY_DIR} metadata $${OBJECTS_DIR}/ $$escape_expand(\n\t) \
     40                     $${PYTHON} ./python_pathfix.py $${OBJECTS_DIR}/metadata
     41    metadata.path = $${PREFIX}/share/mythtv
     42    metadata.files = $${OBJECTS_DIR}/metadata
     43    metadata.CONFIG += no_check_exist
     44    INSTALLS += metadata
     45} else {
     46    # Try to verify our python exists
     47    win32 {
     48        PYTHON_BIN = $$system(where $${PYTHON})
     49    }
     50    unix|macx|mingw {
     51        PYTHON_BIN = $$system(which $${PYTHON})
     52    }
     53    metadata.extra = $${QMAKE_MKDIR} $${OBJECTS_DIR}/metadata/Music $$escape_expand(\n\t) \
     54                     $${QMAKE_COPY_DIR} metadata/Music $${OBJECTS_DIR}/metadata
     55    !isEmpty(PYTHON_BIN) {
     56        metadata.extra += $$escape_expand(\n\t)
     57        metadata.extra += $${PYTHON} ./python_pathfix.py $${OBJECTS_DIR}/metadata/Music
     58    }
     59    metadata.path = $${PREFIX}/share/mythtv
     60    metadata.files = $${OBJECTS_DIR}/metadata
     61    metadata.CONFIG += no_check_exist
     62    INSTALLS += metadata
     63}
     64
     65# internetcontent (perl)
     66using_bindings_perl {
     67    internetcontent_perl.path = $${PREFIX}/share/mythtv/internetcontent
     68    internetcontent_perl.files += internetcontent/nv_perl_libs internetcontent/*.pl
     69    INSTALLS += internetcontent_perl
     70}
     71
     72# internetcontent (python)
     73using_bindings_python {
     74    internetcontent_python.extra = $${QMAKE_MKDIR} $${OBJECTS_DIR}/internetcontent $$escape_expand(\n\t) \
     75                            $${QMAKE_COPY_DIR} internetcontent/nv_python_libs $${OBJECTS_DIR}/internetcontent/ $$escape_expand(\n\t) \
     76                            $${QMAKE_COPY_FILE} internetcontent/*.py $${OBJECTS_DIR}/internetcontent/ $$escape_expand(\n\t) \
     77                            $${PYTHON} ./python_pathfix.py $${OBJECTS_DIR}/internetcontent
     78    internetcontent_python.path = $${PREFIX}/share/mythtv
     79    internetcontent_python.files = $${OBJECTS_DIR}/internetcontent
     80    internetcontent_python.CONFIG += no_check_exist
     81    INSTALLS += internetcontent_python
     82}
     83
     84unix|macx|mingw:QMAKE_CLEAN += -r $${OBJECTS_DIR}/hardwareprofile $${OBJECTS_DIR}/internetcontent $${OBJECTS_DIR}/metadata
     85win32:QMAKE_CLEAN += /s /f /q $${OBJECTS_DIR}/hardwareprofile $${OBJECTS_DIR}/internetcontent $${OBJECTS_DIR}/metadata $$escape_expand(\n\t) \
     86                     rd /s /q $${OBJECTS_DIR}/hardwareprofile $${OBJECTS_DIR}/internetcontent $${OBJECTS_DIR}/metadata
    1087
    11 INSTALLS += installscripts installinternetscripts