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