]> git.lyx.org Git - lyx.git/blobdiff - lib/configure.py
Amend 212314ada71
[lyx.git] / lib / configure.py
index 63b88f7bbbc23f70042c23ce65eefb27c83d0026..ac3b15f0a70e2002853fd1d7e77762d96854aed5 100644 (file)
@@ -1,4 +1,4 @@
-#! /usr/bin/env python
+#! /usr/bin/python
 # -*- coding: utf-8 -*-
 #
 # file configure.py
 from __future__ import print_function
 import glob, logging, os, errno, re, shutil, subprocess, sys, stat
 
+if sys.version_info[0] < 3:
+    import codecs
+    open = codecs.open
+
+
 # set up logging
 logging.basicConfig(level = logging.DEBUG,
     format = '%(levelname)s: %(message)s', # ignore application name
@@ -25,6 +30,13 @@ console.setFormatter(formatter)
 logger = logging.getLogger('LyX')
 logger.addHandler(console)
 
+def quoteIfSpace(name):
+    " utility function: quote name if it contains spaces "
+    if ' ' in name:
+        return '"' + name + '"'
+    else:
+        return name
+
 def writeToFile(filename, lines, append = False):
     " utility function: write or append lines to filename "
     if append:
@@ -39,7 +51,7 @@ def addToRC(lines):
     ''' utility function: shortcut for appending lines to outfile
         add newline at the end of lines.
     '''
-    if lines.strip() != '':
+    if lines.strip():
         writeToFile(outfile, lines + '\n', append = True)
         logger.debug('Add to RC:\n' + lines + '\n\n')
 
@@ -62,10 +74,10 @@ def removeFiles(filenames):
             pass
 
 
-def cmdOutput(cmd, async = False):
+def cmdOutput(cmd, asynchronous = False):
     '''utility function: run a command and get its output as a string
         cmd: command to run
-        async: if False, return whole output as a string, otherwise
+        asynchronous: if False, return whole output as a string, otherwise
                return the stdout handle from which the output can be
                read (the caller is then responsible for closing it)
     '''
@@ -80,7 +92,7 @@ def cmdOutput(cmd, async = False):
     pipe = subprocess.Popen(cmd, shell=b, close_fds=b, stdin=subprocess.PIPE,
                             stdout=subprocess.PIPE, universal_newlines=True)
     pipe.stdin.close()
-    if async:
+    if asynchronous:
         return pipe.stdout
     output = pipe.stdout.read()
     pipe.stdout.close()
@@ -168,14 +180,14 @@ def checkUpgrade():
         logger.info('Checking for upgrade from previous version.')
         parent = os.path.dirname(cwd)
         appname = basename[:(-len(version_suffix))]
-        for version in ['-2.1', '-2.0', '-1.6' ]:
+        for version in ['-2.3', '-2.2', '-2.1', '-2.0', '-1.6' ]:
             logger.debug('Checking for upgrade from previous version ' + version)
             previous = os.path.join(parent, appname + version)
             logger.debug('previous = ' + previous)
             if os.path.isdir( previous ):
                 logger.info('Found directory "%s".', previous)
                 copy_tree( previous, cwd, True )
-                logger.info('Content copied to directory "%s".', cwd)
+                logger.info('Content copied from directory "%s".', previous)
                 return
 
 
@@ -267,10 +279,11 @@ def checkProg(description, progs, rc_entry = [], path = [], not_found = ''):
     if "PATHEXT" in os.environ:
         extlist = extlist + os.environ["PATHEXT"].split(os.pathsep)
     global java, perl
+    unquoted_space = re.compile(r'''((?:[^ "']|"[^"]*"|'[^']*')+)''')
     for idx in range(len(progs)):
         # ac_prog may have options, ac_word is the command name
-        ac_prog = progs[idx]
-        ac_word = ac_prog.split(' ')[0]
+        ac_prog = progs[idx].replace('"', '\\"')
+        ac_word = unquoted_space.split(progs[idx])[1::2][0].strip('"')
         if (ac_word.endswith('.class') or ac_word.endswith('.jar')) and java == '':
             continue
         if ac_word.endswith('.pl') and perl == '':
@@ -503,8 +516,7 @@ def checkInkscape():
     ''' The answer of the real inkscape is validated and a fake binary used if this fails '''
     if sys.platform == 'darwin':
         version_string = cmdOutput("inkscape --version")
-        match = re.match('^Inkscape', version_string)
-        if match:
+        if version_string.startswith('Inkscape'):
             return 'inkscape'
         else:
             return 'inkscape-binary'
@@ -518,12 +530,12 @@ def checkInkscape():
     try:
         aKey = winreg.OpenKey(aReg, r"inkscape.svg\DefaultIcon")
         val = winreg.QueryValueEx(aKey, "")
-        return str(val[0]).split('"')[1].replace('.exe', '')
+        return str(val[0]).split('"')[1]
     except EnvironmentError:
         try:
             aKey = winreg.OpenKey(aReg, r"Applications\inkscape.exe\shell\open\command")
             val = winreg.QueryValueEx(aKey, "")
-            return str(val[0]).split('"')[1].replace('.exe', '')
+            return str(val[0]).split('"')[1]
         except EnvironmentError:
             return 'inkscape'
 
@@ -533,7 +545,7 @@ def checkLatex(dtl_tools):
     path, PPLATEX = checkProg('a DVI postprocessing program', ['pplatex $$i'])
     #-----------------------------------------------------------------
     path, PLATEX = checkProg('pLaTeX, the Japanese LaTeX', ['platex $$i'])
-    if PLATEX != '':
+    if PLATEX:
         # check if PLATEX is pLaTeX2e
         writeToFile('chklatex.ltx', r'\nonstopmode\makeatletter\@@end')
         # run platex on chklatex.ltx and check result
@@ -549,12 +561,12 @@ def checkLatex(dtl_tools):
         PPLATEX = LATEX
     if dtl_tools:
         # Windows only: DraftDVI
-        addToRC(r'''\converter latex      dvi2       "%s"      "latex"
+        addToRC(r'''\converter latex      dvi2       "%s"      "latex,hyperref-driver=dvips"
 \converter dvi2       dvi        "python -tt $$s/scripts/clean_dvi.py $$i $$o" ""''' % PPLATEX)
     else:
-        addToRC(r'\converter latex      dvi        "%s"        "latex"' % PPLATEX)
+        addToRC(r'\converter latex      dvi        "%s"        "latex,hyperref-driver=dvips"' % PPLATEX)
     # no latex
-    if LATEX != '':
+    if LATEX:
         # Check if latex is usable
         writeToFile('chklatex.ltx', r'''
 \nonstopmode
@@ -579,9 +591,9 @@ def checkLuatex():
     ''' Check if luatex is there '''
     path, LUATEX = checkProg('LuaTeX', ['lualatex $$i'])
     path, DVILUATEX = checkProg('LuaTeX (DVI)', ['dvilualatex $$i'])
-    if LUATEX != '':
+    if LUATEX:
         addToRC(r'\converter luatex      pdf5       "%s"       "latex=lualatex"' % LUATEX)
-    if DVILUATEX != '':
+    if DVILUATEX:
         addToRC(r'\converter dviluatex   dvi3        "%s"      "latex=dvilualatex"' % DVILUATEX)
 
 
@@ -621,7 +633,7 @@ def checkFormatEntries(dtl_tools):
     checkViewerEditor('a FEN viewer and editor', ['xboard -lpf $$i -mode EditPosition'],
         rc_entry = [r'\Format fen        fen     FEN                    "" "%%"        "%%"    ""      ""'])
     #
-    checkViewerEditor('a SVG viewer and editor', [inkscape_name],
+    checkViewerEditor('a SVG viewer and editor', [inkscape_gui],
         rc_entry = [r'''\Format svg        "svg" SVG                "" "%%" "%%"       "vector"        "image/svg+xml"
 \Format svgz       "svgz" "SVG (compressed)" "" "%%" "%%"      "vector,zipped=native"  ""'''],
         path = [inkscape_path])
@@ -755,7 +767,8 @@ def checkFormatEntries(dtl_tools):
 \Format lyx16x     16.lyx "LyX 1.6.x"             "" ""        ""      "document"      ""
 \Format lyx20x     20.lyx "LyX 2.0.x"             "" ""        ""      "document"      ""
 \Format lyx21x     21.lyx "LyX 2.1.x"             "" ""        ""      "document"      ""
-\Format lyx22x     22.lyx "LyX 2.2.x"             "" ""        ""      "document,menu=export"  ""
+\Format lyx22x     22.lyx "LyX 2.2.x"             "" ""        ""      "document"      ""
+\Format lyx23x     23.lyx "LyX 2.3.x"             "" ""        ""      "document,menu=export"  ""
 \Format clyx       cjklyx "CJK LyX 1.4.x (big5)"  "" ""        ""      "document"      ""
 \Format jlyx       cjklyx "CJK LyX 1.4.x (euc-jp)" "" ""       ""      "document"      ""
 \Format klyx       cjklyx "CJK LyX 1.4.x (euc-kr)" "" ""       ""      "document"      ""
@@ -772,10 +785,10 @@ def checkFormatEntries(dtl_tools):
 def checkConverterEntries():
     ''' Check all converters (\converter entries) '''
     checkProg('the pdflatex program', ['pdflatex $$i'],
-        rc_entry = [ r'\converter pdflatex   pdf2       "%%"   "latex=pdflatex"' ])
+        rc_entry = [ r'\converter pdflatex   pdf2       "%%"   "latex=pdflatex,hyperref-driver=pdftex"' ])
 
     checkProg('XeTeX', ['xelatex $$i'],
-        rc_entry = [ r'\converter xetex      pdf4       "%%"   "latex=xelatex"' ])
+        rc_entry = [ r'\converter xetex      pdf4       "%%"   "latex=xelatex,hyperref-driver=xetex"' ])
 
     checkLuatex()
 
@@ -793,12 +806,12 @@ def checkConverterEntries():
     in_binary_dir = os.path.join(lyx_binary_dir, 'tex2lyx')
     in_binary_dir = os.path.abspath(in_binary_dir).replace('\\', '/')
 
-    path, t2l = checkProg('a LaTeX/Noweb -> LyX converter', [in_binary_subdir, in_binary_subdir + version_suffix, in_binary_dir, in_binary_dir + version_suffix, 'tex2lyx' + version_suffix, 'tex2lyx'],
+    path, t2l = checkProg('a LaTeX/Noweb -> LyX converter', [quoteIfSpace(in_binary_subdir), quoteIfSpace(in_binary_subdir + version_suffix), quoteIfSpace(in_binary_dir), quoteIfSpace(in_binary_dir + version_suffix), 'tex2lyx' + version_suffix, 'tex2lyx'],
         rc_entry = [r'''\converter latex      lyx        "%% -f $$i $$o"       ""
 \converter latexclipboard lyx        "%% -fixedenc utf8 -f $$i $$o"    ""
 \converter literate   lyx        "%% -n -m noweb -f $$i $$o"   ""
-\converter sweave   lyx        "%% -n -m sweave -f $$i $$o"    "needauth"
-\converter knitr   lyx        "%% -n -m knitr -f $$i $$o"      "needauth"'''], not_found = 'tex2lyx')
+\converter sweave   lyx        "%% -n -m sweave -f $$i $$o"    ""
+\converter knitr   lyx        "%% -n -m knitr -f $$i $$o"      ""'''], not_found = 'tex2lyx')
     if path == '':
         logger.warning("Failed to find tex2lyx on your system.")
 
@@ -827,12 +840,12 @@ def checkConverterEntries():
 \converter knitr   dviluatex  "%%"     "needauth"'''])
     #
     checkProg('a Sweave -> R/S code converter', ['Rscript --verbose --no-save --no-restore $$s/scripts/lyxstangle.R $$i $$e $$r'],
-        rc_entry = [ r'\converter sweave      r      "%%"    "needauth"',
-                     r'\converter sweave-ja   r      "%%"    "needauth"' ])
+        rc_entry = [ r'\converter sweave      r      "%%"    ""',
+                     r'\converter sweave-ja   r      "%%"    ""' ])
     #
     checkProg('a knitr -> R/S code converter', ['Rscript --verbose --no-save --no-restore $$s/scripts/lyxknitr.R $$p$$i $$p$$o $$e $$r tangle'],
-        rc_entry = [ r'\converter knitr      r      "%%"    "needauth"',
-                     r'\converter knitr-ja   r      "%%"    "needauth"' ])
+        rc_entry = [ r'\converter knitr      r      "%%"    ""',
+                     r'\converter knitr-ja   r      "%%"    ""' ])
     #
     checkProg('an HTML -> LaTeX converter', ['html2latex $$i', 'gnuhtml2latex',
         'htmltolatex -input $$i -output $$o', 'htmltolatex.jar -input $$i -output $$o'],
@@ -927,7 +940,7 @@ def checkConverterEntries():
         rc_entry = [ r'\converter rtf      html        "%%"    ""' ])
     # Do not define a converter to pdf6, ps is a pure export format
     checkProg('a PS to PDF converter', ['ps2pdf $$i $$o'],
-        rc_entry = [ r'\converter ps         pdf        "%%"   ""' ])
+        rc_entry = [ r'\converter ps         pdf        "%%"   "hyperref-driver=dvips"' ])
     #
     checkProg('a PS to TXT converter', ['pstotext $$i > $$o'],
         rc_entry = [ r'\converter ps         text2      "%%"   ""' ])
