#! /usr/bin/python3
-# -*- coding: utf-8 -*-
#
# file configure.py
# This file is part of LyX, the document processor.
# \author Bo Peng
# Full author contact details are available in file CREDITS.
-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
'''
if os.name == 'nt':
b = False
- if sys.version_info[0] < 3:
- cmd = 'cmd /d /c pushd ' + shortPath(os.getcwdu()) + '&' + cmd
- else:
- cmd = 'cmd /d /c pushd ' + shortPath(os.getcwd()) + '&' + cmd
+ cmd = 'cmd /d /c pushd ' + shortPath(os.getcwd()) + '&' + cmd
else:
b = True
pipe = subprocess.Popen(cmd, shell=b, close_fds=b, stdin=subprocess.PIPE,
names = os.listdir(src)
except os.error as oserror:
(errno, errstr) = oserror.args
- raise FileError("error listing files in '%s': %s" % (src, errstr))
+ raise FileError(f"error listing files in '{src}': {errstr}")
if not os.path.isdir(dst):
os.makedirs(dst)
link_dest = os.readlink(src_name)
os.symlink(link_dest, dst_name)
outputs.append(dst_name)
- elif level == 0 and name == 'cache':
- logger.info("Skip cache %s", src_name)
+ elif level == 0 and name in [ 'cache', 'configure.log', 'chkconfig.ltx' ]:
+ logger.info("Skip copy of %s", src_name)
elif os.path.isdir(src_name):
outputs.extend(
copy_tree(src_name, dst_name, preserve_symlinks, level=(level + 1)))
logger.info('Checking for upgrade from previous version.')
parent = os.path.dirname(cwd)
appname = basename[:(-len(version_suffix))]
- for version in ['-2.3', '-2.2', '-2.1', '-2.0', '-1.6' ]:
+ for version in ['-2.4', '-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)
fd, tmpfname = mkstemp(suffix='.ltx')
if os.name == 'nt':
encoding = sys.getfilesystemencoding()
- if sys.version_info[0] < 3:
- inpname = shortPath(unicode(tmpfname, encoding)).replace('\\', '/')
- else:
- inpname = shortPath(tmpfname).replace('\\', '/')
+ inpname = shortPath(tmpfname).replace('\\', '/')
else:
inpname = cmdOutput('cygpath -m ' + tmpfname)
logname = os.path.basename(re.sub("(?i).ltx", ".log", inpname))
""" Check for Java, don't give up as often as checkProg, using platform-dependent techniques """
if os.name == 'nt':
# Check in the registry.
- try: # Python 3.
- import winreg
- except ImportError: # Python 2.
- import _winreg as winreg
+ import winreg
potential_keys_64b = ["SOFTWARE\\JavaSoft\\Java Runtime Environment", "SOFTWARE\\JavaSoft\\Java Development Kit",
"SOFTWARE\\JavaSoft\\JDK", "SOFTWARE\\JavaSoft\\JRE"]
return ''
+def checkMacOSappInstalled(prog):
+ '''
+ Use metadata lookup to search for an "installed" macOS application bundle.
+ '''
+ if sys.platform == 'darwin' and len(prog) >= 1:
+ command = r'mdfind "kMDItemContentTypeTree == \"com.apple.application\"c && kMDItemFSName == \"%s\""' % prog
+ result = cmdOutput(command)
+ logger.debug(command + ": " + result)
+ return result != ''
+ return False
+
+
def checkProgAlternatives(description, progs, rc_entry=None,
alt_rc_entry=None, path=None, not_found=''):
'''
continue
msg = '+checking for "' + ac_word + '"... '
found_alt = False
+ if len(alt_rc_entry) >= 1 and ac_word.endswith('.app') and checkMacOSappInstalled(ac_word):
+ logger.info('+add alternative app ' + ac_word)
+ addToRC(alt_rc_entry[0].replace('%%', ac_word))
+ found_alt = True
for ac_dir in path:
if hasattr(os, "access") and not os.access(ac_dir, os.F_OK):
continue
def checkEditorNoRC(description, progs, rc_entry=None, path=None):
- ''' The same as checkViewer, but do not add rc entry '''
+ ''' The same as checkEditor, but do not add rc entry '''
if rc_entry is None:
rc_entry = []
if path is None:
return 'inkscape-binary'
elif os.name != 'nt':
return 'inkscape'
- if sys.version_info[0] < 3:
- import _winreg as winreg
- else:
- import winreg
+ import winreg
aReg = winreg.ConnectRegistry(None, winreg.HKEY_CLASSES_ROOT)
try:
aKey = winreg.OpenKey(aReg, r"inkscape.svg\DefaultIcon")
return valentry.split(',')[0]
else:
return 'inkscape'
- except EnvironmentError:
+ except OSError:
try:
aKey = winreg.OpenKey(aReg, r"inkscape.SVG\shell\open\command")
val = winreg.QueryValueEx(aKey, "")
return str(val[0]).split('"')[1]
- except EnvironmentError:
+ except OSError:
try:
aKey = winreg.OpenKey(aReg, r"Applications\inkscape.exe\shell\open\command")
val = winreg.QueryValueEx(aKey, "")
return str(val[0]).split('"')[1]
- except EnvironmentError:
+ except OSError:
return 'inkscape'
'xed', 'notepad', 'WinEdt', 'WinShell', 'PSPad']
def checkFormatEntries(dtl_tools):
- ''' Check all formats (\Format entries) '''
+ r''' Check all formats (\Format entries) '''
checkViewerEditor('a Tgif viewer and editor', ['tgif'],
rc_entry = [r'\Format tgif "obj, tgo" Tgif "" "%%" "%%" "vector" "application/x-tgif"'])
#
checkViewer('a PDF previewer',
['pdfview', 'kpdf', 'okular', 'qpdfview --unique',
'evince', 'xreader', 'kghostview', 'xpdf', 'SumatraPDF',
- 'acrobat', 'acroread', 'mupdf',
+ 'acrobat', 'acroread', 'mupdf', 'Skim.app',
'gv', 'ghostview', 'AcroRd32', 'gsview64', 'gsview32'],
rc_entry = [r'''\Format pdf pdf "PDF (ps2pdf)" P "%%" "" "document,vector,menu=export" ""
\Format pdf2 pdf "PDF (pdflatex)" F "%%" "" "document,vector,menu=export" ""
\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" ""
-\Format lyx23x 23.lyx "LyX 2.3.x" "" "" "" "document,menu=export" ""
+\Format lyx23x 23.lyx "LyX 2.3.x" "" "" "" "document" ""
+\Format lyx24x 24.lyx "LyX 2.4.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" ""
def checkConverterEntries():
- ''' Check all converters (\converter entries) '''
+ r''' Check all converters (\converter entries) '''
checkProg('the pdflatex program', ['pdflatex $$i'],
rc_entry = [ r'\converter pdflatex pdf2 "%%" "latex=pdflatex,hyperref-driver=pdftex"' ])
checkProg('a RTF -> HTML converter', ['unrtf --html $$i > $$o'],
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'],
+ checkProg('a PS to PDF converter', ['ps2pdf -dALLOWPSTRANSPARENCY $$i $$o'],
rc_entry = [ r'\converter ps pdf "%%" "hyperref-driver=dvips"' ])
#
checkProg('a PS to TXT converter', ['pstotext $$i > $$o'],
# Only define a converter from pdf6 for graphics
checkProg('a PDF to EPS converter', ['pdftops -eps -f 1 -l 1 $$i $$o'],
rc_entry = [ r'\converter pdf6 eps "%%" ""' ])
- # Define a converter from pdf6 to png for Macs where pdftops is missing.
- # The converter utility sips allows to force the dimensions of the resulting
- # png image. The value of 800 pixel for the width is arbitrary and not
- # related to the current screen resolution or width.
- # There is no converter parameter for this information.
- checkProg('a PDF to PNG converter',
- ['sips --resampleWidth 800 --setProperty format png $$i --out $$o'],
- rc_entry = [ r'\converter pdf6 png "%%" ""' ])
# Create one converter for a PDF produced using TeX fonts and one for a
# PDF produced using non-TeX fonts. This does not produce non-unique
# conversion paths, since a given document either uses TeX fonts or not.
checkProg('an EPS -> PDF converter', ['epstopdf'],
rc_entry = [ r'\converter eps pdf6 "epstopdf --outfile=$$o $$i" ""'])
#
- checkProg('an EPS -> PNG converter', ['magick $$i[0] $$o', 'convert $$i[0] $$o'],
- rc_entry = [ r'\converter eps png "%%" ""'])
+ #prepare for pdf -> png, 2nd part depends on IM ban below
+ pdftopng = ['sips --resampleWidth 800 --setProperty format png $$i --out $$o' ]
+ #
+ # Due to more restrictive policies, it is possible that (image)magick
+ # does not allow conversions from eps to png.
+ # So before setting the converter test it it on a mock file
+ _, cmd = checkProg('an EPS -> PNG converter', ['magick', 'convert'])
+ if cmd:
+ writeToFile('mock.eps', r'%!PS')
+ try:
+ subprocess.check_call([cmd, "mock.eps", "mock.png"])
+ removeFiles(['mock.eps', 'mock.png'])
+ rc_entry = r'\converter eps png "%s $$i[0] $$o" ""'
+ addToRC(rc_entry % cmd)
+ except:
+ removeFiles(['mock.eps'])
+ #needs empty record otherwise default converter will be issued
+ addToRC(r'''\converter eps png "" ""
+\converter png eps "" ""
+\converter jpg tiff "convert $$i $$o" ""
+\converter png tiff "convert $$i $$o" ""''')
+ logger.info('ImageMagick seems to ban conversions from EPS. Disabling direct EPS->PNG.')
+ pdftopng.append('pdftoppm -r 72 -png -singlefile $$i > $$o')
+ #
+ # PDF -> PNG: sips (mac), IM convert (windows, linux), pdftoppm (linux with IM ban)
+ # sips:Define a converter from pdf6 to png for Macs where pdftops is missing.
+ # The converter utility sips allows to force the dimensions of the resulting
+ # png image. The value of 800 pixel for the width is arbitrary and not
+ # related to the current screen resolution or width.
+ # There is no converter parameter for this information.
+ #
+ #pdftoppm: Some systems ban IM eps->png conversion. We will offer eps->pdf->png route instead.
+ checkProg('a PDF to PNG converter', pdftopng,
+ rc_entry = [ r'\converter pdf6 png "%%" ""' ])
+
#
# no agr -> pdf6 converter, since the pdf library used by gracebat is not
# free software and therefore not compiled in in many installations.
path, lilypond = checkProg('a LilyPond -> EPS/PDF/PNG converter', ['lilypond'])
if (lilypond):
version_string = cmdOutput("lilypond --version")
- match = re.match('GNU LilyPond (\S+)', version_string)
+ match = re.match(r'GNU LilyPond (\S+)', version_string)
if match:
version_number = match.groups()[0]
version = version_number.split('.')
if int(version[0]) > 2 or (len(version) > 1 and int(version[0]) == 2 and int(version[1]) >= 11):
- addToRC(r'''\converter lilypond eps "lilypond -dbackend=eps -dsafe --ps $$i" ""
-\converter lilypond png "lilypond -dbackend=eps -dsafe --png $$i" ""''')
- addToRC(r'\converter lilypond pdf6 "lilypond -dbackend=eps -dsafe --pdf $$i" ""')
+ addToRC(r'''\converter lilypond eps "lilypond -dbackend=eps --ps $$i" "needauth"
+\converter lilypond png "lilypond -dbackend=eps --png $$i" "needauth"''')
+ addToRC(r'\converter lilypond pdf6 "lilypond -dbackend=eps --pdf $$i" "needauth"')
logger.info('+ found LilyPond version %s.' % version_number)
elif int(version[0]) > 2 or (len(version) > 1 and int(version[0]) == 2 and int(version[1]) >= 6):
- addToRC(r'''\converter lilypond eps "lilypond -b eps --ps --safe $$i" ""
+ addToRC(r'''\converter lilypond eps "lilypond -b eps --ps $$i" "needauth"
\converter lilypond png "lilypond -b eps --png $$i" ""''')
if int(version[0]) > 2 or (len(version) > 1 and int(version[0]) == 2 and int(version[1]) >= 9):
- addToRC(r'\converter lilypond pdf6 "lilypond -b eps --pdf --safe $$i" ""')
+ addToRC(r'\converter lilypond pdf6 "lilypond -b eps --pdf $$i" "needauth"')
logger.info('+ found LilyPond version %s.' % version_number)
else:
logger.info('+ found LilyPond, but version %s is too old.' % version_number)
continue
found_lilypond_book = True
- match = re.match('(\S+)$', version_string)
+ match = re.match(r'(\S+)$', version_string)
if match:
version_number = match.groups()[0]
version = version_number.split('.')
# clicks View PDF after having done a View DVI. To circumvent
# this, use different output folders for eps and pdf outputs.
cmd = cmd.replace('"', r'\"')
- addToRC(r'\converter lilypond-book latex "' + cmd + ' --safe --lily-output-dir=ly-eps $$i" ""')
- addToRC(r'\converter lilypond-book pdflatex "' + cmd + ' --safe --pdf --latex-program=pdflatex --lily-output-dir=ly-pdf $$i" ""')
- addToRC(r'\converter lilypond-book-ja platex "' + cmd + ' --safe --pdf --latex-program=platex --lily-output-dir=ly-pdf $$i" ""')
- addToRC(r'\converter lilypond-book xetex "' + cmd + ' --safe --pdf --latex-program=xelatex --lily-output-dir=ly-pdf $$i" ""')
- addToRC(r'\converter lilypond-book luatex "' + cmd + ' --safe --pdf --latex-program=lualatex --lily-output-dir=ly-pdf $$i" ""')
- addToRC(r'\converter lilypond-book dviluatex "' + cmd + ' --safe --latex-program=dvilualatex --lily-output-dir=ly-eps $$i" ""')
+ addToRC(r'\converter lilypond-book latex "' + cmd + ' --lily-output-dir=ly-eps $$i" "needauth"')
+ addToRC(r'\converter lilypond-book pdflatex "' + cmd + ' --pdf --latex-program=pdflatex --lily-output-dir=ly-pdf $$i" "needauth"')
+ addToRC(r'\converter lilypond-book-ja platex "' + cmd + ' --pdf --latex-program=platex --lily-output-dir=ly-pdf $$i" "needauth"')
+ addToRC(r'\converter lilypond-book xetex "' + cmd + ' --pdf --latex-program=xelatex --lily-output-dir=ly-pdf $$i" "needauth"')
+ addToRC(r'\converter lilypond-book luatex "' + cmd + ' --pdf --latex-program=lualatex --lily-output-dir=ly-pdf $$i" "needauth"')
+ addToRC(r'\converter lilypond-book dviluatex "' + cmd + ' --latex-program=dvilualatex --lily-output-dir=ly-eps $$i" "needauth"')
# Also create the entry to apply LilyPond on DocBook files. However,
# command must be passed as argument, and it might already have
break
else:
logger.info('+ found LilyPond-book, but version %s is too old.' % version_number)
+ else:
+ logger.info('+ found LilyPond book, but version string does not match: %s' % version_string)
+
+ # If not on Windows, skip the check as argument to python.
+ if os.name != 'nt':
+ break
+
if not found_lilypond_book:
logger.info('+ found LilyPond-book, but could not extract version number.')
#
\converter lyx lyx21x "$${python} $$s/lyx2lyx/lyx2lyx -V 2.1 -o $$o $$i" ""
\converter lyx lyx22x "$${python} $$s/lyx2lyx/lyx2lyx -V 2.2 -o $$o $$i" ""
\converter lyx lyx23x "$${python} $$s/lyx2lyx/lyx2lyx -V 2.3 -o $$o $$i" ""
+\converter lyx lyx24x "$${python} $$s/lyx2lyx/lyx2lyx -V 2.4 -o $$o $$i" ""
\converter lyx clyx "$${python} $$s/lyx2lyx/lyx2lyx -V 1.4 -o $$o -c big5 $$i" ""
\converter lyx jlyx "$${python} $$s/lyx2lyx/lyx2lyx -V 1.4 -o $$o -c euc_jp $$i" ""
\converter lyx klyx "$${python} $$s/lyx2lyx/lyx2lyx -V 1.4 -o $$o -c euc_kr $$i" ""
return x.strip()
def processLayoutFile(file):
- """ process layout file and get a line of result
+ r""" process layout file and get a line of result
Declare lines look like this:
"""
classname = file.split(os.sep)[-1].split('.')[0]
# return ('[a,b]', 'a', ',b,c', 'article') for \DeclareLaTeXClass[a,b,c]{article}
- p = re.compile('\s*#\s*\\\\DeclareLaTeXClass\s*(\[([^,]*)(,.*)*])*\s*{(.*)}\s*$')
- q = re.compile('\s*#\s*\\\\DeclareCategory{(.*)}\s*$')
+ p = re.compile('\\s*#\\s*\\\\DeclareLaTeXClass\\s*(\\[([^,]*)(,.*)*])*\\s*{(.*)}\\s*$')
+ q = re.compile('\\s*#\\s*\\\\DeclareCategory{(.*)}\\s*$')
classdeclaration = ""
categorydeclaration = '""'
- for line in open(file, 'r', encoding='utf8').readlines():
+ for line in open(file, encoding='utf8').readlines():
res = p.match(line)
qres = q.match(line)
if res is not None:
# 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('\\s*#\\s*\\\\DeclareLaTeXClass\\s*(\[([^,]*)(,.*)*\])*\\s*{(.*)}\\s*$')
+ declare = re.compile('\\s*#\\s*\\\\DeclareLaTeXClass\\s*(\\[([^,]*)(,.*)*\\])*\\s*{(.*)}\\s*$')
category = re.compile('\\s*#\\s*\\\\DeclareCategory{(.*)}\\s*$')
empty = re.compile('\\s*$')
testclasses = list()
decline = ""
catline = ""
try:
- for line in open(file, 'r', encoding='utf8').readlines():
+ for line in open(file, encoding='utf8').readlines():
if not empty.match(line) and line[0] != '#'[0]:
if decline == "":
- logger.warning("Failed to find valid \Declare line "
+ logger.warning(r"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.match(line) is not None:
- decline = "\\TestDocClass{%s}{%s}" % (classname, line[1:].strip())
+ decline = f"\\TestDocClass{{{classname}}}{{{line[1:].strip()}}}"
testclasses.append(decline)
elif category.match(line) is not None:
catline = ("\\DeclareCategory{%s}{%s}"
def processModuleFile(file, filename):
- ''' process module file and get a line of result
+ r''' process module file and get a line of result
The top of a module file should look like this:
#\DeclareLyXModule[LaTeX Packages]{ModuleName}
We expect output:
"ModuleName" "filename" "Description" "Packages" "Requires" "Excludes" "Category"
'''
- remods = re.compile('\s*#\s*\\\\DeclareLyXModule\s*(?:\[([^]]*?)\])?{(.*)}')
- rereqs = re.compile('\s*#+\s*Requires: (.*)')
- reexcs = re.compile('\s*#+\s*Excludes: (.*)')
+ remods = re.compile('\\s*#\\s*\\\\DeclareLyXModule\\s*(?:\\[([^]]*?)\\])?{(.*)}')
+ rereqs = re.compile(r'\s*#+\s*Requires: (.*)')
+ reexcs = re.compile(r'\s*#+\s*Excludes: (.*)')
recaty = re.compile('\\s*#\\s*\\\\DeclareCategory{(.*)}\\s*$')
- redbeg = re.compile('\s*#+\s*DescriptionBegin\s*$')
- redend = re.compile('\s*#+\s*DescriptionEnd\s*$')
+ redbeg = re.compile(r'\s*#+\s*DescriptionBegin\s*$')
+ redend = re.compile(r'\s*#+\s*DescriptionEnd\s*$')
modname = desc = pkgs = req = excl = catgy = ""
readingDescription = False
descLines = []
- for line in open(file, 'r', encoding='utf8').readlines():
+ for line in open(file, encoding='utf8').readlines():
if readingDescription:
res = redend.match(line)
if res != None:
continue
if modname == "":
- logger.warning("Module file without \DeclareLyXModule line. ")
+ logger.warning(r"Module file without \DeclareLyXModule line. ")
return ""
if pkgs:
def processCiteEngineFile(file, filename):
- ''' process cite engines file and get a line of result
+ r''' process cite engines file and get a line of result
The top of a cite engine file should look like this:
#\DeclareLyXCiteEngine[LaTeX Packages]{CiteEngineName}
We expect output:
"CiteEngineName" "filename" "CiteEngineType" "CiteFramework" "DefaultBiblio" "Description" "Packages"
'''
- 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*(.*)')
+ remods = re.compile('\\s*#\\s*\\\\DeclareLyXCiteEngine\\s*(?:\\[([^]]*?)\\])?{(.*)}')
+ redbeg = re.compile(r'\s*#+\s*DescriptionBegin\s*$')
+ redend = re.compile(r'\s*#+\s*DescriptionEnd\s*$')
+ recet = re.compile(r'\s*CiteEngineType\s*(.*)')
+ redb = re.compile(r'\s*DefaultBiblio\s*(.*)')
+ resfm = re.compile(r'\s*CiteFramework\s*(.*)')
modname = desc = pkgs = cet = db = cfm = ""
readingDescription = False
descLines = []
- for line in open(file, 'r', encoding='utf8').readlines():
+ for line in open(file, encoding='utf8').readlines():
if readingDescription:
res = redend.match(line)
if res != None:
continue
if modname == "":
- logger.warning("Cite Engine File file without \DeclareLyXCiteEngine line. ")
+ logger.warning(r"Cite Engine File file without \DeclareLyXCiteEngine line. ")
return ""
if pkgs:
interpreter = sys.executable
if interpreter == '':
interpreter = "python"
- tfp = cmdOutput('"%s" -tt "%s"' % (interpreter, tfscript))
+ tfp = cmdOutput(f'"{interpreter}" "{tfscript}"')
logger.info(tfp)
logger.info("\tdone")
lyx_check_config = True
lyx_kpsewhich = True
outfile = 'lyxrc.defaults'
- lyxrc_fileformat = 36
+ lyxrc_fileformat = 38
rc_entries = ''
lyx_keep_temps = False
version_suffix = ''
lyx_binary_dir = ''
- logger.info("+Running LyX configure with Python %s.%s.%s", sys.version_info[0], sys.version_info[1], sys.version_info[2])
+ python_version = ".".join([str(n) for n in sys.version_info[:3]])
+ logger.info("+Running LyX configure with Python %s", python_version)
## Parse the command line
for op in sys.argv[1:]: # default shell/for list is $*, the options
if op in [ '-help', '--help', '-h' ]: