]> git.lyx.org Git - lyx.git/blob - lib/configure.py
On Windows, not all file systems provide for a short form of a filename,
[lyx.git] / lib / configure.py
1 #! /usr/bin/env python
2 # -*- coding: utf-8 -*-
3 #
4 # file configure.py
5 # This file is part of LyX, the document processor.
6 # Licence details can be found in the file COPYING.
7
8 # \author Bo Peng
9 # Full author contact details are available in file CREDITS.
10
11 import sys, os, re, shutil, glob, logging
12
13 # set up logging
14 logging.basicConfig(level = logging.DEBUG,
15     format = '%(levelname)s: %(message)s', # ignore application name
16     filename = 'configure.log',
17     filemode = 'w')
18 #
19 # Add a handler to log to console
20 console = logging.StreamHandler()
21 console.setLevel(logging.INFO) # the console only print out general information
22 formatter = logging.Formatter('%(message)s') # only print out the message itself
23 console.setFormatter(formatter)
24 logger = logging.getLogger('LyX')
25 logger.addHandler(console)
26
27 def writeToFile(filename, lines, append = False):
28     " utility function: write or append lines to filename "
29     if append:
30         file = open(filename, 'a')
31     else:
32         file = open(filename, 'w')
33     file.write(lines)
34     file.close()
35
36
37 def addToRC(lines):
38     ''' utility function: shortcut for appending lines to outfile
39         add newline at the end of lines.
40     '''
41     if lines.strip() != '':
42         writeToFile(outfile, lines + '\n', append = True)
43         logger.debug('Add to RC:\n' + lines + '\n\n')
44
45
46 def removeFiles(filenames):
47     '''utility function: 'rm -f'
48         ignore errors when file does not exist, or is a directory.
49     '''
50     for file in filenames:
51         try:
52             os.remove(file)
53             logger.debug('Removing file %s' % file)
54         except:
55             logger.debug('Failed to remove file %s' % file)
56             pass
57
58
59 def cmdOutput(cmd):
60     '''utility function: run a command and get its output as a string
61         cmd: command to run
62     '''
63     fout = os.popen(cmd)
64     output = fout.read()
65     fout.close()
66     return output.strip()
67
68
69 def setEnviron():
70     ''' I do not really know why this is useful, but we might as well keep it.
71         NLS nuisances.
72         Only set these to C if already set.  These must not be set unconditionally
73         because not all systems understand e.g. LANG=C (notably SCO).
74         Fixing LC_MESSAGES prevents Solaris sh from translating var values in set!
75         Non-C LC_CTYPE values break the ctype check.
76     '''
77     os.environ['LANG'] = os.getenv('LANG', 'C')
78     os.environ['LC'] = os.getenv('LC_ALL', 'C')
79     os.environ['LC_MESSAGE'] = os.getenv('LC_MESSAGE', 'C')
80     os.environ['LC_CTYPE'] = os.getenv('LC_CTYPE', 'C')
81
82
83 def createDirectories():
84     ''' Create the build directories if necessary '''
85     for dir in ['bind', 'clipart', 'doc', 'examples', 'images', 'kbd', \
86         'layouts', 'scripts', 'templates', 'ui' ]:
87         if not os.path.isdir( dir ):
88             try:
89                 os.mkdir( dir)
90                 logger.debug('Create directory %s.' % dir)
91             except:
92                 logger.error('Failed to create directory %s.' % dir)
93                 sys.exit(1)
94
95
96 def checkTeXPaths():
97     ''' Determine the path-style needed by the TeX engine on Win32 (Cygwin) '''
98     windows_style_tex_paths = ''
99     if LATEX == '':
100         return windows_style_tex_paths
101     if os.name == 'nt' or sys.platform == 'cygwin':
102         from tempfile import mkstemp
103         fd, tmpfname = mkstemp(suffix='.ltx')
104         if os.name == 'nt':
105             from ctypes import windll, create_unicode_buffer
106             GetShortPathName = windll.kernel32.GetShortPathNameW
107             longname = unicode(tmpfname)
108             shortname = create_unicode_buffer(len(longname)+1)
109             if GetShortPathName(longname, shortname, len(longname)+1):
110                 inpname = shortname.value.replace('\\', '/')
111             else:
112                 inpname = tmpfname.replace('\\', '/')
113         else:
114             inpname = cmdOutput('cygpath -m ' + tmpfname)
115         logname = os.path.basename(inpname.replace('.ltx', '.log'))
116         inpname = inpname.replace('~', '\\string~')
117         os.write(fd, r'\relax')
118         os.close(fd)
119         latex_out = cmdOutput(r'latex "\nonstopmode\input{%s}"' % inpname)
120         if 'Error' in latex_out:
121             latex_out = cmdOutput(r'latex "\nonstopmode\input{\"%s\"}"' % inpname)
122         if 'Error' in latex_out:
123             logger.warning("configure: TeX engine needs posix-style paths in latex files")
124             windows_style_tex_paths = 'false'
125         else:
126             logger.info("configure: TeX engine needs windows-style paths in latex files")
127             windows_style_tex_paths = 'true'
128         removeFiles([tmpfname, logname, 'texput.log'])
129     return windows_style_tex_paths
130
131
132 ## Searching some useful programs
133 def checkProg(description, progs, rc_entry = [], path = [], not_found = ''):
134     '''
135         This function will search a program in $PATH plus given path
136         If found, return directory and program name (not the options).
137
138         description: description of the program
139
140         progs: check programs, for each prog, the first word is used
141             for searching but the whole string is used to replace
142             %% for a rc_entry. So, feel free to add '$$i' etc for programs.
143
144         path: additional pathes
145
146         rc_entry: entry to outfile, can be
147             1. emtpy: no rc entry will be added
148             2. one pattern: %% will be replaced by the first found program,
149                 or '' if no program is found.
150             3. several patterns for each prog and not_found. This is used 
151                 when different programs have different usages. If you do not 
152                 want not_found entry to be added to the RC file, you can specify 
153                 an entry for each prog and use '' for the not_found entry.
154
155         not_found: the value that should be used instead of '' if no program
156             was found
157
158     '''
159     # one rc entry for each progs plus not_found entry
160     if len(rc_entry) > 1 and len(rc_entry) != len(progs) + 1:
161         logger.error("rc entry should have one item or item for each prog and not_found.")
162         sys.exit(2)
163     logger.info('checking for ' + description + '...')
164     ## print '(' + ','.join(progs) + ')',
165     for idx in range(len(progs)):
166         # ac_prog may have options, ac_word is the command name
167         ac_prog = progs[idx]
168         ac_word = ac_prog.split(' ')[0]
169         msg = '+checking for "' + ac_word + '"... '
170         path = os.environ["PATH"].split(os.pathsep) + path
171         extlist = ['']
172         if "PATHEXT" in os.environ:
173             extlist = extlist + os.environ["PATHEXT"].split(os.pathsep)
174         for ac_dir in path:
175             for ext in extlist:
176                 if os.path.isfile( os.path.join(ac_dir, ac_word + ext) ):
177                     logger.info(msg + ' yes')
178                     # write rc entries for this command
179                     if len(rc_entry) == 1:
180                         addToRC(rc_entry[0].replace('%%', ac_prog))
181                     elif len(rc_entry) > 1:
182                         addToRC(rc_entry[idx].replace('%%', ac_prog))
183                     return [ac_dir, ac_word]
184         # if not successful
185         logger.info(msg + ' no')
186     # write rc entries for 'not found'
187     if len(rc_entry) > 0:  # the last one.
188         addToRC(rc_entry[-1].replace('%%', not_found))
189     return ['', not_found]
190
191
192 def checkProgAlternatives(description, progs, rc_entry = [], alt_rc_entry = [], path = [], not_found = ''):
193     ''' 
194         The same as checkProg, but additionally, all found programs will be added
195         as alt_rc_entries
196     '''
197     # one rc entry for each progs plus not_found entry
198     if len(rc_entry) > 1 and len(rc_entry) != len(progs) + 1:
199         logger.error("rc entry should have one item or item for each prog and not_found.")
200         sys.exit(2)
201     logger.info('checking for ' + description + '...')
202     ## print '(' + ','.join(progs) + ')',
203     found_prime = False
204     real_ac_dir = ''
205     real_ac_word = not_found
206     for idx in range(len(progs)):
207         # ac_prog may have options, ac_word is the command name
208         ac_prog = progs[idx]
209         ac_word = ac_prog.split(' ')[0]
210         msg = '+checking for "' + ac_word + '"... '
211         path = os.environ["PATH"].split(os.pathsep) + path
212         extlist = ['']
213         if "PATHEXT" in os.environ:
214             extlist = extlist + os.environ["PATHEXT"].split(os.pathsep)
215         found_alt = False
216         for ac_dir in path:
217             for ext in extlist:
218                 if os.path.isfile( os.path.join(ac_dir, ac_word + ext) ):
219                     logger.info(msg + ' yes')
220                     pr = re.compile(r'(\\\S+)(.*)$')
221                     m = None
222                     # write rc entries for this command
223                     if found_prime == False:
224                         if len(rc_entry) == 1:
225                             addToRC(rc_entry[0].replace('%%', ac_prog))
226                         elif len(rc_entry) > 1:
227                             addToRC(rc_entry[idx].replace('%%', ac_prog))
228                         real_ac_dir = ac_dir
229                         real_ac_word = ac_word
230                         found_prime = True
231                     if len(alt_rc_entry) == 1:
232                         alt_rc = alt_rc_entry[0]
233                         if alt_rc == "":
234                             # if no explicit alt_rc is given, construct one
235                             m = pr.match(rc_entry[0])
236                             if m:
237                                 alt_rc = m.group(1) + "_alternatives" + m.group(2)
238                         addToRC(alt_rc.replace('%%', ac_prog))
239                     elif len(alt_rc_entry) > 1:
240                         alt_rc = alt_rc_entry[idx]
241                         if alt_rc == "":
242                             # if no explicit alt_rc is given, construct one
243                             m = pr.match(rc_entry[idx])
244                             if m:
245                                 alt_rc = m.group(1) + "_alternatives" + m.group(2)
246                         addToRC(alt_rc.replace('%%', ac_prog))
247                     found_alt = True
248                     break
249             if found_alt:
250                 break
251         if found_alt == False:
252             # if not successful
253             logger.info(msg + ' no')
254     if found_prime:
255         return [real_ac_dir, real_ac_word]
256     # write rc entries for 'not found'
257     if len(rc_entry) > 0:  # the last one.
258         addToRC(rc_entry[-1].replace('%%', not_found))
259     return ['', not_found]
260
261
262 def addViewerAlternatives(rcs):
263     r = re.compile(r'\\Format (\S+).*$')
264     m = None
265     alt = ''
266     for idxx in range(len(rcs)):
267         if len(rcs) == 1:
268             m = r.match(rcs[0])
269             if m:
270                 alt = r'\viewer_alternatives ' + m.group(1) + " %%"
271         elif len(rcs) > 1:
272             m = r.match(rcs[idxx])
273             if m:
274                 if idxx > 0:
275                     alt += '\n'
276                 alt += r'\viewer_alternatives ' + m.group(1) + " %%"
277     return alt
278
279
280 def addEditorAlternatives(rcs):
281     r = re.compile(r'\\Format (\S+).*$')
282     m = None
283     alt = ''
284     for idxx in range(len(rcs)):
285         if len(rcs) == 1:
286             m = r.match(rcs[0])
287             if m:
288                 alt = r'\editor_alternatives ' + m.group(1) + " %%"
289         elif len(rcs) > 1:
290             m = r.match(rcs[idxx])
291             if m:
292                 if idxx > 0:
293                     alt += '\n'
294                 alt += r'\editor_alternatives ' + m.group(1) + " %%"
295     return alt
296
297
298 def checkViewer(description, progs, rc_entry = [], path = []):
299     ''' The same as checkProgAlternatives, but for viewers '''
300     if len(rc_entry) > 1 and len(rc_entry) != len(progs) + 1:
301         logger.error("rc entry should have one item or item for each prog and not_found.")
302         sys.exit(2)
303     alt_rc_entry = []
304     for idx in range(len(progs)):
305         if len(rc_entry) == 1:
306             rcs = rc_entry[0].split('\n')
307             alt = addViewerAlternatives(rcs)
308             alt_rc_entry.insert(0, alt)
309         elif len(rc_entry) > 1:
310             rcs = rc_entry[idx].split('\n')
311             alt = addViewerAlternatives(rcs)
312             alt_rc_entry.insert(idx, alt)
313     return checkProgAlternatives(description, progs, rc_entry, alt_rc_entry, path, not_found = 'auto')
314
315
316 def checkEditor(description, progs, rc_entry = [], path = []):
317     ''' The same as checkProgAlternatives, but for editors '''
318     if len(rc_entry) > 1 and len(rc_entry) != len(progs) + 1:
319         logger.error("rc entry should have one item or item for each prog and not_found.")
320         sys.exit(2)
321     alt_rc_entry = []
322     for idx in range(len(progs)):
323         if len(rc_entry) == 1:
324             rcs = rc_entry[0].split('\n')
325             alt = addEditorAlternatives(rcs)
326             alt_rc_entry.insert(0, alt)
327         elif len(rc_entry) > 1:
328             rcs = rc_entry[idx].split('\n')
329             alt = addEditorAlternatives(rcs)
330             alt_rc_entry.insert(idx, alt)
331     return checkProgAlternatives(description, progs, rc_entry, alt_rc_entry, path, not_found = 'auto')
332
333
334 def checkViewerNoRC(description, progs, rc_entry = [], path = []):
335     ''' The same as checkViewer, but do not add rc entry '''
336     if len(rc_entry) > 1 and len(rc_entry) != len(progs) + 1:
337         logger.error("rc entry should have one item or item for each prog and not_found.")
338         sys.exit(2)
339     alt_rc_entry = []
340     for idx in range(len(progs)):
341         if len(rc_entry) == 1:
342             rcs = rc_entry[0].split('\n')
343             alt = addViewerAlternatives(rcs)
344             alt_rc_entry.insert(0, alt)
345         elif len(rc_entry) > 1:
346             rcs = rc_entry[idx].split('\n')
347             alt = addViewerAlternatives(rcs)
348             alt_rc_entry.insert(idx, alt)
349     rc_entry = []
350     return checkProgAlternatives(description, progs, rc_entry, alt_rc_entry, path, not_found = 'auto')
351
352
353 def checkEditorNoRC(description, progs, rc_entry = [], path = []):
354     ''' The same as checkViewer, but do not add rc entry '''
355     if len(rc_entry) > 1 and len(rc_entry) != len(progs) + 1:
356         logger.error("rc entry should have one item or item for each prog and not_found.")
357         sys.exit(2)
358     alt_rc_entry = []
359     for idx in range(len(progs)):
360         if len(rc_entry) == 1:
361             rcs = rc_entry[0].split('\n')
362             alt = addEditorAlternatives(rcs)
363             alt_rc_entry.insert(0, alt)
364         elif len(rc_entry) > 1:
365             rcs = rc_entry[idx].split('\n')
366             alt = addEditorAlternatives(rcs)
367             alt_rc_entry.insert(idx, alt)
368     rc_entry = []
369     return checkProgAlternatives(description, progs, rc_entry, alt_rc_entry, path, not_found = 'auto')
370
371
372 def checkViewerEditor(description, progs, rc_entry = [], path = []):
373     ''' The same as checkProgAlternatives, but for viewers and editors '''
374     checkEditorNoRC(description, progs, rc_entry, path)
375     return checkViewer(description, progs, rc_entry, path)
376
377
378 def checkDTLtools():
379     ''' Check whether DTL tools are available (Windows only) '''
380     # Find programs! Returned path is not used now
381     if ((os.name == 'nt' or sys.platform == 'cygwin') and
382             checkProg('DVI to DTL converter', ['dv2dt']) != ['', ''] and
383             checkProg('DTL to DVI converter', ['dt2dv']) != ['', '']):
384         dtl_tools = True
385     else:
386         dtl_tools = False
387     return dtl_tools
388
389
390 def checkLatex(dtl_tools):
391     ''' Check latex, return lyx_check_config '''
392     path, LATEX = checkProg('a Latex2e program', ['latex $$i', 'latex2e $$i'])
393     path, PPLATEX = checkProg('a DVI postprocessing program', ['pplatex $$i'])
394     #-----------------------------------------------------------------
395     path, PLATEX = checkProg('pLaTeX, the Japanese LaTeX', ['platex $$i'])
396     if PLATEX != '':
397         # check if PLATEX is pLaTeX2e
398         writeToFile('chklatex.ltx', '''
399 \\nonstopmode
400 \\@@end
401 ''')
402         # run platex on chklatex.ltx and check result
403         if cmdOutput(PLATEX + ' chklatex.ltx').find('pLaTeX2e') != -1:
404             # We have the Japanese pLaTeX2e
405             addToRC(r'\converter platex   dvi       "%s"   "latex=platex"' % PLATEX)
406         else:
407             PLATEX = ''
408             removeFiles(['chklatex.ltx', 'chklatex.log'])
409     #-----------------------------------------------------------------
410     # use LATEX to convert from latex to dvi if PPLATEX is not available    
411     if PPLATEX == '':
412         PPLATEX = LATEX
413     if dtl_tools:
414         # Windows only: DraftDVI
415         addToRC(r'''\converter latex      dvi2       "%s"       "latex"
416 \converter dvi2       dvi        "python -tt $$s/scripts/clean_dvi.py $$i $$o"  ""''' % PPLATEX)
417     else:
418         addToRC(r'\converter latex      dvi        "%s" "latex"' % PPLATEX)
419     # no latex
420     if LATEX != '':
421         # Check if latex is usable
422         writeToFile('chklatex.ltx', '''
423 \\nonstopmode\\makeatletter
424 \\ifx\\undefined\\documentclass\\else
425   \\message{ThisIsLaTeX2e}
426 \\fi
427 \\@@end
428 ''')
429         # run latex on chklatex.ltx and check result
430         if cmdOutput(LATEX + ' chklatex.ltx').find('ThisIsLaTeX2e') != -1:
431             # valid latex2e
432             return LATEX
433         else:
434             logger.warning("Latex not usable (not LaTeX2e) ")
435         # remove temporary files
436         removeFiles(['chklatex.ltx', 'chklatex.log'])
437     return ''
438
439
440 def checkLuatex():
441     ''' Check if luatex is there and usable '''
442     path, LUATEX = checkProg('LuaTeX', ['lualatex $$i'])
443     path, DVILUATEX = checkProg('LuaTeX (DVI)', ['dvilualatex $$i'])
444     if LUATEX != '':
445     # luatex binary is there
446         msg = "checking if LuaTeX is usable ..."
447         # Check if luatex is usable
448         writeToFile('luatest.tex', '''
449 \\nonstopmode\\documentclass{minimal}
450 \\usepackage{fontspec}
451 \\begin{document}
452 .
453 \\end{document}
454 ''')
455         # run lualatex on luatest.tex and check result
456         luatest = cmdOutput(LUATEX + ' luatest.tex')
457         if luatest.find('XeTeX is required to compile this document') != -1:
458             # fontspec/luatex too old! We do not support this version.
459             logger.info(msg + ' no (probably not recent enough)')
460         elif luatest.find('! LaTeX Error: File `fontspec.sty\' not found') != -1:
461             # fontspec missing
462             logger.info(msg + ' no (missing fontspec)')
463         else:
464             # working luatex
465             logger.info(msg + ' yes')
466             addToRC(r'\converter luatex      pdf5       "%s"    "latex=lualatex"' % LUATEX)
467             if DVILUATEX != '':
468                 addToRC(r'\converter luatex      dvi3        "%s"       "latex=lualatex"' % DVILUATEX)   
469         # remove temporary files
470         removeFiles(['luatest.tex', 'luatest.log', 'luatest.aux', 'luatest.pdf'])
471
472
473 def checkModule(module):
474     ''' Check for a Python module, return the status '''
475     msg = 'checking for "' + module + ' module"... '
476     try:
477       __import__(module)
478       logger.info(msg + ' yes')
479       return True
480     except ImportError:
481       logger.info(msg + ' no')
482       return False
483
484
485 def checkFormatEntries(dtl_tools):  
486     ''' Check all formats (\Format entries) '''
487     checkViewerEditor('a Tgif viewer and editor', ['tgif'],
488         rc_entry = [r'\Format tgif       obj     Tgif                   "" "%%" "%%"    "vector"'])
489     #
490     checkViewerEditor('a FIG viewer and editor', ['xfig', 'jfig3-itext.jar', 'jfig3.jar'],
491         rc_entry = [r'\Format fig        fig     FIG                    "" "%%" "%%"    "vector"'])
492     #
493     checkViewerEditor('a Dia viewer and editor', ['dia'],
494         rc_entry = [r'\Format dia        dia     DIA                    "" "%%" "%%"    "vector"'])
495     #
496     checkViewerEditor('a Grace viewer and editor', ['xmgrace'],
497         rc_entry = [r'\Format agr        agr     Grace                  "" "%%" "%%"    "vector"'])
498     #
499     checkViewerEditor('a FEN viewer and editor', ['xboard -lpf $$i -mode EditPosition'],
500         rc_entry = [r'\Format fen        fen     FEN                    "" "%%" "%%"    ""'])
501     #
502     checkViewerEditor('a SVG viewer and editor', ['inkscape'],
503         rc_entry = [r'\Format svg        svg     SVG                    "" "%%" "%%"    "vector"'])
504     #
505     path, iv = checkViewerNoRC('a raster image viewer', ['xv', 'kview', 'gimp-remote', 'gimp'],
506         rc_entry = [r'''\Format bmp        bmp     BMP                    "" "%s"       "%s"    ""
507 \Format gif        gif     GIF                    "" "%s"       "%s"    ""
508 \Format jpg        jpg     JPEG                   "" "%s"       "%s"    ""
509 \Format pbm        pbm     PBM                    "" "%s"       "%s"    ""
510 \Format pgm        pgm     PGM                    "" "%s"       "%s"    ""
511 \Format png        png     PNG                    "" "%s"       "%s"    ""
512 \Format ppm        ppm     PPM                    "" "%s"       "%s"    ""
513 \Format tiff       tif     TIFF                   "" "%s"       "%s"    ""
514 \Format xbm        xbm     XBM                    "" "%s"       "%s"    ""
515 \Format xpm        xpm     XPM                    "" "%s"       "%s"    ""'''])
516     path, ie = checkEditorNoRC('a raster image editor', ['gimp-remote', 'gimp'],
517         rc_entry = [r'''\Format bmp        bmp     BMP                    "" "%s"       "%s"    ""
518 \Format gif        gif     GIF                    "" "%s"       "%s"    ""
519 \Format jpg        jpg     JPEG                   "" "%s"       "%s"    ""
520 \Format pbm        pbm     PBM                    "" "%s"       "%s"    ""
521 \Format pgm        pgm     PGM                    "" "%s"       "%s"    ""
522 \Format png        png     PNG                    "" "%s"       "%s"    ""
523 \Format ppm        ppm     PPM                    "" "%s"       "%s"    ""
524 \Format tiff       tif     TIFF                   "" "%s"       "%s"    ""
525 \Format xbm        xbm     XBM                    "" "%s"       "%s"    ""
526 \Format xpm        xpm     XPM                    "" "%s"       "%s"    ""'''])
527     addToRC(r'''\Format bmp        bmp     BMP                    "" "%s"       "%s"    ""
528 \Format gif        gif     GIF                    "" "%s"       "%s"    ""
529 \Format jpg        jpg     JPEG                   "" "%s"       "%s"    ""
530 \Format pbm        pbm     PBM                    "" "%s"       "%s"    ""
531 \Format pgm        pgm     PGM                    "" "%s"       "%s"    ""
532 \Format png        png     PNG                    "" "%s"       "%s"    ""
533 \Format ppm        ppm     PPM                    "" "%s"       "%s"    ""
534 \Format tiff       tif     TIFF                   "" "%s"       "%s"    ""
535 \Format xbm        xbm     XBM                    "" "%s"       "%s"    ""
536 \Format xpm        xpm     XPM                    "" "%s"       "%s"    ""''' % \
537         (iv, ie, iv, ie, iv, ie, iv, ie, iv, ie, iv, ie, iv, ie, iv, ie, iv, ie, iv, ie) )
538     #
539     checkViewerEditor('a text editor', ['xemacs', 'gvim', 'kedit', 'kwrite', 'kate', \
540         'nedit', 'gedit', 'notepad'],
541         rc_entry = [r'''\Format asciichess asc    "Plain text (chess output)"  "" ""    "%%"    ""
542 \Format asciiimage asc    "Plain text (image)"         "" ""    "%%"    ""
543 \Format asciixfig  asc    "Plain text (Xfig output)"   "" ""    "%%"    ""
544 \Format dateout    tmp    "date (output)"         "" "" "%%"    ""
545 \Format docbook    sgml    DocBook                B  "" "%%"    "document,menu=export"
546 \Format docbook-xml xml   "Docbook (XML)"         "" "" "%%"    "document,menu=export"
547 \Format dot        dot    "Graphviz Dot"          "" "" "%%"    "vector"
548 \Format platex     tex    "LaTeX (pLaTeX)"        "" "" "%%"    "document,menu=export"
549 \Format literate   nw      NoWeb                  N  "" "%%"    "document,menu=export"
550 \Format sweave     Rnw    "Sweave"                S  "" "%%"    "document,menu=export"
551 \Format lilypond   ly     "LilyPond music"        "" "" "%%"    "vector"
552 \Format lilypond-book    lytex "LilyPond book (LaTeX)"   "" ""  "%%"    "document,menu=export"
553 \Format latex      tex    "LaTeX (plain)"         L  "" "%%"    "document,menu=export"
554 \Format luatex     tex    "LaTeX (LuaTeX)"        "" "" "%%"    "document,menu=export"
555 \Format pdflatex   tex    "LaTeX (pdflatex)"      "" "" "%%"    "document,menu=export"
556 \Format xetex      tex    "LaTeX (XeTeX)"         "" "" "%%"    "document,menu=export"
557 \Format text       txt    "Plain text"            a  "" "%%"    "document,menu=export"
558 \Format text2      txt    "Plain text (pstotext)" "" "" "%%"    "document"
559 \Format text3      txt    "Plain text (ps2ascii)" "" "" "%%"    "document"
560 \Format text4      txt    "Plain text (catdvi)"   "" "" "%%"    "document"
561 \Format textparagraph txt "Plain Text, Join Lines" "" ""        "%%"    "document"''' ])
562    #Spreadsheets using ssconvert from gnumeric
563     checkViewer('gnumeric spreadsheet software', ['gnumeric'], 
564       rc_entry = [r'''\Format gnumeric gnumeric "Gnumeric spreadsheet" "" ""    "%%"   "document" 
565 \Format excel      xls    "Excel spreadsheet"      "" "" "%%"    "document" 
566 \Format oocalc     ods    "OpenOffice spreadsheet" "" "" "%%"    "document"''']) 
567  #
568     path, xhtmlview = checkViewer('an HTML previewer', ['firefox', 'mozilla file://$$p$$i', 'netscape'],
569         rc_entry = [r'\Format xhtml      xhtml   "LyXHTML"              y "%%" ""    "document,menu=export"'])
570     if xhtmlview == "":
571         addToRC(r'\Format xhtml      xhtml   "LyXHTML"              y "" ""  "document,menu=export"')
572  #
573     checkEditor('a BibTeX editor', ['jabref', 'JabRef', \
574         'pybliographic', 'bibdesk', 'gbib', 'kbib', \
575         'kbibtex', 'sixpack', 'bibedit', 'tkbibtex' \
576         'xemacs', 'gvim', 'kedit', 'kwrite', 'kate', \
577         'nedit', 'gedit', 'notepad'],
578         rc_entry = [r'''\Format bibtex bib    "BibTeX"         "" ""    "%%"    ""''' ])
579     #
580     #checkProg('a Postscript interpreter', ['gs'],
581     #  rc_entry = [ r'\ps_command "%%"' ])
582     checkViewer('a Postscript previewer', ['kghostview', 'okular', 'evince', 'gv', 'ghostview -swap'],
583         rc_entry = [r'''\Format eps        eps     EPS                    "" "%%"       ""      "vector"
584 \Format ps         ps      Postscript             t  "%%"       ""      "document,vector"'''])
585     # for xdg-open issues look here: http://www.mail-archive.com/lyx-devel@lists.lyx.org/msg151818.html
586     checkViewer('a PDF previewer', ['kpdf', 'okular', 'evince', 'kghostview', 'xpdf', 'acrobat', 'acroread', \
587                     'gv', 'ghostview'],
588         rc_entry = [r'''\Format pdf        pdf    "PDF (ps2pdf)"          P  "%%"       ""      "document,vector,menu=export"
589 \Format pdf2       pdf    "PDF (pdflatex)"        F  "%%"       ""      "document,vector,menu=export"
590 \Format pdf3       pdf    "PDF (dvipdfm)"         m  "%%"       ""      "document,vector,menu=export"
591 \Format pdf4       pdf    "PDF (XeTeX)"           X  "%%"       ""      "document,vector,menu=export"
592 \Format pdf5       pdf    "PDF (LuaTeX)"          u  "%%"       ""      "document,vector,menu=export"'''])
593     #
594     checkViewer('a DVI previewer', ['xdvi', 'kdvi', 'okular', 'yap', 'dviout -Set=!m'],
595         rc_entry = [r'''\Format dvi        dvi     DVI                    D  "%%"       ""      "document,vector,menu=export"
596 \Format dvi3       dvi     "DVI (LuaTeX)"          V  "%%"      ""      "document,vector,menu=export"'''])
597     if dtl_tools:
598         # Windows only: DraftDVI
599         addToRC(r'\Format dvi2       dvi     DraftDVI               ""  ""      ""      "vector"')
600     #
601     checkViewer('an HTML previewer', ['firefox', 'mozilla file://$$p$$i', 'netscape'],
602         rc_entry = [r'\Format html       html    HTML                   H  "%%" ""      "document,menu=export"'])
603     #
604     checkViewerEditor('Noteedit', ['noteedit'],
605         rc_entry = [r'\Format noteedit   not     Noteedit               "" "%%" "%%"    "vector"'])
606     #
607     checkViewerEditor('an OpenDocument/OpenOffice viewer', ['swriter', 'oowriter', 'abiword'],
608         rc_entry = [r'''\Format odt        odt     OpenDocument           "" "%%"       "%%"    "document,vector,menu=export"
609 \Format sxw        sxw    "OpenOffice.Org (sxw)"  "" "" ""      "document,vector"'''])
610     # 
611     checkViewerEditor('a Rich Text and Word viewer', ['swriter', 'oowriter', 'abiword'],
612         rc_entry = [r'''\Format rtf        rtf    "Rich Text Format"      "" "%%"       "%%"    "document,vector,menu=export"
613 \Format word       doc    "MS Word"               W  "%%"       "%%"    "document,vector,menu=export"'''])
614     #
615     # entries that do not need checkProg
616     addToRC(r'''\Format date       ""     "date command"          "" "" ""      ""
617 \Format csv        csv    "Table (CSV)"  "" ""  ""      "document"
618 \Format fax        ""      Fax                    "" "" ""      "document"
619 \Format lyx        lyx     LyX                    "" "" ""      ""
620 \Format lyx13x     13.lyx  "LyX 1.3.x"             "" ""        ""      "document"
621 \Format lyx14x     14.lyx  "LyX 1.4.x"             "" ""        ""      "document"
622 \Format lyx15x     15.lyx  "LyX 1.5.x"             "" ""        ""      "document"
623 \Format lyx16x     16.lyx  "LyX 1.6.x"             "" ""        ""      "document,menu=export"
624 \Format clyx       cjklyx "CJK LyX 1.4.x (big5)"  "" "" ""      "document"
625 \Format jlyx       cjklyx "CJK LyX 1.4.x (euc-jp)" "" ""        ""      "document"
626 \Format klyx       cjklyx "CJK LyX 1.4.x (euc-kr)" "" ""        ""      "document"
627 \Format lyxpreview lyxpreview "LyX Preview"       "" "" ""      ""
628 \Format lyxpreview-lytex  lyxpreview-lytex  "LyX Preview (LilyPond book)" "" "" ""      ""
629 \Format lyxpreview-platex lyxpreview-platex "LyX Preview (pLaTeX)"       "" ""  ""      ""
630 \Format pdftex     pdftex_t PDFTEX                "" "" ""      ""
631 \Format program    ""      Program                "" "" ""      ""
632 \Format pstex      pstex_t PSTEX                  "" "" ""      ""
633 \Format wmf        wmf    "Windows Metafile"      "" "" ""      "vector"
634 \Format emf        emf    "Enhanced Metafile"     "" "" ""      "vector"
635 \Format wordhtml   html   "HTML (MS Word)"        "" "" ""      "document"
636 ''')
637
638
639 def checkConverterEntries():
640     ''' Check all converters (\converter entries) '''
641     checkProg('the pdflatex program', ['pdflatex $$i'],
642         rc_entry = [ r'\converter pdflatex   pdf2       "%%"    "latex=pdflatex"' ])
643
644     checkProg('XeTeX', ['xelatex $$i'],
645         rc_entry = [ r'\converter xetex      pdf4       "%%"    "latex=xelatex"' ])
646
647     checkLuatex()
648     
649     ''' If we're running LyX in-place then tex2lyx will be found in
650             ../src/tex2lyx. Add this directory to the PATH temporarily and
651             search for tex2lyx.
652             Use PATH to avoid any problems with paths-with-spaces.
653     '''
654     path_orig = os.environ["PATH"]
655     os.environ["PATH"] = os.path.join('..', 'src', 'tex2lyx') + \
656         os.pathsep + path_orig
657
658 # First search for tex2lyx with version suffix (bug 6986)
659     checkProg('a LaTeX/Noweb -> LyX converter', ['tex2lyx' + version_suffix, 'tex2lyx'],
660         rc_entry = [r'''\converter latex      lyx        "%% -f $$i $$o"        ""
661 \converter literate   lyx        "%% -n -f $$i $$o"     ""'''])
662
663     os.environ["PATH"] = path_orig
664
665     #
666     checkProg('a Noweb -> LaTeX converter', ['noweave -delay -index $$i > $$o'],
667         rc_entry = [r'''\converter literate   latex      "%%"   ""
668 \converter literate   pdflatex      "%%"        ""'''])
669     #
670     checkProg('a Sweave -> LaTeX converter', ['Rscript --verbose --no-save --no-restore $$s/scripts/lyxsweave.R $$p$$i $$p$$o $$e $$r'],
671         rc_entry = [r'''\converter sweave   latex      "%%"     ""
672 \converter sweave   pdflatex   "%%"     ""
673 \converter sweave   xetex      "%%"     ""
674 \converter sweave   luatex     "%%"     ""'''])
675     #
676     checkProg('an HTML -> LaTeX converter', ['html2latex $$i', 'gnuhtml2latex $$i', \
677         'htmltolatex -input $$i -output $$o', 'java -jar htmltolatex.jar -input $$i -output $$o'],
678         rc_entry = [ r'\converter html       latex      "%%"    ""' ])
679     #
680     checkProg('an MS Word -> LaTeX converter', ['wvCleanLatex $$i $$o'],
681         rc_entry = [ r'\converter word       latex      "%%"    ""' ])
682
683     # eLyXer: search as an executable (elyxer.py, elyxer)
684     path, elyxer = checkProg('a LyX -> HTML converter',
685         ['elyxer.py --directory $$r $$i $$o', 'elyxer --directory $$r $$i $$o'],
686         rc_entry = [ r'\converter lyx      html       "%%"      ""' ])
687     path, elyxer = checkProg('a LyX -> HTML (MS Word) converter',
688         ['elyxer.py --html --directory $$r $$i $$o', 'elyxer --html --directory $$r $$i $$o'],
689         rc_entry = [ r'\converter lyx      wordhtml       "%%"  ""' ])
690     if elyxer.find('elyxer') >= 0:
691       addToRC(r'''\copier    html       "python -tt $$s/scripts/ext_copy.py -e html,png,jpg,jpeg,css $$i $$o"''')
692       addToRC(r'''\copier    wordhtml       "python -tt $$s/scripts/ext_copy.py -e html,png,jpg,jpeg,css $$i $$o"''')
693     else:
694       # search for HTML converters other than eLyXer
695       # On SuSE the scripts have a .sh suffix, and on debian they are in /usr/share/tex4ht/
696       path, htmlconv = checkProg('a LaTeX -> HTML converter', ['htlatex $$i', 'htlatex.sh $$i', \
697           '/usr/share/tex4ht/htlatex $$i', 'tth  -t -e2 -L$$b < $$i > $$o', \
698           'latex2html -no_subdir -split 0 -show_section_numbers $$i', 'hevea -s $$i'],
699           rc_entry = [ r'\converter latex      html       "%%"  "needaux"' ])
700       if htmlconv.find('htlatex') >= 0 or htmlconv == 'latex2html':
701         addToRC(r'''\copier    html       "python -tt $$s/scripts/ext_copy.py -e html,png,css $$i $$o"''')
702       else:
703         addToRC(r'''\copier    html       "python -tt $$s/scripts/ext_copy.py $$i $$o"''')
704       path, htmlconv = checkProg('a LaTeX -> HTML (MS Word) converter', ["htlatex $$i 'html,word' 'symbol/!' '-cvalidate'", \
705           "htlatex.sh $$i 'html,word' 'symbol/!' '-cvalidate'", \
706           "/usr/share/tex4ht/htlatex $$i 'html,word' 'symbol/!' '-cvalidate'"],
707           rc_entry = [ r'\converter latex      wordhtml   "%%"  "needaux"' ])
708       if htmlconv.find('htlatex') >= 0:
709         addToRC(r'''\copier    wordhtml       "python -tt $$s/scripts/ext_copy.py -e html,png,css $$i $$o"''')
710       else:
711         addToRC(r'''\copier    wordhtml       "python -tt $$s/scripts/ext_copy.py $$i $$o"''')
712
713
714     # Check if LyXBlogger is installed
715     lyxblogger_found = checkModule('lyxblogger')
716     if lyxblogger_found:
717       addToRC(r'\Format    blog       blog       "LyXBlogger"           "" "" ""  "document"')
718       addToRC(r'\converter xhtml      blog       "python -m lyxblogger $$i"       ""')
719
720     #
721     checkProg('an OpenOffice.org -> LaTeX converter', ['w2l -clean $$i'],
722         rc_entry = [ r'\converter sxw        latex      "%%"    ""' ])
723     #
724     checkProg('an OpenDocument -> LaTeX converter', ['w2l -clean $$i'],
725         rc_entry = [ r'\converter odt        latex      "%%"    ""' ])
726     # According to http://www.tug.org/applications/tex4ht/mn-commands.html
727     # the command mk4ht oolatex $$i has to be used as default,
728     # but as this would require to have Perl installed, in MiKTeX oolatex is
729     # directly available as application.
730     # On SuSE the scripts have a .sh suffix, and on debian they are in /usr/share/tex4ht/
731     # Both SuSE and debian have oolatex
732     checkProg('a LaTeX -> Open Document converter', [
733         'oolatex $$i', 'mk4ht oolatex $$i', 'oolatex.sh $$i', '/usr/share/tex4ht/oolatex $$i',
734         'htlatex $$i \'xhtml,ooffice\' \'ooffice/! -cmozhtf\' \'-coo\' \'-cvalidate\''],
735         rc_entry = [ r'\converter latex      odt        "%%"    "needaux"' ])
736     # On windows it is called latex2rt.exe
737     checkProg('a LaTeX -> RTF converter', ['latex2rtf -p -S -o $$o $$i', 'latex2rt -p -S -o $$o $$i'],
738         rc_entry = [ r'\converter latex      rtf        "%%"    "needaux"' ])
739     #
740     checkProg('a RTF -> HTML converter', ['unrtf --html  $$i > $$o'],
741         rc_entry = [ r'\converter rtf      html        "%%"     ""' ])
742     #
743     checkProg('a PS to PDF converter', ['ps2pdf13 $$i $$o'],
744         rc_entry = [ r'\converter ps         pdf        "%%"    ""' ])
745     #
746     checkProg('a PS to TXT converter', ['pstotext $$i > $$o'],
747         rc_entry = [ r'\converter ps         text2      "%%"    ""' ])
748     #
749     checkProg('a PS to TXT converter', ['ps2ascii $$i $$o'],
750         rc_entry = [ r'\converter ps         text3      "%%"    ""' ])
751     #
752     checkProg('a PS to EPS converter', ['ps2eps $$i'],
753         rc_entry = [ r'\converter ps         eps      "%%"      ""' ])
754     #
755     checkProg('a PDF to PS converter', ['pdf2ps $$i $$o', 'pdftops $$i $$o'],
756         rc_entry = [ r'\converter pdf         ps        "%%"    ""' ])
757     #
758     checkProg('a PDF to EPS converter', ['pdftops -eps -f 1 -l 1 $$i $$o'],
759         rc_entry = [ r'\converter pdf         eps        "%%"   ""' ])
760     #
761     checkProg('a DVI to TXT converter', ['catdvi $$i > $$o'],
762         rc_entry = [ r'\converter dvi        text4      "%%"    ""' ])
763     #
764     checkProg('a DVI to PS converter', ['dvips -o $$o $$i'],
765         rc_entry = [ r'\converter dvi        ps         "%%"    ""' ])
766     #
767     checkProg('a DVI to PDF converter', ['dvipdfmx -o $$o $$i', 'dvipdfm -o $$o $$i'],
768         rc_entry = [ r'\converter dvi        pdf3       "%%"    ""' ])
769     #
770     path, dvipng = checkProg('dvipng', ['dvipng'])
771     path, dv2dt  = checkProg('DVI to DTL converter', ['dv2dt'])
772     if dvipng == "dvipng" and dv2dt == 'dv2dt':
773         addToRC(r'\converter lyxpreview png        "python -tt $$s/scripts/lyxpreview2bitmap.py"        ""')
774     else:
775         # set empty converter to override the default imagemagick
776         addToRC(r'\converter lyxpreview png        ""   ""')
777     if dv2dt == 'dv2dt':
778         addToRC(r'\converter lyxpreview ppm        "python -tt $$s/scripts/lyxpreview2bitmap.py"        ""')
779     else:
780         # set empty converter to override the default imagemagick
781         addToRC(r'\converter lyxpreview ppm        ""   ""')
782     #  
783     checkProg('a fax program', ['kdeprintfax $$i', 'ksendfax $$i', 'hylapex $$i'],
784         rc_entry = [ r'\converter ps         fax        "%%"    ""'])
785     #
786     path, fig2dev = checkProg('a FIG -> Image converter', ['fig2dev'])
787     if fig2dev == "fig2dev":
788         addToRC(r'''\converter fig        eps        "fig2dev -L eps $$i $$o"   ""
789 \converter fig        ppm        "fig2dev -L ppm $$i $$o"       ""
790 \converter fig        png        "fig2dev -L png $$i $$o"       ""
791 \converter fig        pdftex     "python -tt $$s/scripts/fig2pdftex.py $$i $$o" ""
792 \converter fig        pstex      "python -tt $$s/scripts/fig2pstex.py $$i $$o"  ""''')
793     #
794     checkProg('a TIFF -> PS converter', ['tiff2ps $$i > $$o'],
795         rc_entry = [ r'\converter tiff       eps        "%%"    ""', ''])
796     #
797     checkProg('a TGIF -> EPS/PPM converter', ['tgif'],
798         rc_entry = [
799             r'''\converter tgif       eps        "tgif -print -color -eps -stdout $$i > $$o"    ""
800 \converter tgif       png        "tgif -print -color -png -o $$d $$i"   ""
801 \converter tgif       pdf        "tgif -print -color -pdf -stdout $$i > $$o"    ""''',
802             ''])
803     #
804     checkProg('a WMF -> EPS converter', ['metafile2eps $$i $$o', 'wmf2eps -o $$o $$i'],
805         rc_entry = [ r'\converter wmf        eps        "%%"    ""'])
806     #
807     checkProg('an EMF -> EPS converter', ['metafile2eps $$i $$o', 'wmf2eps -o $$o $$i'],
808         rc_entry = [ r'\converter emf        eps        "%%"    ""'])
809     #
810     checkProg('an EPS -> PDF converter', ['epstopdf'],
811         rc_entry = [ r'\converter eps        pdf        "epstopdf --outfile=$$o $$i"    ""', ''])
812     #
813     # no agr -> pdf converter, since the pdf library used by gracebat is not
814     # free software and therefore not compiled in in many installations.
815     # Fortunately, this is not a big problem, because we will use epstopdf to
816     # convert from agr to pdf via eps without loss of quality.
817     checkProg('a Grace -> Image converter', ['gracebat'],
818         rc_entry = [
819             r'''\converter agr        eps        "gracebat -hardcopy -printfile $$o -hdevice EPS $$i 2>/dev/null"       ""
820 \converter agr        png        "gracebat -hardcopy -printfile $$o -hdevice PNG $$i 2>/dev/null"       ""
821 \converter agr        jpg        "gracebat -hardcopy -printfile $$o -hdevice JPEG $$i 2>/dev/null"      ""
822 \converter agr        ppm        "gracebat -hardcopy -printfile $$o -hdevice PNM $$i 2>/dev/null"       ""''',
823             ''])
824     #
825     checkProg('a Dot -> Image converter', ['dot'],
826         rc_entry = [
827             r'''\converter dot        eps        "dot -Teps $$i -o $$o" ""
828 \converter dot        png        "dot -Tpng $$i -o $$o" ""''',
829             ''])
830     #
831     checkProg('a Dia -> PNG converter', ['dia -e $$o -t png $$i'],
832         rc_entry = [ r'\converter dia        png        "%%"    ""'])
833     #
834     checkProg('a Dia -> EPS converter', ['dia -e $$o -t eps $$i'],
835         rc_entry = [ r'\converter dia        eps        "%%"    ""'])
836     #
837     checkProg('a SVG -> PDF converter', ['rsvg-convert -f pdf -o $$o $$i', 'inkscape --file=$$p/$$i --export-area-drawing --without-gui --export-pdf=$$p/$$o'],
838         rc_entry = [ r'\converter svg        pdf        "%%"    ""'])
839     #
840     checkProg('a SVG -> EPS converter', ['rsvg-convert -f ps -o $$o $$i', 'inkscape --file=$$p/$$i --export-area-drawing --without-gui --export-eps=$$p/$$o'],
841         rc_entry = [ r'\converter svg        eps        "%%"    ""'])
842     # the PNG export via Inkscape must not have the full path ($$p) for the file
843     checkProg('a SVG -> PNG converter', ['rsvg-convert -f png -o $$o $$i', 'inkscape --without-gui --file=$$i --export-png=$$o'],
844         rc_entry = [ r'\converter svg        png        "%%"    ""'])
845     
846     #
847     # gnumeric/xls/ods to tex
848     checkProg('a spreadsheet -> latex converter', ['ssconvert'], 
849        rc_entry = [ r'''\converter gnumeric latex "ssconvert --export-type=Gnumeric_html:latex $$i $$o" "" 
850 \converter ods latex "ssconvert --export-type=Gnumeric_html:latex $$i $$o" "" 
851 \converter xls latex "ssconvert --export-type=Gnumeric_html:latex $$i $$o" ""''', 
852 '']) 
853
854     path, lilypond = checkProg('a LilyPond -> EPS/PDF/PNG converter', ['lilypond'])
855     if (lilypond != ''):
856         version_string = cmdOutput("lilypond --version")
857         match = re.match('GNU LilyPond (\S+)', version_string)
858         if match:
859             version_number = match.groups()[0]
860             version = version_number.split('.')
861             if int(version[0]) > 2 or (len(version) > 1 and int(version[0]) == 2 and int(version[1]) >= 11):
862                 addToRC(r'''\converter lilypond   eps        "lilypond -dbackend=eps -dsafe --ps $$i"   ""
863 \converter lilypond   png        "lilypond -dbackend=eps -dsafe --png $$i"      ""''')
864                 addToRC(r'\converter lilypond   pdf        "lilypond -dbackend=eps -dsafe --pdf $$i"    ""')
865                 logger.info('+  found LilyPond version %s.' % version_number)
866             elif int(version[0]) > 2 or (len(version) > 1 and int(version[0]) == 2 and int(version[1]) >= 6):
867                 addToRC(r'''\converter lilypond   eps        "lilypond -b eps --ps --safe $$i"  ""
868 \converter lilypond   png        "lilypond -b eps --png $$i"    ""''')
869                 if int(version[0]) > 2 or (len(version) > 1 and int(version[0]) == 2 and int(version[1]) >= 9):
870                     addToRC(r'\converter lilypond   pdf        "lilypond -b eps --pdf --safe $$i"       ""')
871                 logger.info('+  found LilyPond version %s.' % version_number)
872             else:
873                 logger.info('+  found LilyPond, but version %s is too old.' % version_number)
874         else:
875             logger.info('+  found LilyPond, but could not extract version number.')
876     #
877     path, lilypond_book = checkProg('a LilyPond book (LaTeX) -> LaTeX converter', ['lilypond-book'])
878     if (lilypond_book != ''):
879         version_string = cmdOutput("lilypond-book --version")
880         match = re.match('^(\S+)$', version_string)
881         if match:
882             version_number = match.groups()[0]
883             version = version_number.split('.')
884             if int(version[0]) > 2 or (len(version) > 1 and int(version[0]) == 2 and int(version[1]) >= 13):
885                 if dv2dt == 'dv2dt':
886                     addToRC(r'\converter lyxpreview-lytex ppm "python -tt $$s/scripts/lyxpreview-lytex2bitmap.py" ""')
887                 else:
888                     # set empty converter to override the default imagemagick
889                     addToRC(r'\converter lyxpreview-lytex ppm "" ""')
890                 if dvipng == "dvipng" and dv2dt == 'dv2dt':
891                     addToRC(r'\converter lyxpreview-lytex png "python -tt $$s/scripts/lyxpreview-lytex2bitmap.py" ""')
892                 else:
893                     # set empty converter to override the default imagemagick
894                     addToRC(r'\converter lyxpreview-lytex png "" ""')
895                 # Note: The --lily-output-dir flag is required because lilypond-book
896                 #       does not process input again unless the input has changed,
897                 #       even if the output format being requested is different. So
898                 #       once a .eps file exists, lilypond-book won't create a .pdf
899                 #       even when requested with --pdf. This is a problem if a user
900                 #       clicks View PDF after having done a View DVI. To circumvent
901                 #       this, use different output folders for eps and pdf outputs.
902                 addToRC(r'\converter lilypond-book latex    "lilypond-book --safe --lily-output-dir=ly-eps $$i"                                ""')
903                 addToRC(r'\converter lilypond-book pdflatex "lilypond-book --safe --pdf --latex-program=pdflatex --lily-output-dir=ly-pdf $$i" ""')
904                 addToRC(r'\converter lilypond-book xetex    "lilypond-book --safe --pdf --latex-program=xelatex --lily-output-dir=ly-pdf $$i"  ""')
905                 addToRC(r'\converter lilypond-book luatex   "lilypond-book --safe --pdf --latex-program=lualatex --lily-output-dir=ly-pdf $$i" ""')
906                 logger.info('+  found LilyPond-book version %s.' % version_number)
907             else:
908                 logger.info('+  found LilyPond-book, but version %s is too old.' % version_number)
909         else:
910             logger.info('+  found LilyPond-book, but could not extract version number.')
911     #
912     checkProg('a Noteedit -> LilyPond converter', ['noteedit --export-lilypond $$i'],
913         rc_entry = [ r'\converter noteedit   lilypond   "%%"    ""', ''])
914     #
915     # Currently, lyxpak outputs a gzip compressed tar archive on *nix
916     # and a zip archive on Windows.
917     # So, we configure the appropriate version according to the platform.
918     cmd = r'\converter lyx %s "python -tt $$s/scripts/lyxpak.py $$r/$$i" ""'
919     if os.name == 'nt':
920         addToRC(r'\Format lyxzip     zip    "LyX Archive (zip)"     "" "" ""  "document,menu=export"')
921         addToRC(cmd % "lyxzip")
922     else:
923         addToRC(r'\Format lyxgz      gz     "LyX Archive (tar.gz)"  "" "" ""  "document,menu=export"')
924         addToRC(cmd % "lyxgz")
925         
926     #
927     # FIXME: no rc_entry? comment it out
928     # checkProg('Image converter', ['convert $$i $$o'])
929     #
930     # Entries that do not need checkProg
931     addToRC(r'''\converter lyxpreview-platex ppm        "python -tt $$s/scripts/lyxpreview-platex2bitmap.py"    ""
932 \converter csv        lyx        "python -tt $$s/scripts/csv2lyx.py $$i $$o"    ""
933 \converter date       dateout    "python -tt $$s/scripts/date.py %d-%m-%Y > $$o"        ""
934 \converter docbook    docbook-xml "cp $$i $$o"  "xml"
935 \converter fen        asciichess "python -tt $$s/scripts/fen2ascii.py $$i $$o"  ""
936 \converter lyx        lyx13x     "python -tt $$s/lyx2lyx/lyx2lyx -t 221 $$i > $$o"      ""
937 \converter lyx        lyx14x     "python -tt $$s/lyx2lyx/lyx2lyx -t 245 $$i > $$o"      ""
938 \converter lyx        lyx15x     "python -tt $$s/lyx2lyx/lyx2lyx -t 276 $$i > $$o"      ""
939 \converter lyx        lyx16x     "python -tt $$s/lyx2lyx/lyx2lyx -t 345 $$i > $$o"      ""
940 \converter lyx        clyx       "python -tt $$s/lyx2lyx/lyx2lyx -c big5 -t 245 $$i > $$o"      ""
941 \converter lyx        jlyx       "python -tt $$s/lyx2lyx/lyx2lyx -c euc_jp -t 245 $$i > $$o"    ""
942 \converter lyx        klyx       "python -tt $$s/lyx2lyx/lyx2lyx -c euc_kr -t 245 $$i > $$o"    ""
943 \converter clyx       lyx        "python -tt $$s/lyx2lyx/lyx2lyx -c big5 $$i > $$o"     ""
944 \converter jlyx       lyx        "python -tt $$s/lyx2lyx/lyx2lyx -c euc_jp $$i > $$o"   ""
945 \converter klyx       lyx        "python -tt $$s/lyx2lyx/lyx2lyx -c euc_kr $$i > $$o"   ""
946 ''')
947
948
949 def checkDocBook():
950     ''' Check docbook '''
951     path, DOCBOOK = checkProg('SGML-tools 2.x (DocBook), db2x scripts or xsltproc', ['sgmltools', 'db2dvi', 'xsltproc'],
952         rc_entry = [
953             r'''\converter docbook    dvi        "sgmltools -b dvi $$i" ""
954 \converter docbook    html       "sgmltools -b html $$i"        ""''',
955             r'''\converter docbook    dvi        "db2dvi $$i"   ""
956 \converter docbook    html       "db2html $$i"  ""''',
957             r'''\converter docbook    dvi        ""     ""
958 \converter docbook    html       "" ""''',
959             r'''\converter docbook    dvi        ""     ""
960 \converter docbook    html       ""     ""'''])
961     #
962     if DOCBOOK != '':
963         return ('yes', 'true', '\\def\\hasdocbook{yes}')
964     else:
965         return ('no', 'false', '')
966
967
968 def checkOtherEntries():
969     ''' entries other than Format and Converter '''
970     checkProg('ChkTeX', ['chktex -n1 -n3 -n6 -n9 -n22 -n25 -n30 -n38'],
971         rc_entry = [ r'\chktex_command "%%"' ])
972     checkProgAlternatives('BibTeX or alternative programs', ['bibtex', 'bibtex8', 'biber'],
973         rc_entry = [ r'\bibtex_command "%%"' ],
974         alt_rc_entry = [ r'\bibtex_alternatives "%%"' ])
975     checkProg('a specific Japanese BibTeX variant', ['pbibtex', 'jbibtex', 'bibtex'],
976         rc_entry = [ r'\jbibtex_command "%%"' ])
977     checkProgAlternatives('available index processors', ['texindy', 'makeindex -c -q', 'xindy'],
978         rc_entry = [ r'\index_command "%%"' ],
979         alt_rc_entry = [ r'\index_alternatives "%%"' ])
980     checkProg('an index processor appropriate to Japanese', ['mendex -c -q', 'jmakeindex -c -q', 'makeindex -c -q'],
981         rc_entry = [ r'\jindex_command "%%"' ])
982     path, splitindex = checkProg('the splitindex processor', ['splitindex.pl', 'splitindex'],
983         rc_entry = [ r'\splitindex_command "%%"' ])
984     if splitindex == '':
985         checkProg('the splitindex processor (java version)', ['splitindex.class'],
986             rc_entry = [ r'\splitindex_command "java splitindex"' ])
987     checkProg('a nomenclature processor', ['makeindex'],
988         rc_entry = [ r'\nomencl_command "makeindex -s nomencl.ist"' ])
989     ## FIXME: OCTAVE is not used anywhere
990     # path, OCTAVE = checkProg('Octave', ['octave'])
991     ## FIXME: MAPLE is not used anywhere
992     # path, MAPLE = checkProg('Maple', ['maple'])
993     checkProg('a spool command', ['lp', 'lpr'],
994         rc_entry = [
995             r'''\print_spool_printerprefix "-d "
996 \print_spool_command "lp"''',
997             r'''\print_spool_printerprefix "-P",
998 \print_spool_command "lpr"''',
999             ''])
1000     # Add the rest of the entries (no checkProg is required)
1001     addToRC(r'''\copier    fig        "python -tt $$s/scripts/fig_copy.py $$i $$o"
1002 \copier    pstex      "python -tt $$s/scripts/tex_copy.py $$i $$o $$l"
1003 \copier    pdftex     "python -tt $$s/scripts/tex_copy.py $$i $$o $$l"
1004 \copier    program    "python -tt $$s/scripts/ext_copy.py $$i $$o"
1005 ''')
1006
1007
1008 def processLayoutFile(file, bool_docbook):
1009     ''' process layout file and get a line of result
1010         
1011         Declare lines look like this: (article.layout, scrbook.layout, svjog.layout)
1012         
1013         \DeclareLaTeXClass{article}
1014         \DeclareLaTeXClass[scrbook]{book (koma-script)}
1015         \DeclareLaTeXClass[svjour,svjog.clo]{article (Springer - svjour/jog)}
1016
1017         we expect output:
1018         
1019         "article" "article" "article" "false" "article.cls"
1020         "scrbook" "scrbook" "book (koma-script)" "false" "scrbook.cls"
1021         "svjog" "svjour" "article (Springer - svjour/jog)" "false" "svjour.cls,svjog.clo"
1022     '''
1023     def checkForClassExtension(x):
1024         '''if the extension for a latex class is not
1025            provided, add .cls to the classname'''
1026         if not '.' in x:
1027             return x.strip() + '.cls'
1028         else:
1029             return x.strip()
1030     classname = file.split(os.sep)[-1].split('.')[0]
1031     # return ('LaTeX', '[a,b]', 'a', ',b,c', 'article') for \DeclareLaTeXClass[a,b,c]{article}
1032     p = re.compile(r'\Declare(LaTeX|DocBook)Class\s*(\[([^,]*)(,.*)*\])*\s*{(.*)}')
1033     for line in open(file).readlines():
1034         res = p.search(line)
1035         if res != None:
1036             (classtype, optAll, opt, opt1, desc) = res.groups()
1037             avai = {'LaTeX':'false', 'DocBook':bool_docbook}[classtype]
1038             if opt == None:
1039                 opt = classname
1040                 prereq_latex = checkForClassExtension(classname)
1041             else:
1042                 prereq_list = optAll[1:-1].split(',')
1043                 prereq_list = map(checkForClassExtension, prereq_list)
1044                 prereq_latex = ','.join(prereq_list)
1045             prereq_docbook = {'true':'', 'false':'docbook'}[bool_docbook]
1046             prereq = {'LaTeX':prereq_latex, 'DocBook':prereq_docbook}[classtype]
1047             return '"%s" "%s" "%s" "%s" "%s"\n' % (classname, opt, desc, avai, prereq)
1048     logger.warning("Layout file " + file + " has no \DeclareXXClass line. ")
1049     return ""
1050
1051
1052 def checkLatexConfig(check_config, bool_docbook):
1053     ''' Explore the LaTeX configuration 
1054         Return None (will be passed to sys.exit()) for success.
1055     '''
1056     msg = 'checking LaTeX configuration... '
1057     # if --without-latex-config is forced, or if there is no previous 
1058     # version of textclass.lst, re-generate a default file.
1059     if not os.path.isfile('textclass.lst') or not check_config:
1060         # remove the files only if we want to regenerate
1061         removeFiles(['textclass.lst', 'packages.lst'])
1062         #
1063         # Then, generate a default textclass.lst. In case configure.py
1064         # fails, we still have something to start lyx.
1065         logger.info(msg + ' default values')
1066         logger.info('+checking list of textclasses... ')
1067         tx = open('textclass.lst', 'w')
1068         tx.write('''
1069 # This file declares layouts and their associated definition files
1070 # (include dir. relative to the place where this file is).
1071 # It contains only default values, since chkconfig.ltx could not be run
1072 # for some reason. Run ./configure.py if you need to update it after a
1073 # configuration change.
1074 ''')
1075         # build the list of available layout files and convert it to commands
1076         # for chkconfig.ltx
1077         foundClasses = []
1078         for file in glob.glob( os.path.join('layouts', '*.layout') ) + \
1079             glob.glob( os.path.join(srcdir, 'layouts', '*.layout' ) ) :
1080             # valid file?
1081             if not os.path.isfile(file): 
1082                 continue
1083             # get stuff between /xxxx.layout .
1084             classname = file.split(os.sep)[-1].split('.')[0]
1085             #  tr ' -' '__'`
1086             cleanclass = classname.replace(' ', '_')
1087             cleanclass = cleanclass.replace('-', '_')
1088             # make sure the same class is not considered twice
1089             if foundClasses.count(cleanclass) == 0: # not found before
1090                 foundClasses.append(cleanclass)
1091                 retval = processLayoutFile(file, bool_docbook)
1092                 if retval != "":
1093                     tx.write(retval)
1094         tx.close()
1095         logger.info('\tdone')
1096     if not check_config:
1097         return None
1098     # the following will generate textclass.lst.tmp, and packages.lst.tmp
1099     logger.info(msg + '\tauto')
1100     removeFiles(['wrap_chkconfig.ltx', 'chkconfig.vars', \
1101         'chkconfig.classes', 'chklayouts.tex'])
1102     rmcopy = False
1103     if not os.path.isfile( 'chkconfig.ltx' ):
1104         shutil.copyfile( os.path.join(srcdir, 'chkconfig.ltx'), 'chkconfig.ltx' )
1105         rmcopy = True
1106     writeToFile('wrap_chkconfig.ltx', '%s\n\\input{chkconfig.ltx}\n' % docbook_cmd)
1107     # Construct the list of classes to test for.
1108     # build the list of available layout files and convert it to commands
1109     # for chkconfig.ltx
1110     declare = re.compile(r'\Declare(LaTeX|DocBook)Class\s*(\[([^,]*)(,.*)*\])*\s*{(.*)}')
1111     empty = re.compile(r'^\s*$')
1112     testclasses = list()
1113     for file in glob.glob( os.path.join('layouts', '*.layout') ) + \
1114         glob.glob( os.path.join(srcdir, 'layouts', '*.layout' ) ) :
1115         nodeclaration = False
1116         if not os.path.isfile(file):
1117             continue
1118         classname = file.split(os.sep)[-1].split('.')[0]
1119         for line in open(file).readlines():
1120             if not empty.match(line) and line[0] != '#':
1121                 logger.warning("Failed to find valid \Declare line for layout file `" + file + "'.\n\t=> Skipping this file!")
1122                 nodeclaration = True
1123                 break
1124             if declare.search(line) == None:
1125                 continue
1126             testclasses.append("\\TestDocClass{%s}{%s}" % (classname, line[1:].strip()))
1127             break
1128         if nodeclaration:
1129             continue
1130     testclasses.sort()
1131     cl = open('chklayouts.tex', 'w')
1132     for line in testclasses:
1133         cl.write(line + '\n')
1134     cl.close()
1135     #
1136     # we have chklayouts.tex, then process it
1137     fout = os.popen(LATEX + ' wrap_chkconfig.ltx')
1138     while True:
1139         line = fout.readline()
1140         if not line:
1141             break;
1142         if re.match('^\+', line):
1143             logger.info(line.strip())
1144     # if the command succeeds, None will be returned
1145     ret = fout.close()
1146     #
1147     # currently, values in chhkconfig are only used to set
1148     # \font_encoding
1149     values = {}
1150     for line in open('chkconfig.vars').readlines():
1151         key, val = re.sub('-', '_', line).split('=')
1152         val = val.strip()
1153         values[key] = val.strip("'")
1154     # chk_fontenc may not exist 
1155     try:
1156         addToRC(r'\font_encoding "%s"' % values["chk_fontenc"])
1157     except:
1158         pass
1159     if rmcopy:   # remove the copied file
1160         removeFiles( [ 'chkconfig.ltx' ] )
1161     # if configure successed, move textclass.lst.tmp to textclass.lst
1162     # and packages.lst.tmp to packages.lst
1163     if os.path.isfile('textclass.lst.tmp') and len(open('textclass.lst.tmp').read()) > 0 \
1164         and os.path.isfile('packages.lst.tmp') and len(open('packages.lst.tmp').read()) > 0:
1165         shutil.move('textclass.lst.tmp', 'textclass.lst')
1166         shutil.move('packages.lst.tmp', 'packages.lst')
1167     return ret
1168
1169
1170 def checkModulesConfig():
1171   removeFiles(['lyxmodules.lst', 'chkmodules.tex'])
1172
1173   logger.info('+checking list of modules... ')
1174   tx = open('lyxmodules.lst', 'w')
1175   tx.write('''## This file declares modules and their associated definition files.
1176 ## It has been automatically generated by configure
1177 ## Use "Options/Reconfigure" if you need to update it after a
1178 ## configuration change. 
1179 ## "ModuleName" "filename" "Description" "Packages" "Requires" "Excludes" "Category"
1180 ''')
1181
1182   # build the list of available modules
1183   seen = []
1184   # note that this searches the local directory first, then the
1185   # system directory. that way, we pick up the user's version first.
1186   for file in glob.glob( os.path.join('layouts', '*.module') ) + \
1187       glob.glob( os.path.join(srcdir, 'layouts', '*.module' ) ) :
1188       # valid file?
1189       logger.info(file)
1190       if not os.path.isfile(file): 
1191           continue
1192
1193       filename = file.split(os.sep)[-1]
1194       filename = filename[:-7]
1195       if seen.count(filename):
1196           continue
1197
1198       seen.append(filename)
1199       retval = processModuleFile(file, filename, bool_docbook)
1200       if retval != "":
1201           tx.write(retval)
1202   tx.close()
1203   logger.info('\tdone')
1204
1205
1206 def processModuleFile(file, filename, bool_docbook):
1207     ''' process module file and get a line of result
1208
1209         The top of a module file should look like this:
1210           #\DeclareLyXModule[LaTeX Packages]{ModuleName}
1211           #DescriptionBegin
1212           #...body of description...
1213           #DescriptionEnd
1214           #Requires: [list of required modules]
1215           #Excludes: [list of excluded modules]
1216           #Category: [category name]
1217         The last three lines are optional (though do give a category).
1218         We expect output:
1219           "ModuleName" "filename" "Description" "Packages" "Requires" "Excludes" "Category"
1220     '''
1221     remods = re.compile(r'\DeclareLyXModule\s*(?:\[([^]]*?)\])?{(.*)}')
1222     rereqs = re.compile(r'#+\s*Requires: (.*)')
1223     reexcs = re.compile(r'#+\s*Excludes: (.*)')
1224     recaty = re.compile(r'#+\s*Category: (.*)')
1225     redbeg = re.compile(r'#+\s*DescriptionBegin\s*$')
1226     redend = re.compile(r'#+\s*DescriptionEnd\s*$')
1227
1228     modname = desc = pkgs = req = excl = catgy = ""
1229     readingDescription = False
1230     descLines = []
1231
1232     for line in open(file).readlines():
1233       if readingDescription:
1234         res = redend.search(line)
1235         if res != None:
1236           readingDescription = False
1237           desc = " ".join(descLines)
1238           # Escape quotes.
1239           desc = desc.replace('"', '\\"')
1240           continue
1241         descLines.append(line[1:].strip())
1242         continue
1243       res = redbeg.search(line)
1244       if res != None:
1245         readingDescription = True
1246         continue
1247       res = remods.search(line)
1248       if res != None:
1249           (pkgs, modname) = res.groups()
1250           if pkgs == None:
1251             pkgs = ""
1252           else:
1253             tmp = [s.strip() for s in pkgs.split(",")]
1254             pkgs = ",".join(tmp)
1255           continue
1256       res = rereqs.search(line)
1257       if res != None:
1258         req = res.group(1)
1259         tmp = [s.strip() for s in req.split("|")]
1260         req = "|".join(tmp)
1261         continue
1262       res = reexcs.search(line)
1263       if res != None:
1264         excl = res.group(1)
1265         tmp = [s.strip() for s in excl.split("|")]
1266         excl = "|".join(tmp)
1267         continue
1268       res = recaty.search(line)
1269       if res != None:
1270         catgy = res.group(1)
1271         continue
1272
1273     if modname == "":
1274       logger.warning("Module file without \DeclareLyXModule line. ")
1275       return ""
1276
1277     if pkgs != "":
1278         # this module has some latex dependencies:
1279         # append the dependencies to chkmodules.tex,
1280         # which is \input'ed by chkconfig.ltx
1281         testpackages = list()
1282         for pkg in pkgs.split(","):
1283             if "->" in pkg:
1284                 # this is a converter dependency: skip
1285                 continue
1286             if pkg.endswith(".sty"):
1287                 pkg = pkg[:-4]
1288             testpackages.append("\\TestPackage{%s}" % (pkg,))
1289         cm = open('chkmodules.tex', 'a')
1290         for line in testpackages:
1291             cm.write(line + '\n')
1292         cm.close()
1293
1294     return '"%s" "%s" "%s" "%s" "%s" "%s" "%s"\n' % (modname, filename, desc, pkgs, req, excl, catgy)
1295     
1296
1297
1298 def checkTeXAllowSpaces():
1299     ''' Let's check whether spaces are allowed in TeX file names '''
1300     tex_allows_spaces = 'false'
1301     if lyx_check_config:
1302         msg = "Checking whether TeX allows spaces in file names... "
1303         writeToFile('a b.tex', r'\message{working^^J}' )
1304         if LATEX != '':
1305             if os.name == 'nt' or sys.platform == 'cygwin':
1306                 latex_out = cmdOutput(LATEX + r""" "\nonstopmode\input{\"a b\"}" """)
1307                 if not 'working' in latex_out:
1308                     latex_out = cmdOutput(LATEX + r' "\nonstopmode\input{a b}"')
1309             else:
1310                 latex_out = cmdOutput(LATEX + r""" '\nonstopmode\input{"a b"}' """)
1311         else:
1312             latex_out = ''
1313         if 'working' in latex_out:
1314             logger.info(msg + 'yes')
1315             tex_allows_spaces = 'true'
1316         else:
1317             logger.info(msg + 'no')
1318             tex_allows_spaces = 'false'
1319         addToRC(r'\tex_allows_spaces ' + tex_allows_spaces)
1320         removeFiles( [ 'a b.tex', 'a b.log', 'texput.log' ])
1321
1322
1323 def removeTempFiles():
1324     # Final clean-up
1325     if not lyx_keep_temps:
1326         removeFiles(['chkconfig.vars',  \
1327             'wrap_chkconfig.ltx', 'wrap_chkconfig.log', \
1328             'chklayouts.tex', 'chkmodules.tex', 'missfont.log', 
1329             'chklatex.ltx', 'chklatex.log'])
1330
1331
1332 if __name__ == '__main__':
1333     lyx_check_config = True
1334     outfile = 'lyxrc.defaults'
1335     rc_entries = ''
1336     lyx_keep_temps = False
1337     version_suffix = ''
1338     ## Parse the command line
1339     for op in sys.argv[1:]:   # default shell/for list is $*, the options
1340         if op in [ '-help', '--help', '-h' ]:
1341             print '''Usage: configure [options]
1342 Options:
1343     --help                   show this help lines
1344     --keep-temps             keep temporary files (for debug. purposes)
1345     --without-latex-config   do not run LaTeX to determine configuration
1346     --with-version-suffix=suffix suffix of binary installed files
1347 '''
1348             sys.exit(0)
1349         elif op == '--without-latex-config':
1350             lyx_check_config = False
1351         elif op == '--keep-temps':
1352             lyx_keep_temps = True
1353         elif op[0:22] == '--with-version-suffix=':  # never mind if op is not long enough
1354             version_suffix = op[22:]
1355         else:
1356             print "Unknown option", op
1357             sys.exit(1)
1358     #
1359     # check if we run from the right directory
1360     srcdir = os.path.dirname(sys.argv[0])
1361     if srcdir == '':
1362         srcdir = '.'
1363     if not os.path.isfile( os.path.join(srcdir, 'chkconfig.ltx') ):
1364         logger.error("configure: error: cannot find chkconfig.ltx script")
1365         sys.exit(1)
1366     setEnviron()
1367     createDirectories()
1368     dtl_tools = checkDTLtools()
1369     ## Write the first part of outfile
1370     writeToFile(outfile, '''# This file has been automatically generated by LyX' lib/configure.py
1371 # script. It contains default settings that have been determined by
1372 # examining your system. PLEASE DO NOT MODIFY ANYTHING HERE! If you
1373 # want to customize LyX, use LyX' Preferences dialog or modify directly 
1374 # the "preferences" file instead. Any setting in that file will
1375 # override the values given here.
1376 ''')
1377     # check latex
1378     LATEX = checkLatex(dtl_tools)
1379     checkFormatEntries(dtl_tools)
1380     checkConverterEntries()
1381     (chk_docbook, bool_docbook, docbook_cmd) = checkDocBook()
1382     checkTeXAllowSpaces()
1383     windows_style_tex_paths = checkTeXPaths()
1384     if windows_style_tex_paths != '':
1385         addToRC(r'\tex_expects_windows_paths %s' % windows_style_tex_paths)
1386     checkOtherEntries()
1387     checkModulesConfig()
1388     # --without-latex-config can disable lyx_check_config
1389     ret = checkLatexConfig(lyx_check_config and LATEX != '', bool_docbook)
1390     removeTempFiles()
1391     # The return error code can be 256. Because most systems expect an error code
1392     # in the range 0-127, 256 can be interpretted as 'success'. Because we expect
1393     # a None for success, 'ret is not None' is used to exit.
1394     sys.exit(ret is not None)