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