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