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