@@ -977,13 +990,13 @@ def checkConverterEntries():
         rc_entry = [ r'\converter dvi        text4      "%%"   ""' ])
     #
     checkProg('a DVI to PS converter', ['dvips -o $$o $$i'],
-        rc_entry = [ r'\converter dvi        ps         "%%"   ""' ])
+        rc_entry = [ r'\converter dvi        ps         "%%"   "hyperref-driver=dvips"' ])
     #
     checkProg('a DVI to cropped EPS converter', ['dvips -E -o $$o $$i'],
         rc_entry = [ r'\converter dvi        eps3         "%%" ""' ])
     #
-    checkProg('a DVI to PDF converter', ['dvipdfmx -o $$o $$i', 'dvipdfm -o $$o $$i'],
-        rc_entry = [ r'\converter dvi        pdf3       "%%"   ""' ])
+    checkProg('a DVI to PDF converter', ['dvipdfmx', 'dvipdfm'],
+        rc_entry = [ r'\converter dvi        pdf3       "%%  -o $$o $$i"       "hyperref-driver=%%"' ])
     #
     checkProg('a fax program', ['kdeprintfax $$i', 'ksendfax $$i', 'hylapex $$i'],
         rc_entry = [ r'\converter ps         fax        "%%"   ""'])
@@ -997,11 +1010,11 @@ def checkConverterEntries():
 \converter fig        pdftex     "python -tt $$s/scripts/fig2pdftex.py $$i $$o"        ""
 \converter fig        pstex      "python -tt $$s/scripts/fig2pstex.py $$i $$o" ""''')
     #
-    checkProg('a SVG -> PDFTeX converter', [inkscape_name],
+    checkProg('a SVG -> PDFTeX converter', [inkscape_cl],
         rc_entry = [ r'\converter svg        pdftex     "python -tt $$s/scripts/svg2pdftex.py %% $$p$$i $$p$$o" ""'],
         path = [inkscape_path])
     #
-    checkProg('a SVG -> PSTeX converter', [inkscape_name],
+    checkProg('a SVG -> PSTeX converter', [inkscape_cl],
         rc_entry = [ r'\converter svg        pstex     "python -tt $$s/scripts/svg2pstex.py %% $$p$$i $$p$$o" ""'],
         path = [inkscape_path])
     #
@@ -1014,18 +1027,16 @@ def checkConverterEntries():
 \converter tgif       png        "tgif -print -color -png -o $$d $$i"  ""
 \converter tgif       pdf6       "tgif -print -color -pdf -stdout $$i > $$o"   ""'''])
     #
