]> git.lyx.org Git - lyx.git/blob - lib/configure.py
Give credit to the icon teams.
[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,menu=export"
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      "%%"     ""
663 \converter sweave   pdflatex   "%%"     ""
664 \converter sweave   xetex      "%%"     ""
665 \converter sweave   luatex     "%%"     ""'''])
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                 addToRC(r'\converter lilypond-book xetex    "lilypond-book --safe --pdf --latex-program=xelatex --lily-output-dir=ly-pdf $$i"  ""')
896                 addToRC(r'\converter lilypond-book luatex   "lilypond-book --safe --pdf --latex-program=lualatex --lily-output-dir=ly-pdf $$i" ""')
897                 logger.info('+  found LilyPond-book version %s.' % version_number)
898             else:
899                 logger.info('+  found LilyPond-book, but version %s is too old.' % version_number)
900         else:
901             logger.info('+  found LilyPond-book, but could not extract version number.')
902     #
903     checkProg('a Noteedit -> LilyPond converter', ['noteedit --export-lilypond $$i'],
904         rc_entry = [ r'\converter noteedit   lilypond   "%%"    ""', ''])
905     #
906     # Currently, lyxpak outputs a gzip compressed tar archive on *nix
907     # and a zip archive on Windows.
908     # So, we configure the appropriate version according to the platform.
909     cmd = r'\converter lyx %s "python -tt $$s/scripts/lyxpak.py $$r/$$i" ""'
910     if os.name == 'nt':
911         addToRC(r'\Format lyxzip     zip    "LyX Archive (zip)"     "" "" ""  "document,menu=export"')
912         addToRC(cmd % "lyxzip")
913     else:
914         addToRC(r'\Format lyxgz      gz     "LyX Archive (tar.gz)"  "" "" ""  "document,menu=export"')
915         addToRC(cmd % "lyxgz")
916         
917     #
918     # FIXME: no rc_entry? comment it out
919     # checkProg('Image converter', ['convert $$i $$o'])
920     #
921     # Entries that do not need checkProg
922     addToRC(r'''\converter lyxpreview-platex ppm        "python -tt $$s/scripts/lyxpreview-platex2bitmap.py"    ""
923 \converter csv        lyx        "python -tt $$s/scripts/csv2lyx.py $$i $$o"    ""
924 \converter date       dateout    "python -tt $$s/scripts/date.py %d-%m-%Y > $$o"        ""
925 \converter docbook    docbook-xml "cp $$i $$o"  "xml"
926 \converter fen        asciichess "python -tt $$s/scripts/fen2ascii.py $$i $$o"  ""
927 \converter lyx        lyx13x     "python -tt $$s/lyx2lyx/lyx2lyx -t 221 $$i > $$o"      ""
928 \converter lyx        lyx14x     "python -tt $$s/lyx2lyx/lyx2lyx -t 245 $$i > $$o"      ""
929 \converter lyx        lyx15x     "python -tt $$s/lyx2lyx/lyx2lyx -t 276 $$i > $$o"      ""
930 \converter lyx        lyx16x     "python -tt $$s/lyx2lyx/lyx2lyx -t 345 $$i > $$o"      ""
931 \converter lyx        clyx       "python -tt $$s/lyx2lyx/lyx2lyx -c big5 -t 245 $$i > $$o"      ""
932 \converter lyx        jlyx       "python -tt $$s/lyx2lyx/lyx2lyx -c euc_jp -t 245 $$i > $$o"    ""
933 \converter lyx        klyx       "python -tt $$s/lyx2lyx/lyx2lyx -c euc_kr -t 245 $$i > $$o"    ""
934 \converter clyx       lyx        "python -tt $$s/lyx2lyx/lyx2lyx -c big5 $$i > $$o"     ""
935 \converter jlyx       lyx        "python -tt $$s/lyx2lyx/lyx2lyx -c euc_jp $$i > $$o"   ""
936 \converter klyx       lyx        "python -tt $$s/lyx2lyx/lyx2lyx -c euc_kr $$i > $$o"   ""
937 ''')
938
939
940 def checkDocBook():
941     ''' Check docbook '''
942     path, DOCBOOK = checkProg('SGML-tools 2.x (DocBook), db2x scripts or xsltproc', ['sgmltools', 'db2dvi', 'xsltproc'],
943         rc_entry = [
944             r'''\converter docbook    dvi        "sgmltools -b dvi $$i" ""
945 \converter docbook    html       "sgmltools -b html $$i"        ""''',
946             r'''\converter docbook    dvi        "db2dvi $$i"   ""
947 \converter docbook    html       "db2html $$i"  ""''',
948             r'''\converter docbook    dvi        ""     ""
949 \converter docbook    html       "" ""''',
950             r'''\converter docbook    dvi        ""     ""
951 \converter docbook    html       ""     ""'''])
952     #
953     if DOCBOOK != '':
954         return ('yes', 'true', '\\def\\hasdocbook{yes}')
955     else:
956         return ('no', 'false', '')
957
958
959 def checkOtherEntries():
960     ''' entries other than Format and Converter '''
961     checkProg('ChkTeX', ['chktex -n1 -n3 -n6 -n9 -n22 -n25 -n30 -n38'],
962         rc_entry = [ r'\chktex_command "%%"' ])
963     checkProgAlternatives('BibTeX or alternative programs', ['bibtex', 'bibtex8', 'biber'],
964         rc_entry = [ r'\bibtex_command "%%"' ],
965         alt_rc_entry = [ r'\bibtex_alternatives "%%"' ])
966     checkProg('a specific Japanese BibTeX variant', ['pbibtex', 'jbibtex', 'bibtex'],
967         rc_entry = [ r'\jbibtex_command "%%"' ])
968     checkProgAlternatives('available index processors', ['texindy', 'makeindex -c -q', 'xindy'],
969         rc_entry = [ r'\index_command "%%"' ],
970         alt_rc_entry = [ r'\index_alternatives "%%"' ])
971     checkProg('an index processor appropriate to Japanese', ['mendex -c -q', 'jmakeindex -c -q', 'makeindex -c -q'],
972         rc_entry = [ r'\jindex_command "%%"' ])
973     path, splitindex = checkProg('the splitindex processor', ['splitindex.pl', 'splitindex'],
974         rc_entry = [ r'\splitindex_command "%%"' ])
975     if splitindex == '':
976         checkProg('the splitindex processor (java version)', ['splitindex.class'],
977             rc_entry = [ r'\splitindex_command "java splitindex"' ])
978     checkProg('a nomenclature processor', ['makeindex'],
979         rc_entry = [ r'\nomencl_command "makeindex -s nomencl.ist"' ])
980     ## FIXME: OCTAVE is not used anywhere
981     # path, OCTAVE = checkProg('Octave', ['octave'])
982     ## FIXME: MAPLE is not used anywhere
983     # path, MAPLE = checkProg('Maple', ['maple'])
984     checkProg('a spool command', ['lp', 'lpr'],
985         rc_entry = [
986             r'''\print_spool_printerprefix "-d "
987 \print_spool_command "lp"''',
988             r'''\print_spool_printerprefix "-P",
989 \print_spool_command "lpr"''',
990             ''])
991     # Add the rest of the entries (no checkProg is required)
992     addToRC(r'''\copier    fig        "python -tt $$s/scripts/fig_copy.py $$i $$o"
993 \copier    pstex      "python -tt $$s/scripts/tex_copy.py $$i $$o $$l"
994 \copier    pdftex     "python -tt $$s/scripts/tex_copy.py $$i $$o $$l"
995 \copier    program    "python -tt $$s/scripts/ext_copy.py $$i $$o"
996 ''')
997
998
999 def processLayoutFile(file, bool_docbook):
1000     ''' process layout file and get a line of result
1001         
1002         Declare lines look like this: (article.layout, scrbook.layout, svjog.layout)
1003         
1004         \DeclareLaTeXClass{article}
1005         \DeclareLaTeXClass[scrbook]{book (koma-script)}
1006         \DeclareLaTeXClass[svjour,svjog.clo]{article (Springer - svjour/jog)}
1007
1008         we expect output:
1009         
1010         "article" "article" "article" "false" "article.cls"
1011         "scrbook" "scrbook" "book (koma-script)" "false" "scrbook.cls"
1012         "svjog" "svjour" "article (Springer - svjour/jog)" "false" "svjour.cls,svjog.clo"
1013     '''
1014     def checkForClassExtension(x):
1015         '''if the extension for a latex class is not
1016            provided, add .cls to the classname'''
1017         if not '.' in x:
1018             return x.strip() + '.cls'
1019         else:
1020             return x.strip()
1021     classname = file.split(os.sep)[-1].split('.')[0]
1022     # return ('LaTeX', '[a,b]', 'a', ',b,c', 'article') for \DeclareLaTeXClass[a,b,c]{article}
1023     p = re.compile(r'\Declare(LaTeX|DocBook)Class\s*(\[([^,]*)(,.*)*\])*\s*{(.*)}')
1024     for line in open(file).readlines():
1025         res = p.search(line)
1026         if res != None:
1027             (classtype, optAll, opt, opt1, desc) = res.groups()
1028             avai = {'LaTeX':'false', 'DocBook':bool_docbook}[classtype]
1029             if opt == None:
1030                 opt = classname
1031                 prereq_latex = checkForClassExtension(classname)
1032             else:
1033                 prereq_list = optAll[1:-1].split(',')
1034                 prereq_list = map(checkForClassExtension, prereq_list)
1035                 prereq_latex = ','.join(prereq_list)
1036             prereq_docbook = {'true':'', 'false':'docbook'}[bool_docbook]
1037             prereq = {'LaTeX':prereq_latex, 'DocBook':prereq_docbook}[classtype]
1038             return '"%s" "%s" "%s" "%s" "%s"\n' % (classname, opt, desc, avai, prereq)
1039     logger.warning("Layout file " + file + " has no \DeclareXXClass line. ")
1040     return ""
1041
1042
1043 def checkLatexConfig(check_config, bool_docbook):
1044     ''' Explore the LaTeX configuration 
1045         Return None (will be passed to sys.exit()) for success.
1046     '''
1047     msg = 'checking LaTeX configuration... '
1048     # if --without-latex-config is forced, or if there is no previous 
1049     # version of textclass.lst, re-generate a default file.
1050     if not os.path.isfile('textclass.lst') or not check_config:
1051         # remove the files only if we want to regenerate
1052         removeFiles(['textclass.lst', 'packages.lst'])
1053         #
1054         # Then, generate a default textclass.lst. In case configure.py
1055         # fails, we still have something to start lyx.
1056         logger.info(msg + ' default values')
1057         logger.info('+checking list of textclasses... ')
1058         tx = open('textclass.lst', 'w')
1059         tx.write('''
1060 # This file declares layouts and their associated definition files
1061 # (include dir. relative to the place where this file is).
1062 # It contains only default values, since chkconfig.ltx could not be run
1063 # for some reason. Run ./configure.py if you need to update it after a
1064 # configuration change.
1065 ''')
1066         # build the list of available layout files and convert it to commands
1067         # for chkconfig.ltx
1068         foundClasses = []
1069         for file in glob.glob( os.path.join('layouts', '*.layout') ) + \
1070             glob.glob( os.path.join(srcdir, 'layouts', '*.layout' ) ) :
1071             # valid file?
1072             if not os.path.isfile(file): 
1073                 continue
1074             # get stuff between /xxxx.layout .
1075             classname = file.split(os.sep)[-1].split('.')[0]
1076             #  tr ' -' '__'`
1077             cleanclass = classname.replace(' ', '_')
1078             cleanclass = cleanclass.replace('-', '_')
1079             # make sure the same class is not considered twice
1080             if foundClasses.count(cleanclass) == 0: # not found before
1081                 foundClasses.append(cleanclass)
1082                 retval = processLayoutFile(file, bool_docbook)
1083                 if retval != "":
1084                     tx.write(retval)
1085         tx.close()
1086         logger.info('\tdone')
1087     if not check_config:
1088         return None
1089     # the following will generate textclass.lst.tmp, and packages.lst.tmp
1090     logger.info(msg + '\tauto')
1091     removeFiles(['wrap_chkconfig.ltx', 'chkconfig.vars', \
1092         'chkconfig.classes', 'chklayouts.tex'])
1093     rmcopy = False
1094     if not os.path.isfile( 'chkconfig.ltx' ):
1095         shutil.copyfile( os.path.join(srcdir, 'chkconfig.ltx'), 'chkconfig.ltx' )
1096         rmcopy = True
1097     writeToFile('wrap_chkconfig.ltx', '%s\n\\input{chkconfig.ltx}\n' % docbook_cmd)
1098     # Construct the list of classes to test for.
1099     # build the list of available layout files and convert it to commands
1100     # for chkconfig.ltx
1101     declare = re.compile(r'\Declare(LaTeX|DocBook)Class\s*(\[([^,]*)(,.*)*\])*\s*{(.*)}')
1102     empty = re.compile(r'^\s*$')
1103     testclasses = list()
1104     for file in glob.glob( os.path.join('layouts', '*.layout') ) + \
1105         glob.glob( os.path.join(srcdir, 'layouts', '*.layout' ) ) :
1106         nodeclaration = False
1107         if not os.path.isfile(file):
1108             continue
1109         classname = file.split(os.sep)[-1].split('.')[0]
1110         for line in open(file).readlines():
1111             if not empty.match(line) and line[0] != '#':
1112                 logger.warning("Failed to find valid \Declare line for layout file `" + file + "'.\n\t=> Skipping this file!")
1113                 nodeclaration = True
1114                 break
1115             if declare.search(line) == None:
1116                 continue
1117             testclasses.append("\\TestDocClass{%s}{%s}" % (classname, line[1:].strip()))
1118             break
1119         if nodeclaration:
1120             continue
1121     testclasses.sort()
1122     cl = open('chklayouts.tex', 'w')
1123     for line in testclasses:
1124         cl.write(line + '\n')
1125     cl.close()
1126     #
1127     # we have chklayouts.tex, then process it
1128     fout = os.popen(LATEX + ' wrap_chkconfig.ltx')
1129     while True:
1130         line = fout.readline()
1131         if not line:
1132             break;
1133         if re.match('^\+', line):
1134             logger.info(line.strip())
1135     # if the command succeeds, None will be returned
1136     ret = fout.close()
1137     #
1138     # currently, values in chhkconfig are only used to set
1139     # \font_encoding
1140     values = {}
1141     for line in open('chkconfig.vars').readlines():
1142         key, val = re.sub('-', '_', line).split('=')
1143         val = val.strip()
1144         values[key] = val.strip("'")
1145     # chk_fontenc may not exist 
1146     try:
1147         addToRC(r'\font_encoding "%s"' % values["chk_fontenc"])
1148     except:
1149         pass
1150     if rmcopy:   # remove the copied file
1151         removeFiles( [ 'chkconfig.ltx' ] )
1152     # if configure successed, move textclass.lst.tmp to textclass.lst
1153     # and packages.lst.tmp to packages.lst
1154     if os.path.isfile('textclass.lst.tmp') and len(open('textclass.lst.tmp').read()) > 0 \
1155         and os.path.isfile('packages.lst.tmp') and len(open('packages.lst.tmp').read()) > 0:
1156         shutil.move('textclass.lst.tmp', 'textclass.lst')
1157         shutil.move('packages.lst.tmp', 'packages.lst')
1158     return ret
1159
1160
1161 def checkModulesConfig():
1162   removeFiles(['lyxmodules.lst', 'chkmodules.tex'])
1163
1164   logger.info('+checking list of modules... ')
1165   tx = open('lyxmodules.lst', 'w')
1166   tx.write('''## This file declares modules and their associated definition files.
1167 ## It has been automatically generated by configure
1168 ## Use "Options/Reconfigure" if you need to update it after a
1169 ## configuration change. 
1170 ## "ModuleName" "filename" "Description" "Packages" "Requires" "Excludes" "Category"
1171 ''')
1172   # build the list of available modules
1173   foundClasses = []
1174   for file in glob.glob( os.path.join('layouts', '*.module') ) + \
1175       glob.glob( os.path.join(srcdir, 'layouts', '*.module' ) ) :
1176       # valid file?
1177       logger.info(file)
1178       if not os.path.isfile(file): 
1179           continue
1180       retval = processModuleFile(file, bool_docbook)
1181       if retval != "":
1182           tx.write(retval)
1183   tx.close()
1184   logger.info('\tdone')
1185
1186
1187 def processModuleFile(file, bool_docbook):
1188     ''' process module file and get a line of result
1189
1190         The top of a module file should look like this:
1191           #\DeclareLyXModule[LaTeX Packages]{ModuleName}
1192           #DescriptionBegin
1193           #...body of description...
1194           #DescriptionEnd
1195           #Requires: [list of required modules]
1196           #Excludes: [list of excluded modules]
1197           #Category: [category name]
1198         The last three lines are optional (though do give a category).
1199         We expect output:
1200           "ModuleName" "filename" "Description" "Packages" "Requires" "Excludes" "Category"
1201     '''
1202     remods = re.compile(r'\DeclareLyXModule\s*(?:\[([^]]*?)\])?{(.*)}')
1203     rereqs = re.compile(r'#+\s*Requires: (.*)')
1204     reexcs = re.compile(r'#+\s*Excludes: (.*)')
1205     recaty = re.compile(r'#+\s*Category: (.*)')
1206     redbeg = re.compile(r'#+\s*DescriptionBegin\s*$')
1207     redend = re.compile(r'#+\s*DescriptionEnd\s*$')
1208
1209     modname = desc = pkgs = req = excl = catgy = ""
1210     readingDescription = False
1211     descLines = []
1212     filename = file.split(os.sep)[-1]
1213     filename = filename[:-7]
1214
1215     for line in open(file).readlines():
1216       if readingDescription:
1217         res = redend.search(line)
1218         if res != None:
1219           readingDescription = False
1220           desc = " ".join(descLines)
1221           # Escape quotes.
1222           desc = desc.replace('"', '\\"')
1223           continue
1224         descLines.append(line[1:].strip())
1225         continue
1226       res = redbeg.search(line)
1227       if res != None:
1228         readingDescription = True
1229         continue
1230       res = remods.search(line)
1231       if res != None:
1232           (pkgs, modname) = res.groups()
1233           if pkgs == None:
1234             pkgs = ""
1235           else:
1236             tmp = [s.strip() for s in pkgs.split(",")]
1237             pkgs = ",".join(tmp)
1238           continue
1239       res = rereqs.search(line)
1240       if res != None:
1241         req = res.group(1)
1242         tmp = [s.strip() for s in req.split("|")]
1243         req = "|".join(tmp)
1244         continue
1245       res = reexcs.search(line)
1246       if res != None:
1247         excl = res.group(1)
1248         tmp = [s.strip() for s in excl.split("|")]
1249         excl = "|".join(tmp)
1250         continue
1251       res = recaty.search(line)
1252       if res != None:
1253         catgy = res.group(1)
1254         continue
1255
1256     if modname == "":
1257       logger.warning("Module file without \DeclareLyXModule line. ")
1258       return ""
1259
1260     if pkgs != "":
1261         # this module has some latex dependencies:
1262         # append the dependencies to chkmodules.tex,
1263         # which is \input'ed by chkconfig.ltx
1264         testpackages = list()
1265         for pkg in pkgs.split(","):
1266             if "->" in pkg:
1267                 # this is a converter dependency: skip
1268                 continue
1269             if pkg.endswith(".sty"):
1270                 pkg = pkg[:-4]
1271             testpackages.append("\\TestPackage{%s}" % (pkg,))
1272         cm = open('chkmodules.tex', 'a')
1273         for line in testpackages:
1274             cm.write(line + '\n')
1275         cm.close()
1276
1277     return '"%s" "%s" "%s" "%s" "%s" "%s" "%s"\n' % (modname, filename, desc, pkgs, req, excl, catgy)
1278     
1279
1280
1281 def checkTeXAllowSpaces():
1282     ''' Let's check whether spaces are allowed in TeX file names '''
1283     tex_allows_spaces = 'false'
1284     if lyx_check_config:
1285         msg = "Checking whether TeX allows spaces in file names... "
1286         writeToFile('a b.tex', r'\message{working^^J}' )
1287         if LATEX != '':
1288             if os.name == 'nt':
1289                 latex_out = cmdOutput(LATEX + r""" "\nonstopmode\input{\"a b\"}" """)
1290             else:
1291                 latex_out = cmdOutput(LATEX + r""" '\nonstopmode\input{"a b"}' """)
1292         else:
1293             latex_out = ''
1294         if 'working' in latex_out:
1295             logger.info(msg + 'yes')
1296             tex_allows_spaces = 'true'
1297         else:
1298             logger.info(msg + 'no')
1299             tex_allows_spaces = 'false'
1300         addToRC(r'\tex_allows_spaces ' + tex_allows_spaces)
1301         removeFiles( [ 'a b.tex', 'a b.log', 'texput.log' ])
1302
1303
1304 def removeTempFiles():
1305     # Final clean-up
1306     if not lyx_keep_temps:
1307         removeFiles(['chkconfig.vars',  \
1308             'wrap_chkconfig.ltx', 'wrap_chkconfig.log', \
1309             'chklayouts.tex', 'chkmodules.tex', 'missfont.log', 
1310             'chklatex.ltx', 'chklatex.log'])
1311
1312
1313 if __name__ == '__main__':
1314     lyx_check_config = True
1315     outfile = 'lyxrc.defaults'
1316     rc_entries = ''
1317     lyx_keep_temps = False
1318     version_suffix = ''
1319     ## Parse the command line
1320     for op in sys.argv[1:]:   # default shell/for list is $*, the options
1321         if op in [ '-help', '--help', '-h' ]:
1322             print '''Usage: configure [options]
1323 Options:
1324     --help                   show this help lines
1325     --keep-temps             keep temporary files (for debug. purposes)
1326     --without-latex-config   do not run LaTeX to determine configuration
1327     --with-version-suffix=suffix suffix of binary installed files
1328 '''
1329             sys.exit(0)
1330         elif op == '--without-latex-config':
1331             lyx_check_config = False
1332         elif op == '--keep-temps':
1333             lyx_keep_temps = True
1334         elif op[0:22] == '--with-version-suffix=':  # never mind if op is not long enough
1335             version_suffix = op[22:]
1336         else:
1337             print "Unknown option", op
1338             sys.exit(1)
1339     #
1340     # check if we run from the right directory
1341     srcdir = os.path.dirname(sys.argv[0])
1342     if srcdir == '':
1343         srcdir = '.'
1344     if not os.path.isfile( os.path.join(srcdir, 'chkconfig.ltx') ):
1345         logger.error("configure: error: cannot find chkconfig.ltx script")
1346         sys.exit(1)
1347     setEnviron()
1348     createDirectories()
1349     dtl_tools = checkDTLtools()
1350     ## Write the first part of outfile
1351     writeToFile(outfile, '''# This file has been automatically generated by LyX' lib/configure.py
1352 # script. It contains default settings that have been determined by
1353 # examining your system. PLEASE DO NOT MODIFY ANYTHING HERE! If you
1354 # want to customize LyX, use LyX' Preferences dialog or modify directly 
1355 # the "preferences" file instead. Any setting in that file will
1356 # override the values given here.
1357 ''')
1358     # check latex
1359     LATEX = checkLatex(dtl_tools)
1360     checkFormatEntries(dtl_tools)
1361     checkConverterEntries()
1362     (chk_docbook, bool_docbook, docbook_cmd) = checkDocBook()
1363     checkTeXAllowSpaces()
1364     windows_style_tex_paths = checkTeXPaths()
1365     if windows_style_tex_paths != '':
1366         addToRC(r'\tex_expects_windows_paths %s' % windows_style_tex_paths)
1367     checkOtherEntries()
1368     checkModulesConfig()
1369     # --without-latex-config can disable lyx_check_config
1370     ret = checkLatexConfig(lyx_check_config and LATEX != '', bool_docbook)
1371     removeTempFiles()
1372     # The return error code can be 256. Because most systems expect an error code
1373     # in the range 0-127, 256 can be interpretted as 'success'. Because we expect
1374     # a None for success, 'ret is not None' is used to exit.
1375     sys.exit(ret is not None)