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