-    checkProg('a WMF -> EPS converter', ['metafile2eps $$i $$o', 'wmf2eps -o $$o $$i', inkscape_name + ' --file=%s$$i --export-area-drawing --without-gui --export-eps=%s$$o'
-               % (inkscape_fileprefix, inkscape_fileprefix)],
+    checkProg('a WMF -> EPS converter', ['metafile2eps $$i $$o', 'wmf2eps -o $$o $$i', inkscape_cl + ' --file=$$i --export-area-drawing --without-gui --export-eps=$$o'],
         rc_entry = [ r'\converter wmf        eps        "%%"   ""'])
     #
-    checkProg('an EMF -> EPS converter', ['metafile2eps $$i $$o', inkscape_name + ' --file=%s$$i --export-area-drawing --without-gui --export-eps=%s$$o'
-               % (inkscape_fileprefix, inkscape_fileprefix)],
+    checkProg('an EMF -> EPS converter', ['metafile2eps $$i $$o', inkscape_cl + ' --file=$$i --export-area-drawing --without-gui --export-eps=$$o'],
         rc_entry = [ r'\converter emf        eps        "%%"   ""'])
     #
-    checkProg('a WMF -> PDF converter', [inkscape_name + ' --file=%s$$i --export-area-drawing --without-gui --export-pdf=%s$$o' % (inkscape_fileprefix, inkscape_fileprefix)],
+    checkProg('a WMF -> PDF converter', [inkscape_cl + ' --file=$$i --export-area-drawing --without-gui --export-pdf=$$o'],
         rc_entry = [ r'\converter wmf        pdf6        "%%"  ""'])
     #
-    checkProg('an EMF -> PDF converter', [inkscape_name + ' --file=%s$$i --export-area-drawing --without-gui --export-pdf=%s$$o' % (inkscape_fileprefix, inkscape_fileprefix)],
+    checkProg('an EMF -> PDF converter', [inkscape_cl + ' --file=$$i --export-area-drawing --without-gui --export-pdf=$$o'],
         rc_entry = [ r'\converter emf        pdf6        "%%"  ""'])
     # Only define a converter to pdf6 for graphics
     checkProg('an EPS -> PDF converter', ['epstopdf'],
@@ -1071,20 +1082,17 @@ def checkConverterEntries():
         rc_entry = [ r'\converter svg        svgz       "%%"   ""'])
     # Only define a converter to pdf6 for graphics
     # Prefer rsvg-convert over inkscape since it is faster (see http://www.lyx.org/trac/ticket/9891)
-    checkProg('a SVG -> PDF converter', ['rsvg-convert -f pdf -o $$o $$i', inkscape_name + ' --file=%s$$i --export-area-drawing --without-gui --export-pdf=%s$$o'
-               % (inkscape_fileprefix, inkscape_fileprefix)],
+    checkProg('a SVG -> PDF converter', ['rsvg-convert -f pdf -o $$o $$i', inkscape_cl + ' --file=$$i --export-area-drawing --without-gui --export-pdf=$$o'],
         rc_entry = [ r'''\converter svg        pdf6       "%%"    ""
 \converter svgz       pdf6       "%%"    ""'''],
         path = ['', inkscape_path])
     #
-    checkProg('a SVG -> EPS converter', ['rsvg-convert -f ps -o $$o $$i', inkscape_name + ' --file=%s$$i --export-area-drawing --without-gui --export-eps=%s$$o'
-               % (inkscape_fileprefix, inkscape_fileprefix)],
+    checkProg('a SVG -> EPS converter', ['rsvg-convert -f ps -o $$o $$i', inkscape_cl + ' --file=$$i --export-area-drawing --without-gui --export-eps=$$o'],
         rc_entry = [ r'''\converter svg        eps        "%%"    ""
 \converter svgz       eps        "%%"    ""'''],
         path = ['', inkscape_path])
     #
-    checkProg('a SVG -> PNG converter', ['rsvg-convert -f png -o $$o $$i', inkscape_name + ' --without-gui --file=%s$$i --export-png=%s$$o'
-               % (inkscape_fileprefix, inkscape_fileprefix)],
+    checkProg('a SVG -> PNG converter', ['rsvg-convert -f png -o $$o $$i', inkscape_cl + ' --without-gui --file=$$i --export-png=$$o'],
         rc_entry = [ r'''\converter svg        png        "%%"    "",
 \converter svgz       png        "%%"    ""'''],
         path = ['', inkscape_path])
@@ -1106,7 +1114,7 @@ def checkConverterEntries():
 '''])
 
     path, lilypond = checkProg('a LilyPond -> EPS/PDF/PNG converter', ['lilypond'])
-    if (lilypond != ''):
+    if (lilypond):
         version_string = cmdOutput("lilypond --version")
         match = re.match('GNU LilyPond (\S+)', version_string)
         if match:
@@ -1129,9 +1137,9 @@ def checkConverterEntries():
             logger.info('+  found LilyPond, but could not extract version number.')
     #
     path, lilypond_book = checkProg('a LilyPond book (LaTeX) -> LaTeX converter', ['lilypond-book'])
-    if (lilypond_book != ''):
+    if (lilypond_book):
         version_string = cmdOutput("lilypond-book --version")
-        match = re.match('^(\S+)$', version_string)
+        match = re.match('(\S+)$', version_string)
         if match:
             version_number = match.groups()[0]
             version = version_number.split('.')
@@ -1185,6 +1193,7 @@ def checkConverterEntries():
 \converter lyx        lyx20x     "python -tt $$s/lyx2lyx/lyx2lyx -V 2.0 -o $$o $$i"    ""
 \converter lyx        lyx21x     "python -tt $$s/lyx2lyx/lyx2lyx -V 2.1 -o $$o $$i"    ""
 \converter lyx        lyx22x     "python -tt $$s/lyx2lyx/lyx2lyx -V 2.2 -o $$o $$i"    ""
+\converter lyx        lyx23x     "python -tt $$s/lyx2lyx/lyx2lyx -V 2.3 -o $$o $$i"    ""
 \converter lyx        clyx       "python -tt $$s/lyx2lyx/lyx2lyx -V 1.4 -o $$o -c big5   $$i"  ""
 \converter lyx        jlyx       "python -tt $$s/lyx2lyx/lyx2lyx -V 1.4 -o $$o -c euc_jp $$i"  ""
 \converter lyx        klyx       "python -tt $$s/lyx2lyx/lyx2lyx -V 1.4 -o $$o -c euc_kr $$i"  ""
@@ -1210,7 +1219,7 @@ def checkDocBook():
             r'''\converter docbook    dvi        ""    ""
 \converter docbook    html       ""    ""'''])
     #
-    if DOCBOOK != '':
+    if DOCBOOK:
         return ('yes', 'true', '\\def\\hasdocbook{yes}')
     else:
         return ('no', 'false', '')
@@ -1252,6 +1261,13 @@ def checkOtherEntries():
 \copier    program    "python -tt $$s/scripts/ext_copy.py $$i $$o"
 ''')
 
+def _checkForClassExtension(x):
+    '''if the extension for a latex class is not
+        provided, add .cls to the classname'''
+    if not '.' in x:
+        return x.strip() + '.cls'
+    else:
+        return x.strip()
 
 def processLayoutFile(file, bool_docbook):
     ''' process layout file and get a line of result
@@ -1280,31 +1296,24 @@ def processLayoutFile(file, bool_docbook):
         "scrbook" "scrbook" "book (koma-script)" "false" "scrbook.cls" "Books"
         "svjog" "svjour" "article (Springer - svjour/jog)" "false" "svjour.cls,svjog.clo" ""
     '''
-    def checkForClassExtension(x):
-        '''if the extension for a latex class is not
-           provided, add .cls to the classname'''
-        if not '.' in x:
-            return x.strip() + '.cls'
-        else:
-            return x.strip()
     classname = file.split(os.sep)[-1].split('.')[0]
     # return ('LaTeX', '[a,b]', 'a', ',b,c', 'article') for \DeclareLaTeXClass[a,b,c]{article}
-    p = re.compile(r'^\s*#\s*\\Declare(LaTeX|DocBook)Class\s*(\[([^,]*)(,.*)*\])*\s*{(.*)}\s*$')
-    q = re.compile(r'^\s*#\s*\\DeclareCategory{(.*)}\s*$')
+    p = re.compile('\s*#\s*\\\\Declare(LaTeX|DocBook)Class\s*(\[([^,]*)(,.*)*\])*\s*{(.*)}\s*$')
+    q = re.compile('\s*#\s*\\\\DeclareCategory{(.*)}\s*$')
     classdeclaration = ""
     categorydeclaration = '""'
-    for line in open(file).readlines():
-        res = p.search(line)
-        qres = q.search(line)
+    for line in open(file, 'r', encoding='utf8').readlines():
+        res = p.match(line)
+        qres = q.match(line)
         if res != None:
             (classtype, optAll, opt, opt1, desc) = res.groups()
-            avai = {'LaTeX':'false', 'DocBook':bool_docbook}[classtype]
+            avai = {'LaTeX': 'false', 'DocBook': bool_docbook}[classtype]
             if opt == None:
                 opt = classname
-                prereq_latex = checkForClassExtension(classname)
+                prereq_latex = _checkForClassExtension(classname)
             else:
                 prereq_list = optAll[1:-1].split(',')
-                prereq_list = list(map(checkForClassExtension, prereq_list))
+                prereq_list = list(map(_checkForClassExtension, prereq_list))
                 prereq_latex = ','.join(prereq_list)
             prereq_docbook = {'true':'', 'false':'docbook'}[bool_docbook]
             prereq = {'LaTeX':prereq_latex, 'DocBook':prereq_docbook}[classtype]
@@ -1314,9 +1323,9 @@ def processLayoutFile(file, bool_docbook):
                 return classdeclaration + " " + categorydeclaration
         if qres != None:
              categorydeclaration = '"%s"' % (qres.groups()[0])
-             if classdeclaration != "":
+             if classdeclaration:
                  return classdeclaration + " " + categorydeclaration
-    if classdeclaration != "":
+    if classdeclaration:
         return classdeclaration + " " + categorydeclaration
     logger.warning("Layout file " + file + " has no \DeclareXXClass line. ")
     return ""
@@ -1337,7 +1346,7 @@ def checkLatexConfig(check_config, bool_docbook):
         # fails, we still have something to start lyx.
         logger.info(msg + ' default values')
         logger.info('+checking list of textclasses... ')
-        tx = open('textclass.lst', 'w')
+        tx = open('textclass.lst', 'w', encoding='utf8')
         tx.write('''
 # This file declares layouts and their associated definition files
 # (include dir. relative to the place where this file is).
@@ -1356,20 +1365,19 @@ def checkLatexConfig(check_config, bool_docbook):
             # get stuff between /xxxx.layout .
             classname = file.split(os.sep)[-1].split('.')[0]
             #  tr ' -' '__'`
-            cleanclass = classname.replace(' ', '_')
-            cleanclass = cleanclass.replace('-', '_')
+            cleanclass = classname.replace(' ', '_').replace('-', '_')
             # make sure the same class is not considered twice
             if foundClasses.count(cleanclass) == 0: # not found before
                 foundClasses.append(cleanclass)
                 retval = processLayoutFile(file, bool_docbook)
-                if retval != "":
-                    tx.write(retval)
+                if retval:
+                    tx.write(retval + os.linesep)
         tx.close()
         logger.info('\tdone')
     if not os.path.isfile('packages.lst') or not check_config:
         logger.info('+generating default list of packages... ')
         removeFiles(['packages.lst'])
-        tx = open('packages.lst', 'w')
+        tx = open('packages.lst', 'w', encoding='utf8')
         tx.close()
         logger.info('\tdone')
     if not check_config:
@@ -1386,9 +1394,9 @@ def checkLatexConfig(check_config, bool_docbook):
     # Construct the list of classes to test for.
     # build the list of available layout files and convert it to commands
     # for chkconfig.ltx
-    declare = re.compile(b'^\\s*#\\s*\\\\Declare(LaTeX|DocBook)Class\\s*(\[([^,]*)(,.*)*\])*\\s*{(.*)}\\s*$')
-    category = re.compile(b'^\\s*#\\s*\\\\DeclareCategory{(.*)}\\s*$')
-    empty = re.compile(b'^\\s*$')
+    declare = re.compile('\\s*#\\s*\\\\Declare(LaTeX|DocBook)Class\\s*(\[([^,]*)(,.*)*\])*\\s*{(.*)}\\s*$')
+    category = re.compile('\\s*#\\s*\\\\DeclareCategory{(.*)}\\s*$')
+    empty = re.compile('\\s*$')
     testclasses = list()
     for file in (glob.glob( os.path.join('layouts', '*.layout') )
                  + glob.glob( os.path.join(srcdir, 'layouts', '*.layout' ) ) ):
@@ -1396,34 +1404,32 @@ def checkLatexConfig(check_config, bool_docbook):
         if not os.path.isfile(file):
             continue
         classname = file.split(os.sep)[-1].split('.')[0]
-        decline = b""
-        catline = b""
-        for line in open(file, 'rb').readlines():
-            if not empty.match(line) and line[0] != b'#'[0]:
-                if decline == b"":
+        decline = ""
+        catline = ""
+        for line in open(file, 'r', encoding='utf8').readlines():
+            if not empty.match(line) and line[0] != '#'[0]:
+                if decline == "":
                     logger.warning("Failed to find valid \Declare line "
                         "for layout file `%s'.\n\t=> Skipping this file!" % file)
                     nodeclaration = True
                 # A class, but no category declaration. Just break.
                 break
-            if declare.search(line) != None:
-                decline = b"\\TestDocClass{%s}{%s}" \
-                           % (classname.encode('ascii'), line[1:].strip())
+            if declare.match(line) != None:
+                decline = "\\TestDocClass{%s}{%s}" % (classname, line[1:].strip())
                 testclasses.append(decline)
-            elif category.search(line) != None:
-                catline = (b"\\DeclareCategory{%s}{%s}"
-                           % (classname.encode('ascii'),
-                              category.search(line).groups()[0]))
+            elif category.match(line) != None:
+                catline = ("\\DeclareCategory{%s}{%s}"
+                           % (classname, category.match(line).groups()[0]))
                 testclasses.append(catline)
-            if catline == b"" or decline == b"":
+            if catline == "" or decline == "":
                 continue
             break
         if nodeclaration:
             continue
     testclasses.sort()
-    cl = open('chklayouts.tex', 'wb')
+    cl = open('chklayouts.tex', 'w', encoding='utf8')
     for line in testclasses:
-        cl.write(line + b'\n')
+        cl.write(line + '\n')
     cl.close()
     #
     # we have chklayouts.tex, then process it
@@ -1432,7 +1438,7 @@ def checkLatexConfig(check_config, bool_docbook):
         line = latex_out.readline()
         if not line:
             break;
-        if re.match('^\+', line):
+        if line.startswith('+'):
             logger.info(line.strip())
     # if the command succeeds, None will be returned
     ret = latex_out.close()
@@ -1441,24 +1447,19 @@ def checkLatexConfig(check_config, bool_docbook):
     if rmcopy:
         removeFiles( [ 'chkconfig.ltx' ] )
     #
-    # currently, values in chkconfig are only used to set
-    # \font_encoding
-    values = {}
-    for line in open('chkconfig.vars').readlines():
-        key, val = re.sub('-', '_', line).split('=')
-        val = val.strip()
-        values[key] = val.strip("'")
-    # chk_fontenc may not exist
-    try:
-        addToRC(r'\font_encoding "%s"' % values["chk_fontenc"])
-    except:
-        pass
+    # values in chkconfig were only used to set
+    # \font_encoding, which is obsolete
+#    values = {}
+#    for line in open('chkconfig.vars').readlines():
+#        key, val = re.sub('-', '_', line).split('=')
+#        val = val.strip()
+#        values[key] = val.strip("'")
     # if configure successed, move textclass.lst.tmp to textclass.lst
     # and packages.lst.tmp to packages.lst
     if (os.path.isfile('textclass.lst.tmp')
-          and len(open('textclass.lst.tmp').read()) > 0
+          and len(open('textclass.lst.tmp', encoding='utf8').read()) > 0
         and os.path.isfile('packages.lst.tmp')
-          and len(open('packages.lst.tmp').read()) > 0):
+          and len(open('packages.lst.tmp', encoding='utf8').read()) > 0):
         shutil.move('textclass.lst.tmp', 'textclass.lst')
         shutil.move('packages.lst.tmp', 'packages.lst')
     return ret
@@ -1468,12 +1469,12 @@ def checkModulesConfig():
   removeFiles(['lyxmodules.lst', 'chkmodules.tex'])
 
   logger.info('+checking list of modules... ')
-  tx = open('lyxmodules.lst', 'w')
+  tx = open('lyxmodules.lst', 'w', encoding='utf8')
   tx.write('''## This file declares modules and their associated definition files.
 ## It has been automatically generated by configure
 ## Use "Options/Reconfigure" if you need to update it after a
 ## configuration change.
-## "ModuleName" "filename" "Description" "Packages" "Requires" "Excludes" "Category"
+## "ModuleName" "filename" "Description" "Packages" "Requires" "Excludes" "Category" "Local"
 ''')
 
   # build the list of available modules
@@ -1494,7 +1495,7 @@ def checkModulesConfig():
 
       seen.append(filename)
       retval = processModuleFile(file, filename, bool_docbook)
-      if retval != "":
+      if retval:
           tx.write(retval)
   tx.close()
   logger.info('\tdone')
@@ -1515,20 +1516,20 @@ def processModuleFile(file, filename, bool_docbook):
         We expect output:
           "ModuleName" "filename" "Description" "Packages" "Requires" "Excludes" "Category"
     '''
-    remods = re.compile(r'\DeclareLyXModule\s*(?:\[([^]]*?)\])?{(.*)}')
-    rereqs = re.compile(r'#+\s*Requires: (.*)')
-    reexcs = re.compile(r'#+\s*Excludes: (.*)')
-    recaty = re.compile(r'#+\s*Category: (.*)')
-    redbeg = re.compile(r'#+\s*DescriptionBegin\s*$')
-    redend = re.compile(r'#+\s*DescriptionEnd\s*$')
+    remods = re.compile('\s*#\s*\\\\DeclareLyXModule\s*(?:\[([^]]*?)\])?{(.*)}')
+    rereqs = re.compile('\s*#+\s*Requires: (.*)')
+    reexcs = re.compile('\s*#+\s*Excludes: (.*)')
+    recaty = re.compile('\\s*#\\s*\\\\DeclareCategory{(.*)}\\s*$')
+    redbeg = re.compile('\s*#+\s*DescriptionBegin\s*$')
+    redend = re.compile('\s*#+\s*DescriptionEnd\s*$')
 
     modname = desc = pkgs = req = excl = catgy = ""
     readingDescription = False
     descLines = []
 
-    for line in open(file).readlines():
+    for line in open(file, 'r', encoding='utf8').readlines():
       if readingDescription:
-        res = redend.search(line)
+        res = redend.match(line)
         if res != None:
           readingDescription = False
           desc = " ".join(descLines)
@@ -1537,11 +1538,11 @@ def processModuleFile(file, filename, bool_docbook):
           continue
         descLines.append(line[1:].strip())
         continue
-      res = redbeg.search(line)
+      res = redbeg.match(line)
       if res != None:
         readingDescription = True
         continue
-      res = remods.search(line)
+      res = remods.match(line)
       if res != None:
           (pkgs, modname) = res.groups()
           if pkgs == None:
@@ -1550,19 +1551,19 @@ def processModuleFile(file, filename, bool_docbook):
             tmp = [s.strip() for s in pkgs.split(",")]
             pkgs = ",".join(tmp)
           continue
-      res = rereqs.search(line)
+      res = rereqs.match(line)
       if res != None:
         req = res.group(1)
         tmp = [s.strip() for s in req.split("|")]
         req = "|".join(tmp)
         continue
-      res = reexcs.search(line)
+      res = reexcs.match(line)
       if res != None:
         excl = res.group(1)
         tmp = [s.strip() for s in excl.split("|")]
         excl = "|".join(tmp)
         continue
-      res = recaty.search(line)
+      res = recaty.match(line)
       if res != None:
         catgy = res.group(1)
         continue
@@ -1571,7 +1572,7 @@ def processModuleFile(file, filename, bool_docbook):
       logger.warning("Module file without \DeclareLyXModule line. ")
       return ""
 
-    if pkgs != "":
+    if pkgs:
         # this module has some latex dependencies:
         # append the dependencies to chkmodules.tex,
         # which is \input'ed by chkconfig.ltx
@@ -1582,21 +1583,24 @@ def processModuleFile(file, filename, bool_docbook):
                 continue
             if pkg.endswith(".sty"):
                 pkg = pkg[:-4]
-            testpackages.append("\\TestPackage{%s}" % (pkg,))
-        cm = open('chkmodules.tex', 'a')
+            testpackages.append("\\TestPackage{%s}" % pkg)
+        cm = open('chkmodules.tex', 'a', encoding='utf8')
         for line in testpackages:
             cm.write(line + '\n')
         cm.close()
 
-    return ('"%s" "%s" "%s" "%s" "%s" "%s" "%s"\n'
-            % (modname, filename, desc, pkgs, req, excl, catgy))
+    local = "true"
+    if (file.startswith(srcdir)):
+        local = "false"
+    return ('"%s" "%s" "%s" "%s" "%s" "%s" "%s" "%s"\n'
+            % (modname, filename, desc, pkgs, req, excl, catgy, local))
 
 
 def checkCiteEnginesConfig():
   removeFiles(['lyxciteengines.lst', 'chkciteengines.tex'])
 
   logger.info('+checking list of cite engines... ')
-  tx = open('lyxciteengines.lst', 'w')
+  tx = open('lyxciteengines.lst', 'w', encoding='utf8')
   tx.write('''## This file declares cite engines and their associated definition files.
 ## It has been automatically generated by configure
 ## Use "Options/Reconfigure" if you need to update it after a
@@ -1622,7 +1626,7 @@ def checkCiteEnginesConfig():
 
       seen.append(filename)
       retval = processCiteEngineFile(file, filename, bool_docbook)
-      if retval != "":
+      if retval:
           tx.write(retval)
   tx.close()
   logger.info('\tdone')
@@ -1639,20 +1643,20 @@ def processCiteEngineFile(file, filename, bool_docbook):
         We expect output:
           "CiteEngineName" "filename" "CiteEngineType" "CiteFramework" "DefaultBiblio" "Description" "Packages"
     '''
-    remods = re.compile(r'\DeclareLyXCiteEngine\s*(?:\[([^]]*?)\])?{(.*)}')
-    redbeg = re.compile(r'#+\s*DescriptionBegin\s*$')
-    redend = re.compile(r'#+\s*DescriptionEnd\s*$')
-    recet = re.compile(r'\s*CiteEngineType\s*(.*)')
-    redb = re.compile(r'\s*DefaultBiblio\s*(.*)')
-    resfm = re.compile(r'\s*CiteFramework\s*(.*)')
+    remods = re.compile('\s*#\s*\\\\DeclareLyXCiteEngine\s*(?:\[([^]]*?)\])?{(.*)}')
+    redbeg = re.compile('\s*#+\s*DescriptionBegin\s*$')
+    redend = re.compile('\s*#+\s*DescriptionEnd\s*$')
+    recet = re.compile('\s*CiteEngineType\s*(.*)')
+    redb = re.compile('\s*DefaultBiblio\s*(.*)')
+    resfm = re.compile('\s*CiteFramework\s*(.*)')
 
     modname = desc = pkgs = cet = db = cfm = ""
     readingDescription = False
     descLines = []
 
-    for line in open(file).readlines():
+    for line in open(file, 'r', encoding='utf8').readlines():
       if readingDescription:
-        res = redend.search(line)
+        res = redend.match(line)
         if res != None:
           readingDescription = False
           desc = " ".join(descLines)
@@ -1661,11 +1665,11 @@ def processCiteEngineFile(file, filename, bool_docbook):
           continue
         descLines.append(line[1:].strip())
         continue
-      res = redbeg.search(line)
+      res = redbeg.match(line)
       if res != None:
         readingDescription = True
         continue
-      res = remods.search(line)
+      res = remods.match(line)
       if res != None:
           (pkgs, modname) = res.groups()
           if pkgs == None:
@@ -1674,15 +1678,15 @@ def processCiteEngineFile(file, filename, bool_docbook):
             tmp = [s.strip() for s in pkgs.split(",")]
             pkgs = ",".join(tmp)
           continue
-      res = recet.search(line)
+      res = recet.match(line)
       if res != None:
         cet = res.group(1)
         continue
-      res = redb.search(line)
+      res = redb.match(line)
       if res != None:
         db = res.group(1)
         continue
-      res = resfm.search(line)
+      res = resfm.match(line)
       if res != None:
         cfm = res.group(1)
         continue
@@ -1691,7 +1695,7 @@ def processCiteEngineFile(file, filename, bool_docbook):
       logger.warning("Cite Engine File file without \DeclareLyXCiteEngine line. ")
       return ""
 
-    if pkgs != "":
+    if pkgs:
         # this cite engine has some latex dependencies:
         # append the dependencies to chkciteengines.tex,
         # which is \input'ed by chkconfig.ltx
@@ -1702,20 +1706,21 @@ def processCiteEngineFile(file, filename, bool_docbook):
                 continue
             if pkg.endswith(".sty"):
                 pkg = pkg[:-4]
-            testpackages.append("\\TestPackage{%s}" % (pkg,))
-        cm = open('chkciteengines.tex', 'a')
+            testpackages.append("\\TestPackage{%s}" % pkg)
+        cm = open('chkciteengines.tex', 'a', encoding='utf8')
         for line in testpackages:
             cm.write(line + '\n')
         cm.close()
 
-    return '"%s" "%s" "%s" "%s" "%s" "%s" "%s"\n' % (modname, filename, cet, cfm, db, desc, pkgs)
+    return ('"%s" "%s" "%s" "%s" "%s" "%s" "%s"\n'
+            % (modname, filename, cet, cfm, db, desc, pkgs))
 
 
 def checkXTemplates():
   removeFiles(['xtemplates.lst'])
 
   logger.info('+checking list of external templates... ')
-  tx = open('xtemplates.lst', 'w')
+  tx = open('xtemplates.lst', 'w', encoding='utf8')
   tx.write('''## This file lists external templates.
 ## It has been automatically generated by configure
 ## Use "Options/Reconfigure" if you need to update it after a
@@ -1738,7 +1743,7 @@ def checkXTemplates():
           continue
 
       seen.append(filename)
-      if filename != "":
+      if filename:
           tx.write(filename + "\n")
   tx.close()
   logger.info('\tdone')
@@ -1750,7 +1755,7 @@ def checkTeXAllowSpaces():
     if lyx_check_config:
         msg = "Checking whether TeX allows spaces in file names... "
         writeToFile('a b.tex', r'\message{working^^J}' )
-        if LATEX != '':
+        if LATEX:
             if os.name == 'nt' or sys.platform == 'cygwin':
                 latex_out = cmdOutput(LATEX + r""" "\nonstopmode\input{\"a b\"}\makeatletter\@@end" """)
             else:
@@ -1770,14 +1775,14 @@ def checkTeXAllowSpaces():
 def rescanTeXFiles():
     ''' Run kpsewhich to update information about TeX files '''
     logger.info("+Indexing TeX files... ")
-    if not os.path.isfile( os.path.join(srcdir, 'scripts', 'TeXFiles.py') ):
+    tfscript = os.path.join(srcdir, 'scripts', 'TeXFiles.py')
+    if not os.path.isfile(tfscript):
         logger.error("configure: error: cannot find TeXFiles.py script")
         sys.exit(1)
     interpreter = sys.executable
     if interpreter == '':
         interpreter = "python"
-    tfp = cmdOutput(interpreter + " -tt " + '"'
-                    + os.path.join(srcdir, 'scripts', 'TeXFiles.py') + '"')
+    tfp = cmdOutput('"%s" -tt "%s"' % (interpreter, tfscript))
     logger.info(tfp)
     logger.info("\tdone")
 
@@ -1794,7 +1799,7 @@ if __name__ == '__main__':
     lyx_check_config = True
     lyx_kpsewhich = True
     outfile = 'lyxrc.defaults'
-    lyxrc_fileformat = 24
+    lyxrc_fileformat = 29
     rc_entries = ''
     lyx_keep_temps = False
     version_suffix = ''
@@ -1854,18 +1859,18 @@ Format %i
     # check java and perl before any checkProg that may require them
     java = checkProg('a java interpreter', ['java'])[1]
     perl = checkProg('a perl interpreter', ['perl'])[1]
-    (inkscape_path, inkscape_name) = os.path.split(checkInkscape())
-    # On MacOSX, Inkscape requires full path file arguments. This
-    # is not needed on Linux and Win and even breaks the latter.
-    inkscape_fileprefix = ""
-    if sys.platform == 'darwin':
-        inkscape_fileprefix = "$$p"
+    (inkscape_path, inkscape_gui) = os.path.split(checkInkscape())
+    # On Windows, we need to call the "inkscape.com" wrapper
+    # for command line purposes. Other OSes do not differentiate.
+    inkscape_cl = inkscape_gui
+    if os.name == 'nt':
+        inkscape_cl = inkscape_gui.replace('.exe', '.com')
     checkFormatEntries(dtl_tools)
     checkConverterEntries()
     (chk_docbook, bool_docbook, docbook_cmd) = checkDocBook()
     checkTeXAllowSpaces()
     windows_style_tex_paths = checkTeXPaths()
-    if windows_style_tex_paths != '':
+    if windows_style_tex_paths:
         addToRC(r'\tex_expects_windows_paths %s' % windows_style_tex_paths)
     checkOtherEntries()
     if lyx_kpsewhich:
@@ -1874,7 +1879,7 @@ Format %i
     checkCiteEnginesConfig()
     checkXTemplates()
     # --without-latex-config can disable lyx_check_config
-    ret = checkLatexConfig(lyx_check_config and LATEX != '', bool_docbook)
+    ret = checkLatexConfig(lyx_check_config and LATEX, bool_docbook)
     removeTempFiles()
     # The return error code can be 256. Because most systems expect an error code
     # in the range 0-127, 256 can be interpretted as 'success'. Because we expect