]> git.lyx.org Git - lyx.git/blob - lib/configure.py
acd48812199dbf308f98619bf2cba0f311615513
[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 os.environ.has_key("PATHEXT"):
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 os.environ.has_key("PATHEXT"):
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             logger.info("rc_entry: " + rc_entry[0])
296             rcs = rc_entry[0].split('\n')
297             alt = addViewerAlternatives(rcs)
298             alt_rc_entry.insert(0, alt)
299         elif len(rc_entry) > 1:
300             logger.info("rc_entry: " + rc_entry[idx])
301             rcs = rc_entry[idx].split('\n')
302             alt = addViewerAlternatives(rcs)
303             alt_rc_entry.insert(idx, alt)
304     return checkProgAlternatives(description, progs, rc_entry, alt_rc_entry, path, not_found = 'auto')
305
306
307 def checkEditor(description, progs, rc_entry = [], path = []):
308     ''' The same as checkProgAlternatives, but for editors '''
309     if len(rc_entry) > 1 and len(rc_entry) != len(progs) + 1:
310         logger.error("rc entry should have one item or item for each prog and not_found.")
311         sys.exit(2)
312     alt_rc_entry = []
313     for idx in range(len(progs)):
314         if len(rc_entry) == 1:
315             logger.info("rc_entry: " + rc_entry[0])
316             rcs = rc_entry[0].split('\n')
317             alt = addEditorAlternatives(rcs)
318             alt_rc_entry.insert(0, alt)
319         elif len(rc_entry) > 1:
320             logger.info("rc_entry: " + rc_entry[idx])
321             rcs = rc_entry[idx].split('\n')
322             alt = addEditorAlternatives(rcs)
323             alt_rc_entry.insert(idx, alt)
324     return checkProgAlternatives(description, progs, rc_entry, alt_rc_entry, path, not_found = 'auto')
325
326
327 def checkViewerNoRC(description, progs, rc_entry = [], path = []):
328     ''' The same as checkViewer, but do not add rc entry '''
329     if len(rc_entry) > 1 and len(rc_entry) != len(progs) + 1:
330         logger.error("rc entry should have one item or item for each prog and not_found.")
331         sys.exit(2)
332     alt_rc_entry = []
333     for idx in range(len(progs)):
334         if len(rc_entry) == 1:
335             logger.info("rc_entry: " + rc_entry[0])
336             rcs = rc_entry[0].split('\n')
337             alt = addViewerAlternatives(rcs)
338             alt_rc_entry.insert(0, alt)
339         elif len(rc_entry) > 1:
340             logger.info("rc_entry: " + rc_entry[idx])
341             rcs = rc_entry[idx].split('\n')
342             alt = addViewerAlternatives(rcs)
343             alt_rc_entry.insert(idx, alt)
344     rc_entry = []
345     return checkProgAlternatives(description, progs, rc_entry, alt_rc_entry, path, not_found = 'auto')
346
347
348 def checkEditorNoRC(description, progs, rc_entry = [], path = []):
349     ''' The same as checkViewer, but do not add rc entry '''
350     if len(rc_entry) > 1 and len(rc_entry) != len(progs) + 1:
351         logger.error("rc entry should have one item or item for each prog and not_found.")
352         sys.exit(2)
353     alt_rc_entry = []
354     for idx in range(len(progs)):
355         if len(rc_entry) == 1:
356             logger.info("rc_entry: " + rc_entry[0])
357             rcs = rc_entry[0].split('\n')
358             alt = addEditorAlternatives(rcs)
359             alt_rc_entry.insert(0, alt)
360         elif len(rc_entry) > 1:
361             logger.info("rc_entry: " + rc_entry[idx])
362             rcs = rc_entry[idx].split('\n')
363             alt = addEditorAlternatives(rcs)
364             alt_rc_entry.insert(idx, alt)
365     rc_entry = []
366     return checkProgAlternatives(description, progs, rc_entry, alt_rc_entry, path, not_found = 'auto')
367
368
369 def checkViewerEditor(description, progs, rc_entry = [], path = []):
370     ''' The same as checkProgAlternatives, but for viewers and editors '''
371     checkEditorNoRC(description, progs, rc_entry, path)
372     return checkViewer(description, progs, rc_entry, path)
373
374
375 def checkDTLtools():
376     ''' Check whether DTL tools are available (Windows only) '''
377     # Find programs! Returned path is not used now
378     if ((os.name == 'nt' or sys.platform == 'cygwin') and
379             checkProg('DVI to DTL converter', ['dv2dt']) != ['', ''] and
380             checkProg('DTL to DVI converter', ['dt2dv']) != ['', '']):
381         dtl_tools = True
382     else:
383         dtl_tools = False
384     return dtl_tools
385
386
387 def checkLatex(dtl_tools):
388     ''' Check latex, return lyx_check_config '''
389     path, LATEX = checkProg('a Latex2e program', ['latex $$i', 'platex $$i', 'latex2e $$i'])
390     path, PPLATEX = checkProg('a DVI postprocessing program', ['pplatex $$i'])
391     #-----------------------------------------------------------------
392     path, PLATEX = checkProg('pLaTeX, the Japanese LaTeX', ['platex $$i'])
393     if PLATEX != '':
394         # check if PLATEX is pLaTeX2e
395         writeToFile('chklatex.ltx', '''
396 \\nonstopmode
397 \\@@end
398 ''')
399         # run platex on chklatex.ltx and check result
400         if cmdOutput(PLATEX + ' chklatex.ltx').find('pLaTeX2e') != -1:
401             # We have the Japanese pLaTeX2e
402             addToRC(r'\converter platex   dvi       "%s"   "latex"' % PLATEX)
403             LATEX = PLATEX
404         else:
405             PLATEX = ''
406             removeFiles(['chklatex.ltx', 'chklatex.log'])
407     #-----------------------------------------------------------------
408     # use LATEX to convert from latex to dvi if PPLATEX is not available    
409     if PPLATEX == '':
410         PPLATEX = LATEX
411     if dtl_tools:
412         # Windows only: DraftDVI
413         addToRC(r'''\converter latex      dvi2       "%s"       "latex"
414 \converter dvi2       dvi        "python -tt $$s/scripts/clean_dvi.py $$i $$o"  ""''' % PPLATEX)
415     else:
416         addToRC(r'\converter latex      dvi        "%s" "latex"' % PPLATEX)
417     # no latex
418     if LATEX != '':
419         # Check if latex is usable
420         writeToFile('chklatex.ltx', '''
421 \\nonstopmode\\makeatletter
422 \\ifx\\undefined\\documentclass\\else
423   \\message{ThisIsLaTeX2e}
424 \\fi
425 \\@@end
426 ''')
427         # run latex on chklatex.ltx and check result
428         if cmdOutput(LATEX + ' chklatex.ltx').find('ThisIsLaTeX2e') != -1:
429             # valid latex2e
430             return LATEX
431         else:
432             logger.warning("Latex not usable (not LaTeX2e) ")
433         # remove temporary files
434         removeFiles(['chklatex.ltx', 'chklatex.log'])
435     return ''
436
437
438 def checkFormatEntries(dtl_tools):  
439     ''' Check all formats (\Format entries) '''
440     checkViewerEditor('a Tgif viewer and editor', ['tgif'],
441         rc_entry = [r'\Format tgif       obj     Tgif                   "" "%%" "%%"    "vector"'])
442     #
443     checkViewerEditor('a FIG viewer and editor', ['xfig', 'jfig3-itext.jar', 'jfig3.jar'],
444         rc_entry = [r'\Format fig        fig     FIG                    "" "%%" "%%"    "vector"'])
445     #
446     checkViewerEditor('a Dia viewer and editor', ['dia'],
447         rc_entry = [r'\Format dia        dia     DIA                    "" "%%" "%%"    "vector"'])
448     #
449     checkViewerEditor('a Grace viewer and editor', ['xmgrace'],
450         rc_entry = [r'\Format agr        agr     Grace                  "" "%%" "%%"    "vector"'])
451     #
452     checkViewerEditor('a FEN viewer and editor', ['xboard -lpf $$i -mode EditPosition'],
453         rc_entry = [r'\Format fen        fen     FEN                    "" "%%" "%%"    ""'])
454     #
455     path, iv = checkViewerNoRC('a raster image viewer', ['xv', 'kview', 'gimp-remote', 'gimp'],
456         rc_entry = [r'''\Format bmp        bmp     BMP                    "" "%s"       "%s"    ""
457 \Format gif        gif     GIF                    "" "%s"       "%s"    ""
458 \Format jpg        jpg     JPEG                   "" "%s"       "%s"    ""
459 \Format pbm        pbm     PBM                    "" "%s"       "%s"    ""
460 \Format pgm        pgm     PGM                    "" "%s"       "%s"    ""
461 \Format png        png     PNG                    "" "%s"       "%s"    ""
462 \Format ppm        ppm     PPM                    "" "%s"       "%s"    ""
463 \Format tiff       tif     TIFF                   "" "%s"       "%s"    ""
464 \Format xbm        xbm     XBM                    "" "%s"       "%s"    ""
465 \Format xpm        xpm     XPM                    "" "%s"       "%s"    ""'''])
466     path, ie = checkEditorNoRC('a raster image editor', ['gimp-remote', 'gimp'],
467         rc_entry = [r'''\Format bmp        bmp     BMP                    "" "%s"       "%s"    ""
468 \Format gif        gif     GIF                    "" "%s"       "%s"    ""
469 \Format jpg        jpg     JPEG                   "" "%s"       "%s"    ""
470 \Format pbm        pbm     PBM                    "" "%s"       "%s"    ""
471 \Format pgm        pgm     PGM                    "" "%s"       "%s"    ""
472 \Format png        png     PNG                    "" "%s"       "%s"    ""
473 \Format ppm        ppm     PPM                    "" "%s"       "%s"    ""
474 \Format tiff       tif     TIFF                   "" "%s"       "%s"    ""
475 \Format xbm        xbm     XBM                    "" "%s"       "%s"    ""
476 \Format xpm        xpm     XPM                    "" "%s"       "%s"    ""'''])
477     addToRC(r'''\Format bmp        bmp     BMP                    "" "%s"       "%s"    ""
478 \Format gif        gif     GIF                    "" "%s"       "%s"    ""
479 \Format jpg        jpg     JPEG                   "" "%s"       "%s"    ""
480 \Format pbm        pbm     PBM                    "" "%s"       "%s"    ""
481 \Format pgm        pgm     PGM                    "" "%s"       "%s"    ""
482 \Format png        png     PNG                    "" "%s"       "%s"    ""
483 \Format ppm        ppm     PPM                    "" "%s"       "%s"    ""
484 \Format tiff       tif     TIFF                   "" "%s"       "%s"    ""
485 \Format xbm        xbm     XBM                    "" "%s"       "%s"    ""
486 \Format xpm        xpm     XPM                    "" "%s"       "%s"    ""''' % \
487         (iv, ie, iv, ie, iv, ie, iv, ie, iv, ie, iv, ie, iv, ie, iv, ie, iv, ie, iv, ie) )
488     #
489     checkViewerEditor('a text editor', ['sensible-editor', 'xemacs', 'gvim', 'kedit', 'kwrite', 'kate', \
490         'nedit', 'gedit', 'notepad'],
491         rc_entry = [r'''\Format asciichess asc    "Plain text (chess output)"  "" ""    "%%"    ""
492 \Format asciiimage asc    "Plain text (image)"         "" ""    "%%"    ""
493 \Format asciixfig  asc    "Plain text (Xfig output)"   "" ""    "%%"    ""
494 \Format dateout    tmp    "date (output)"         "" "" "%%"    ""
495 \Format docbook    sgml    DocBook                B  "" "%%"    "document"
496 \Format docbook-xml xml   "Docbook (XML)"         "" "" "%%"    "document"
497 \Format dot        dot    "Graphviz Dot"          "" "" "%%"    "vector"
498 \Format platex     tex    "LaTeX (pLaTeX)"        "" "" "%%"    "document"
499 \Format literate   nw      NoWeb                  N  "" "%%"    "document"
500 \Format sweave     Rnw    "Sweave"                S  "" "%%"    "document"
501 \Format lilypond   ly     "LilyPond music"        "" "" "%%"    "vector"
502 \Format latex      tex    "LaTeX (plain)"         L  "" "%%"    "document"
503 \Format pdflatex   tex    "LaTeX (pdflatex)"      "" "" "%%"    "document"
504 \Format xetex      tex    "LaTeX (XeTeX)"         "" "" "%%"    "document"
505 \Format text       txt    "Plain text"            a  "" "%%"    "document"
506 \Format text2      txt    "Plain text (pstotext)" "" "" "%%"    "document"
507 \Format text3      txt    "Plain text (ps2ascii)" "" "" "%%"    "document"
508 \Format text4      txt    "Plain text (catdvi)"   "" "" "%%"    "document"
509 \Format textparagraph txt "Plain Text, Join Lines" "" ""        "%%"    "document"''' ])
510  #
511     path, xhtmlview = checkViewer('an HTML previewer', ['firefox', 'mozilla file://$$p$$i', 'netscape'],
512         rc_entry = [r'\Format xhtml      html   "LyX HTML"              "" "%%" ""    "document"'])
513     if xhtmlview == "":
514         addToRC(r'\Format xhtml      html   "LyX HTML"              "" "" "%%"  "document"')
515  #
516     checkEditor('a BibTeX editor', ['sensible-editor', 'jabref', 'JabRef', \
517         'pybliographic', 'bibdesk', 'gbib', 'kbib', \
518         'kbibtex', 'sixpack', 'bibedit', 'tkbibtex' \
519         'xemacs', 'gvim', 'kedit', 'kwrite', 'kate', \
520         'nedit', 'gedit', 'notepad'],
521         rc_entry = [r'''\Format bibtex bib    "BibTeX"         "" ""    "%%"    ""''' ])
522     #
523     #checkProg('a Postscript interpreter', ['gs'],
524     #  rc_entry = [ r'\ps_command "%%"' ])
525     checkViewer('a Postscript previewer', ['kghostview', 'okular', 'evince', 'gv', 'ghostview -swap'],
526         rc_entry = [r'''\Format eps        eps     EPS                    "" "%%"       ""      "vector"
527 \Format ps         ps      Postscript             t  "%%"       ""      "document,vector"'''])
528     # for xdg-open issues look here: http://www.mail-archive.com/lyx-devel@lists.lyx.org/msg151818.html
529     checkViewer('a PDF previewer', ['kpdf', 'okular', 'evince', 'kghostview', 'xpdf', 'acrobat', 'acroread', \
530                     'gv', 'ghostview'],
531         rc_entry = [r'''\Format pdf        pdf    "PDF (ps2pdf)"          P  "%%"       ""      "document,vector"
532 \Format pdf2       pdf    "PDF (pdflatex)"        F  "%%"       ""      "document,vector"
533 \Format pdf3       pdf    "PDF (dvipdfm)"         m  "%%"       ""      "document,vector"
534 \Format pdf4       pdf    "PDF (XeTeX)"           X  "%%"       ""      "document,vector"'''])
535     #
536     checkViewer('a DVI previewer', ['xdvi', 'kdvi', 'okular', 'yap', 'dviout -Set=!m'],
537         rc_entry = [r'\Format dvi        dvi     DVI                    D  "%%" ""      "document,vector"'])
538     if dtl_tools:
539         # Windows only: DraftDVI
540         addToRC(r'\Format dvi2       dvi     DraftDVI               ""  ""      ""      "vector"')
541     #
542     checkViewer('an HTML previewer', ['firefox', 'mozilla file://$$p$$i', 'netscape'],
543         rc_entry = [r'\Format html       html    HTML                   H  "%%" ""      "document"'])
544     #
545     checkViewerEditor('Noteedit', ['noteedit'],
546         rc_entry = [r'\Format noteedit   not     Noteedit               "" "%%" "%%"    "vector"'])
547     #
548     checkViewerEditor('an OpenDocument viewer', ['swriter', 'oowriter'],
549         rc_entry = [r'\Format odt        odt     OpenDocument           "" "%%" "%%"    "document,vector"'])
550     #
551     # entried that do not need checkProg
552     addToRC(r'''\Format date       ""     "date command"          "" "" ""      ""
553 \Format csv        csv    "Table (CSV)"  "" ""  ""      "document"
554 \Format fax        ""      Fax                    "" "" ""      "document"
555 \Format lyx        lyx     LyX                    "" "" ""      ""
556 \Format lyx13x     lyx13  "LyX 1.3.x"             "" "" ""      "document"
557 \Format lyx14x     lyx14  "LyX 1.4.x"             "" "" ""      "document"
558 \Format lyx15x     lyx15  "LyX 1.5.x"             "" "" ""      "document"
559 \Format lyx16x     lyx16  "LyX 1.6.x"             "" "" ""      "document"
560 \Format clyx       cjklyx "CJK LyX 1.4.x (big5)"  "" "" ""      "document"
561 \Format jlyx       cjklyx "CJK LyX 1.4.x (euc-jp)" "" ""        ""      "document"
562 \Format klyx       cjklyx "CJK LyX 1.4.x (euc-kr)" "" ""        ""      "document"
563 \Format lyxpreview lyxpreview "LyX Preview"       "" "" ""      ""
564 \Format lyxpreview-platex lyxpreview-platex "LyX Preview (pLaTeX)"       "" ""  ""      ""
565 \Format pdftex     pdftex_t PDFTEX                "" "" ""      ""
566 \Format program    ""      Program                "" "" ""      ""
567 \Format pstex      pstex_t PSTEX                  "" "" ""      ""
568 \Format rtf        rtf    "Rich Text Format"      "" "" ""      "document,vector"
569 \Format sxw        sxw    "OpenOffice.Org (sxw)"  ""  ""        ""      "document,vector"
570 \Format wmf        wmf    "Windows Metafile"      "" "" ""      "vector"
571 \Format emf        emf    "Enhanced Metafile"     "" "" ""      "vector"
572 \Format word       doc    "MS Word"               W  "" ""      "document,vector"
573 \Format wordhtml   html   "HTML (MS Word)"        "" "" ""      "document"
574 ''')
575
576
577 def checkConverterEntries():
578     ''' Check all converters (\converter entries) '''
579     checkProg('the pdflatex program', ['pdflatex $$i'],
580         rc_entry = [ r'\converter pdflatex   pdf2       "%%"    "latex"' ])
581
582     checkProg('XeTeX', ['xelatex $$i'],
583         rc_entry = [ r'\converter xetex      pdf4       "%%"    "latex"' ])
584     
585     ''' If we're running LyX in-place then tex2lyx will be found in
586             ../src/tex2lyx. Add this directory to the PATH temporarily and
587             search for tex2lyx.
588             Use PATH to avoid any problems with paths-with-spaces.
589     '''
590     path_orig = os.environ["PATH"]
591     os.environ["PATH"] = os.path.join('..', 'src', 'tex2lyx') + \
592         os.pathsep + path_orig
593
594     checkProg('a LaTeX/Noweb -> LyX converter', ['tex2lyx', 'tex2lyx' + version_suffix],
595         rc_entry = [r'''\converter latex      lyx        "%% -f $$i $$o"        ""
596 \converter literate   lyx        "%% -n -f $$i $$o"     ""'''])
597
598     os.environ["PATH"] = path_orig
599
600     #
601     checkProg('a Noweb -> LaTeX converter', ['noweave -delay -index $$i > $$o'],
602         rc_entry = [r'''\converter literate   latex      "%%"   ""
603 \converter literate   pdflatex      "%%"        ""'''])
604     #
605     checkProg('a Sweave -> LaTeX converter', ['R CMD Sweave $$i'],
606         rc_entry = [r'''\converter sweave   latex      "%%"     ""
607 \converter sweave   pdflatex      "%%"  ""'''])
608     #
609     checkProg('an HTML -> LaTeX converter', ['html2latex $$i', 'gnuhtml2latex $$i', \
610         'htmltolatex -input $$i -output $$o', 'java -jar htmltolatex.jar -input $$i -output $$o'],
611         rc_entry = [ r'\converter html       latex      "%%"    ""' ])
612     #
613     checkProg('an MS Word -> LaTeX converter', ['wvCleanLatex $$i $$o'],
614         rc_entry = [ r'\converter word       latex      "%%"    ""' ])
615     #
616     path, elyxer = checkProg('a LyX -> HTML converter', ['elyxer.py', 'elyxer'],
617       rc_entry = [ r'\converter lyx      html       "python -tt $$s/scripts/elyxer.py --directory $$r $$i $$o"  ""' ])
618     if elyxer.find('elyxer') >= 0:
619       addToRC(r'''\copier    html       "python -tt $$s/scripts/ext_copy.py -e html,png,jpg,jpeg,css $$i $$o"''')
620     else:
621       # On SuSE the scripts have a .sh suffix, and on debian they are in /usr/share/tex4ht/
622       path, htmlconv = checkProg('a LaTeX -> HTML converter', ['htlatex $$i', 'htlatex.sh $$i', \
623           '/usr/share/tex4ht/htlatex $$i', 'tth  -t -e2 -L$$b < $$i > $$o', \
624           'latex2html -no_subdir -split 0 -show_section_numbers $$i', 'hevea -s $$i'],
625           rc_entry = [ r'\converter latex      html       "%%"  "needaux"' ])
626       if htmlconv.find('htlatex') >= 0 or htmlconv == 'latex2html':
627         addToRC(r'''\copier    html       "python -tt $$s/scripts/ext_copy.py -e html,png,css $$i $$o"''')
628       else:
629         addToRC(r'''\copier    html       "python -tt $$s/scripts/ext_copy.py $$i $$o"''')
630
631     # On SuSE the scripts have a .sh suffix, and on debian they are in /usr/share/tex4ht/
632     path, htmlconv = checkProg('a LaTeX -> MS Word converter', ["htlatex $$i 'html,word' 'symbol/!' '-cvalidate'", \
633         "htlatex.sh $$i 'html,word' 'symbol/!' '-cvalidate'", \
634         "/usr/share/tex4ht/htlatex $$i 'html,word' 'symbol/!' '-cvalidate'"],
635         rc_entry = [ r'\converter latex      wordhtml   "%%"    "needaux"' ])
636     if htmlconv.find('htlatex') >= 0:
637       addToRC(r'''\copier    wordhtml       "python -tt $$s/scripts/ext_copy.py -e html,png,css $$i $$o"''')
638     #
639     checkProg('an OpenOffice.org -> LaTeX converter', ['w2l -clean $$i'],
640         rc_entry = [ r'\converter sxw        latex      "%%"    ""' ])
641     #
642     checkProg('an OpenDocument -> LaTeX converter', ['w2l -clean $$i'],
643         rc_entry = [ r'\converter odt        latex      "%%"    ""' ])
644     # According to http://www.tug.org/applications/tex4ht/mn-commands.html
645     # the command mk4ht oolatex $$i has to be used as default,
646     # but as this would require to have Perl installed, in MiKTeX oolatex is
647     # directly available as application.
648     # On SuSE the scripts have a .sh suffix, and on debian they are in /usr/share/tex4ht/
649     # Both SuSE and debian have oolatex
650     checkProg('a LaTeX -> Open Document converter', [
651         'oolatex $$i', 'mk4ht oolatex $$i', 'oolatex.sh $$i', '/usr/share/tex4ht/oolatex $$i',
652         'htlatex $$i \'xhtml,ooffice\' \'ooffice/! -cmozhtf\' \'-coo\' \'-cvalidate\''],
653         rc_entry = [ r'\converter latex      odt        "%%"    "needaux"' ])
654     # On windows it is called latex2rt.exe
655     checkProg('a LaTeX -> RTF converter', ['latex2rtf -p -S -o $$o $$i', 'latex2rt -p -S -o $$o $$i'],
656         rc_entry = [ r'\converter latex      rtf        "%%"    "needaux"' ])
657     #
658     checkProg('a RTF -> HTML converter', ['unrtf --html  $$i > $$o'],
659         rc_entry = [ r'\converter rtf      html        "%%"     ""' ])
660     #
661     checkProg('a PS to PDF converter', ['ps2pdf13 $$i $$o'],
662         rc_entry = [ r'\converter ps         pdf        "%%"    ""' ])
663     #
664     checkProg('a PS to TXT converter', ['pstotext $$i > $$o'],
665         rc_entry = [ r'\converter ps         text2      "%%"    ""' ])
666     #
667     checkProg('a PS to TXT converter', ['ps2ascii $$i $$o'],
668         rc_entry = [ r'\converter ps         text3      "%%"    ""' ])
669     #
670     checkProg('a PS to EPS converter', ['ps2eps $$i'],
671         rc_entry = [ r'\converter ps         eps      "%%"      ""' ])
672     #
673     checkProg('a PDF to PS converter', ['pdf2ps $$i $$o', 'pdftops $$i $$o'],
674         rc_entry = [ r'\converter pdf         ps        "%%"    ""' ])
675     #
676     checkProg('a PDF to EPS converter', ['pdftops -eps -f 1 -l 1 $$i $$o'],
677         rc_entry = [ r'\converter pdf         eps        "%%"   ""' ])
678     #
679     checkProg('a DVI to TXT converter', ['catdvi $$i > $$o'],
680         rc_entry = [ r'\converter dvi        text4      "%%"    ""' ])
681     #
682     checkProg('a DVI to PS converter', ['dvips -o $$o $$i'],
683         rc_entry = [ r'\converter dvi        ps         "%%"    ""' ])
684     #
685     checkProg('a DVI to PDF converter', ['dvipdfmx -o $$o $$i', 'dvipdfm -o $$o $$i'],
686         rc_entry = [ r'\converter dvi        pdf3       "%%"    ""' ])
687     #
688     path, dvipng = checkProg('dvipng', ['dvipng'])
689     if dvipng == "dvipng":
690         addToRC(r'\converter lyxpreview png        "python -tt $$s/scripts/lyxpreview2bitmap.py"        ""')
691     else:
692         addToRC(r'\converter lyxpreview png        ""   ""')
693     #  
694     checkProg('a fax program', ['kdeprintfax $$i', 'ksendfax $$i', 'hylapex $$i'],
695         rc_entry = [ r'\converter ps         fax        "%%"    ""'])
696     #
697     checkProg('a FIG -> EPS/PPM converter', ['fig2dev'],
698         rc_entry = [
699             r'''\converter fig        eps        "fig2dev -L eps $$i $$o"       ""
700 \converter fig        ppm        "fig2dev -L ppm $$i $$o"       ""
701 \converter fig        png        "fig2dev -L png $$i $$o"       ""''',
702             ''])
703     #
704     checkProg('a TIFF -> PS converter', ['tiff2ps $$i > $$o'],
705         rc_entry = [ r'\converter tiff       eps        "%%"    ""', ''])
706     #
707     checkProg('a TGIF -> EPS/PPM converter', ['tgif'],
708         rc_entry = [
709             r'''\converter tgif       eps        "tgif -print -color -eps -stdout $$i > $$o"    ""
710 \converter tgif       png        "tgif -print -color -png -o $$d $$i"   ""
711 \converter tgif       pdf        "tgif -print -color -pdf -stdout $$i > $$o"    ""''',
712             ''])
713     #
714     checkProg('a WMF -> EPS converter', ['metafile2eps $$i $$o', 'wmf2eps -o $$o $$i'],
715         rc_entry = [ r'\converter wmf        eps        "%%"    ""'])
716     #
717     checkProg('an EMF -> EPS converter', ['metafile2eps $$i $$o', 'wmf2eps -o $$o $$i'],
718         rc_entry = [ r'\converter emf        eps        "%%"    ""'])
719     #
720     checkProg('an EPS -> PDF converter', ['epstopdf'],
721         rc_entry = [ r'\converter eps        pdf        "epstopdf --outfile=$$o $$i"    ""', ''])
722     #
723     # no agr -> pdf converter, since the pdf library used by gracebat is not
724     # free software and therefore not compiled in in many installations.
725     # Fortunately, this is not a big problem, because we will use epstopdf to
726     # convert from agr to pdf via eps without loss of quality.
727     checkProg('a Grace -> Image converter', ['gracebat'],
728         rc_entry = [
729             r'''\converter agr        eps        "gracebat -hardcopy -printfile $$o -hdevice EPS $$i 2>/dev/null"       ""
730 \converter agr        png        "gracebat -hardcopy -printfile $$o -hdevice PNG $$i 2>/dev/null"       ""
731 \converter agr        jpg        "gracebat -hardcopy -printfile $$o -hdevice JPEG $$i 2>/dev/null"      ""
732 \converter agr        ppm        "gracebat -hardcopy -printfile $$o -hdevice PNM $$i 2>/dev/null"       ""''',
733             ''])
734     #
735     checkProg('a Dot -> PDF converter', ['dot -Tpdf $$i -o $$o'],
736         rc_entry = [ r'\converter dot        pdf        "%%"    ""'])
737     #
738     checkProg('a Dia -> PNG converter', ['dia -e $$o -t png $$i'],
739         rc_entry = [ r'\converter dia        png        "%%"    ""'])
740     #
741     checkProg('a Dia -> EPS converter', ['dia -e $$o -t eps $$i'],
742         rc_entry = [ r'\converter dia        eps        "%%"    ""'])
743     #
744     #
745     path, lilypond = checkProg('a LilyPond -> EPS/PDF/PNG converter', ['lilypond'])
746     if (lilypond != ''):
747         version_string = cmdOutput("lilypond --version")
748         match = re.match('GNU LilyPond (\S+)', version_string)
749         if match:
750             version_number = match.groups()[0]
751             version = version_number.split('.')
752             if int(version[0]) > 2 or (len(version) > 1 and int(version[0]) == 2 and int(version[1]) >= 11):
753                 addToRC(r'''\converter lilypond   eps        "lilypond -dbackend=eps --ps $$i"  ""
754 \converter lilypond   png        "lilypond -dbackend=eps --png $$i"     ""''')
755                 addToRC(r'\converter lilypond   pdf        "lilypond -dbackend=eps --pdf $$i"   ""')
756                 print '+  found LilyPond version %s.' % version_number
757             elif int(version[0]) > 2 or (len(version) > 1 and int(version[0]) == 2 and int(version[1]) >= 6):
758                 addToRC(r'''\converter lilypond   eps        "lilypond -b eps --ps $$i" ""
759 \converter lilypond   png        "lilypond -b eps --png $$i"    ""''')
760                 if int(version[0]) > 2 or (len(version) > 1 and int(version[0]) == 2 and int(version[1]) >= 9):
761                     addToRC(r'\converter lilypond   pdf        "lilypond -b eps --pdf $$i"      ""')
762                 logger.info('+  found LilyPond version %s.' % version_number)
763             else:
764                 logger.info('+  found LilyPond, but version %s is too old.' % version_number)
765         else:
766             logger.info('+  found LilyPond, but could not extract version number.')
767     #
768     checkProg('a Noteedit -> LilyPond converter', ['noteedit --export-lilypond $$i'],
769         rc_entry = [ r'\converter noteedit   lilypond   "%%"    ""', ''])
770     #
771     # FIXME: no rc_entry? comment it out
772     # checkProg('Image converter', ['convert $$i $$o'])
773     #
774     # Entries that do not need checkProg
775     addToRC(r'''\converter lyxpreview ppm        "python -tt $$s/scripts/lyxpreview2bitmap.py"  ""
776 \converter lyxpreview-platex ppm        "python -tt $$s/scripts/lyxpreview-platex2bitmap.py"    ""
777 \converter csv        lyx        "python -tt $$s/scripts/csv2lyx.py $$i $$o"    ""
778 \converter date       dateout    "python -tt $$s/scripts/date.py %d-%m-%Y > $$o"        ""
779 \converter docbook    docbook-xml "cp $$i $$o"  "xml"
780 \converter fen        asciichess "python -tt $$s/scripts/fen2ascii.py $$i $$o"  ""
781 \converter fig        pdftex     "python -tt $$s/scripts/fig2pdftex.py $$i $$o" ""
782 \converter fig        pstex      "python -tt $$s/scripts/fig2pstex.py $$i $$o"  ""
783 \converter lyx        lyx13x     "python -tt $$s/lyx2lyx/lyx2lyx -t 221 $$i > $$o"      ""
784 \converter lyx        lyx14x     "python -tt $$s/lyx2lyx/lyx2lyx -t 245 $$i > $$o"      ""
785 \converter lyx        lyx15x     "python -tt $$s/lyx2lyx/lyx2lyx -t 276 $$i > $$o"      ""
786 \converter lyx        lyx16x     "python -tt $$s/lyx2lyx/lyx2lyx -t 345 $$i > $$o"      ""
787 \converter lyx        clyx       "python -tt $$s/lyx2lyx/lyx2lyx -c big5 -t 245 $$i > $$o"      ""
788 \converter lyx        jlyx       "python -tt $$s/lyx2lyx/lyx2lyx -c euc_jp -t 245 $$i > $$o"    ""
789 \converter lyx        klyx       "python -tt $$s/lyx2lyx/lyx2lyx -c euc_kr -t 245 $$i > $$o"    ""
790 \converter clyx       lyx        "python -tt $$s/lyx2lyx/lyx2lyx -c big5 $$i > $$o"     ""
791 \converter jlyx       lyx        "python -tt $$s/lyx2lyx/lyx2lyx -c euc_jp $$i > $$o"   ""
792 \converter klyx       lyx        "python -tt $$s/lyx2lyx/lyx2lyx -c euc_kr $$i > $$o"   ""
793 ''')
794
795
796 def checkDocBook():
797     ''' Check docbook '''
798     path, DOCBOOK = checkProg('SGML-tools 2.x (DocBook), db2x scripts or xsltproc', ['sgmltools', 'db2dvi', 'xsltproc'],
799         rc_entry = [
800             r'''\converter docbook    dvi        "sgmltools -b dvi $$i" ""
801 \converter docbook    html       "sgmltools -b html $$i"        ""''',
802             r'''\converter docbook    dvi        "db2dvi $$i"   ""
803 \converter docbook    html       "db2html $$i"  ""''',
804             r'''\converter docbook    dvi        ""     ""
805 \converter docbook    html       "" ""''',
806             r'''\converter docbook    dvi        ""     ""
807 \converter docbook    html       ""     ""'''])
808     #
809     if DOCBOOK != '':
810         return ('yes', 'true', '\\def\\hasdocbook{yes}')
811     else:
812         return ('no', 'false', '')
813
814
815 def checkOtherEntries():
816     ''' entries other than Format and Converter '''
817     checkProg('ChkTeX', ['chktex -n1 -n3 -n6 -n9 -n22 -n25 -n30 -n38'],
818         rc_entry = [ r'\chktex_command "%%"' ])
819     checkProgAlternatives('BibTeX or alternative programs', ['bibtex', 'bibtex8', 'biber'],
820         rc_entry = [ r'\bibtex_command "%%"' ],
821         alt_rc_entry = [ r'\bibtex_alternatives "%%"' ])
822     checkProg('JBibTeX, the Japanese BibTeX', ['jbibtex', 'bibtex'],
823         rc_entry = [ r'\jbibtex_command "%%"' ])
824     checkProgAlternatives('available index processors', ['texindy', 'makeindex -c -q'],
825         rc_entry = [ r'\index_command "%%"' ],
826         alt_rc_entry = [ r'\index_alternatives "%%"' ])
827     checkProg('an index processor appropriate to Japanese', ['mendex -c -q', 'makeindex -c -q'],
828         rc_entry = [ r'\jindex_command "%%"' ])
829     path, splitindex = checkProg('the splitindex processor', ['splitindex.pl', 'splitindex'],
830         rc_entry = [ r'\splitindex_command "%%"' ])
831     if splitindex == '':
832         checkProg('the splitindex processor (java version)', ['splitindex.class'],
833             rc_entry = [ r'\splitindex_command "java splitindex"' ])
834     checkProg('a nomenclature processor', ['makeindex'],
835         rc_entry = [ r'\nomencl_command "makeindex -s nomencl.ist"' ])
836     ## FIXME: OCTAVE is not used anywhere
837     # path, OCTAVE = checkProg('Octave', ['octave'])
838     ## FIXME: MAPLE is not used anywhere
839     # path, MAPLE = checkProg('Maple', ['maple'])
840     checkProg('a spool command', ['lp', 'lpr'],
841         rc_entry = [
842             r'''\print_spool_printerprefix "-d "
843 \print_spool_command "lp"''',
844             r'''\print_spool_printerprefix "-P",
845 \print_spool_command "lpr"''',
846             ''])
847     # Add the rest of the entries (no checkProg is required)
848     addToRC(r'''\copier    fig        "python -tt $$s/scripts/fig_copy.py $$i $$o"
849 \copier    pstex      "python -tt $$s/scripts/tex_copy.py $$i $$o $$l"
850 \copier    pdftex     "python -tt $$s/scripts/tex_copy.py $$i $$o $$l"
851 \copier    program    "python -tt $$s/scripts/ext_copy.py $$i $$o"
852 ''')
853
854
855 def processLayoutFile(file, bool_docbook):
856     ''' process layout file and get a line of result
857         
858         Declare lines look like this: (article.layout, scrbook.layout, svjog.layout)
859         
860         \DeclareLaTeXClass{article}
861         \DeclareLaTeXClass[scrbook]{book (koma-script)}
862         \DeclareLaTeXClass[svjour,svjog.clo]{article (Springer - svjour/jog)}
863
864         we expect output:
865         
866         "article" "article" "article" "false"
867         "scrbook" "scrbook" "book (koma-script)" "false"
868         "svjog" "svjour" "article (Springer - svjour/jog)" "false"
869     '''
870     classname = file.split(os.sep)[-1].split('.')[0]
871     # return ('LaTeX', '[a,b]', 'a', ',b,c', 'article') for \DeclareLaTeXClass[a,b,c]{article}
872     p = re.compile(r'\Declare(LaTeX|DocBook)Class\s*(\[([^,]*)(,.*)*\])*\s*{(.*)}')
873     for line in open(file).readlines():
874         res = p.search(line)
875         if res != None:
876             (classtype, optAll, opt, opt1, desc) = res.groups()
877             avai = {'LaTeX':'false', 'DocBook':bool_docbook}[classtype]
878             if opt == None:
879                 opt = classname
880             return '"%s" "%s" "%s" "%s"\n' % (classname, opt, desc, avai)
881     logger.warning("Layout file " + file + " has no \DeclareXXClass line. ")
882     return ""
883
884
885 def checkLatexConfig(check_config, bool_docbook):
886     ''' Explore the LaTeX configuration 
887         Return None (will be passed to sys.exit()) for success.
888     '''
889     msg = 'checking LaTeX configuration... '
890     # if --without-latex-config is forced, or if there is no previous 
891     # version of textclass.lst, re-generate a default file.
892     if not os.path.isfile('textclass.lst') or not check_config:
893         # remove the files only if we want to regenerate
894         removeFiles(['textclass.lst', 'packages.lst'])
895         #
896         # Then, generate a default textclass.lst. In case configure.py
897         # fails, we still have something to start lyx.
898         logger.info(msg + ' default values')
899         logger.info('+checking list of textclasses... ')
900         tx = open('textclass.lst', 'w')
901         tx.write('''
902 # This file declares layouts and their associated definition files
903 # (include dir. relative to the place where this file is).
904 # It contains only default values, since chkconfig.ltx could not be run
905 # for some reason. Run ./configure.py if you need to update it after a
906 # configuration change.
907 ''')
908         # build the list of available layout files and convert it to commands
909         # for chkconfig.ltx
910         foundClasses = []
911         for file in glob.glob( os.path.join('layouts', '*.layout') ) + \
912             glob.glob( os.path.join(srcdir, 'layouts', '*.layout' ) ) :
913             # valid file?
914             if not os.path.isfile(file): 
915                 continue
916             # get stuff between /xxxx.layout .
917             classname = file.split(os.sep)[-1].split('.')[0]
918             #  tr ' -' '__'`
919             cleanclass = classname.replace(' ', '_')
920             cleanclass = cleanclass.replace('-', '_')
921             # make sure the same class is not considered twice
922             if foundClasses.count(cleanclass) == 0: # not found before
923                 foundClasses.append(cleanclass)
924                 retval = processLayoutFile(file, bool_docbook)
925                 if retval != "":
926                     tx.write(retval)
927         tx.close()
928         logger.info('\tdone')
929     if not check_config:
930         return None
931     # the following will generate textclass.lst.tmp, and packages.lst.tmp
932     else:
933         logger.info(msg + '\tauto')
934         removeFiles(['wrap_chkconfig.ltx', 'chkconfig.vars', \
935             'chkconfig.classes', 'chklayouts.tex'])
936         rmcopy = False
937         if not os.path.isfile( 'chkconfig.ltx' ):
938             shutil.copyfile( os.path.join(srcdir, 'chkconfig.ltx'), 'chkconfig.ltx' )
939             rmcopy = True
940         writeToFile('wrap_chkconfig.ltx', '%s\n\\input{chkconfig.ltx}\n' % docbook_cmd)
941         # Construct the list of classes to test for.
942         # build the list of available layout files and convert it to commands
943         # for chkconfig.ltx
944         p1 = re.compile(r'\Declare(LaTeX|DocBook)Class')
945         testclasses = list()
946         for file in glob.glob( os.path.join('layouts', '*.layout') ) + \
947             glob.glob( os.path.join(srcdir, 'layouts', '*.layout' ) ) :
948             if not os.path.isfile(file):
949                 continue
950             classname = file.split(os.sep)[-1].split('.')[0]
951             for line in open(file).readlines():
952                 if p1.search(line) == None:
953                     continue
954                 if line[0] != '#':
955                     logger.error("Wrong input layout file with line '" + line)
956                     sys.exit(3)
957                 testclasses.append("\\TestDocClass{%s}{%s}" % (classname, line[1:].strip()))
958                 break
959         testclasses.sort()
960         cl = open('chklayouts.tex', 'w')
961         for line in testclasses:
962             cl.write(line + '\n')
963         cl.close()
964         #
965         # we have chklayouts.tex, then process it
966         fout = os.popen(LATEX + ' wrap_chkconfig.ltx')
967         while True:
968             line = fout.readline()
969             if not line:
970                 break;
971             if re.match('^\+', line):
972                 logger.info(line.strip())
973         # if the command succeeds, None will be returned
974         ret = fout.close()
975         #
976         # currently, values in chhkconfig are only used to set
977         # \font_encoding
978         values = {}
979         for line in open('chkconfig.vars').readlines():
980             key, val = re.sub('-', '_', line).split('=')
981             val = val.strip()
982             values[key] = val.strip("'")
983         # chk_fontenc may not exist 
984         try:
985             addToRC(r'\font_encoding "%s"' % values["chk_fontenc"])
986         except:
987             pass
988         if rmcopy:   # remove the copied file
989             removeFiles( [ 'chkconfig.ltx' ] )
990         # if configure successed, move textclass.lst.tmp to textclass.lst
991         # and packages.lst.tmp to packages.lst
992         if os.path.isfile('textclass.lst.tmp') and len(open('textclass.lst.tmp').read()) > 0 \
993             and os.path.isfile('packages.lst.tmp') and len(open('packages.lst.tmp').read()) > 0:
994             shutil.move('textclass.lst.tmp', 'textclass.lst')
995             shutil.move('packages.lst.tmp', 'packages.lst')
996         return ret
997
998
999 def checkModulesConfig():
1000   removeFiles(['lyxmodules.lst'])
1001
1002   logger.info('+checking list of modules... ')
1003   tx = open('lyxmodules.lst', 'w')
1004   tx.write('''## This file declares modules and their associated definition files.
1005 ## It has been automatically generated by configure
1006 ## Use "Options/Reconfigure" if you need to update it after a
1007 ## configuration change. 
1008 ## "ModuleName" "filename" "Description" "Packages" "Requires" "Excludes" "Category"
1009 ''')
1010   # build the list of available modules
1011   foundClasses = []
1012   for file in glob.glob( os.path.join('layouts', '*.module') ) + \
1013       glob.glob( os.path.join(srcdir, 'layouts', '*.module' ) ) :
1014       # valid file?
1015       logger.info(file)
1016       if not os.path.isfile(file): 
1017           continue
1018       retval = processModuleFile(file, bool_docbook)
1019       if retval != "":
1020           tx.write(retval)
1021   tx.close()
1022   logger.info('\tdone')
1023
1024
1025 def processModuleFile(file, bool_docbook):
1026     ''' process module file and get a line of result
1027
1028         The top of a module file should look like this:
1029           #\DeclareLyXModule[LaTeX Packages]{ModuleName}
1030           #DescriptionBegin
1031           #...body of description...
1032           #DescriptionEnd
1033           #Requires: [list of required modules]
1034           #Excludes: [list of excluded modules]
1035           #Category: [category name]
1036         The last three lines are optional (though do give a category).
1037         We expect output:
1038           "ModuleName" "filename" "Description" "Packages" "Requires" "Excludes" "Category"
1039     '''
1040     p = re.compile(r'\DeclareLyXModule\s*(?:\[([^]]*?)\])?{(.*)}')
1041     r = re.compile(r'#+\s*Requires: (.*)')
1042     x = re.compile(r'#+\s*Excludes: (.*)')
1043     c = re.compile(r'#+\s*Category: (.*)')
1044     b = re.compile(r'#+\s*DescriptionBegin\s*$')
1045     e = re.compile(r'#+\s*DescriptionEnd\s*$')
1046
1047     modname = desc = pkgs = req = excl = catgy = ""
1048     readingDescription = False
1049     descLines = []
1050     filename = file.split(os.sep)[-1]
1051     filename = filename[:-7]
1052
1053     for line in open(file).readlines():
1054       if readingDescription:
1055         res = e.search(line)
1056         if res != None:
1057           readingDescription = False
1058           desc = " ".join(descLines)
1059           continue
1060         descLines.append(line[1:].strip())
1061         continue
1062       res = b.search(line)
1063       if res != None:
1064         readingDescription = True
1065         continue
1066       res = p.search(line)
1067       if res != None:
1068           (pkgs, modname) = res.groups()
1069           if pkgs == None:
1070             pkgs = ""
1071           else:
1072             tmp = [s.strip() for s in pkgs.split(",")]
1073             pkgs = ",".join(tmp)
1074           continue
1075       res = r.search(line)
1076       if res != None:
1077         req = res.group(1)
1078         tmp = [s.strip() for s in req.split("|")]
1079         req = "|".join(tmp)
1080         continue
1081       res = x.search(line)
1082       if res != None:
1083         excl = res.group(1)
1084         tmp = [s.strip() for s in excl.split("|")]
1085         excl = "|".join(tmp)
1086         continue
1087       res = c.search(line)
1088       if res != None:
1089         catgy = res.group(1)
1090         continue
1091     if modname != "":
1092         return '"%s" "%s" "%s" "%s" "%s" "%s" "%s"\n' % (modname, filename, desc, pkgs, req, excl, catgy)
1093     logger.warning("Module file without \DeclareLyXModule line. ")
1094     return ""
1095
1096
1097 def checkTeXAllowSpaces():
1098     ''' Let's check whether spaces are allowed in TeX file names '''
1099     tex_allows_spaces = 'false'
1100     if lyx_check_config:
1101         msg = "Checking whether TeX allows spaces in file names... "
1102         writeToFile('a b.tex', r'\message{working^^J}' )
1103         if os.name == 'nt':
1104             latex_out = cmdOutput(LATEX + r""" "\nonstopmode\input{\"a b\"}" """)
1105         else:
1106             latex_out = cmdOutput(LATEX + r""" '\nonstopmode\input{"a b"}' """)
1107         if 'working' in latex_out:
1108             logger.info(msg + 'yes')
1109             tex_allows_spaces = 'true'
1110         else:
1111             logger.info(msg + 'no')
1112             tex_allows_spaces = 'false'
1113         addToRC(r'\tex_allows_spaces ' + tex_allows_spaces)
1114         removeFiles( [ 'a b.tex', 'a b.log', 'texput.log' ])
1115
1116
1117 def removeTempFiles():
1118     # Final clean-up
1119     if not lyx_keep_temps:
1120         removeFiles(['chkconfig.vars',  \
1121             'wrap_chkconfig.ltx', 'wrap_chkconfig.log', \
1122             'chklayouts.tex', 'missfont.log', 
1123             'chklatex.ltx', 'chklatex.log'])
1124
1125
1126 if __name__ == '__main__':
1127     lyx_check_config = True
1128     outfile = 'lyxrc.defaults'
1129     rc_entries = ''
1130     lyx_keep_temps = False
1131     version_suffix = ''
1132     ## Parse the command line
1133     for op in sys.argv[1:]:   # default shell/for list is $*, the options
1134         if op in [ '-help', '--help', '-h' ]:
1135             print '''Usage: configure [options]
1136 Options:
1137     --help                   show this help lines
1138     --keep-temps             keep temporary files (for debug. purposes)
1139     --without-latex-config   do not run LaTeX to determine configuration
1140     --with-version-suffix=suffix suffix of binary installed files
1141 '''
1142             sys.exit(0)
1143         elif op == '--without-latex-config':
1144             lyx_check_config = False
1145         elif op == '--keep-temps':
1146             lyx_keep_temps = True
1147         elif op[0:22] == '--with-version-suffix=':  # never mind if op is not long enough
1148             version_suffix = op[22:]
1149         else:
1150             print "Unknown option", op
1151             sys.exit(1)
1152     #
1153     # check if we run from the right directory
1154     srcdir = os.path.dirname(sys.argv[0])
1155     if srcdir == '':
1156         srcdir = '.'
1157     if not os.path.isfile( os.path.join(srcdir, 'chkconfig.ltx') ):
1158         logger.error("configure: error: cannot find chkconfig.ltx script")
1159         sys.exit(1)
1160     setEnviron()
1161     createDirectories()
1162     windows_style_tex_paths = checkTeXPaths()
1163     dtl_tools = checkDTLtools()
1164     ## Write the first part of outfile
1165     writeToFile(outfile, '''# This file has been automatically generated by LyX' lib/configure.py
1166 # script. It contains default settings that have been determined by
1167 # examining your system. PLEASE DO NOT MODIFY ANYTHING HERE! If you
1168 # want to customize LyX, use LyX' Preferences dialog or modify directly 
1169 # the "preferences" file instead. Any setting in that file will
1170 # override the values given here.
1171 ''')
1172     # check latex
1173     LATEX = checkLatex(dtl_tools)
1174     checkFormatEntries(dtl_tools)
1175     checkConverterEntries()
1176     (chk_docbook, bool_docbook, docbook_cmd) = checkDocBook()
1177     checkTeXAllowSpaces()
1178     if windows_style_tex_paths != '':
1179         addToRC(r'\tex_expects_windows_paths %s' % windows_style_tex_paths)
1180     checkOtherEntries()
1181     # --without-latex-config can disable lyx_check_config
1182     ret = checkLatexConfig(lyx_check_config and LATEX != '', bool_docbook)
1183     checkModulesConfig() #lyx_check_config and LATEX != '')
1184     removeTempFiles()
1185     # The return error code can be 256. Because most systems expect an error code
1186     # in the range 0-127, 256 can be interpretted as 'success'. Because we expect
1187     # a None for success, 'ret is not None' is used to exit.
1188     sys.exit(ret is not None)