]> git.lyx.org Git - lyx.git/blob - lib/configure.py
- Spanish documentation updates by Ignacio
[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
12
13
14 class Tee:
15     ''' Writing to a Tee object will write to all file objects it keeps.
16         That is to say, writing to Tee(sys.stdout, open(logfile, 'w')) will
17         write to sys.stdout as well as a log file.
18     '''
19     def __init__(self, *args):
20         self.files = args
21
22     def write(self, data):
23         for f in self.files:
24             result = f.write(data)
25         return result
26
27     def writelines(self, seq):
28         for i in seq:
29             self.write(i)
30
31
32 def writeToFile(filename, lines, append = False):
33     " utility function: write or append lines to filename "
34     if append:
35         file = open(filename, 'a')
36     else:
37         file = open(filename, 'w')
38     file.write(lines)
39     file.close()
40
41
42 def addToRC(lines):
43     ''' utility function: shortcut for appending lines to outfile
44         add newline at the end of lines.
45     '''
46     if lines.strip() != '':
47         writeToFile(outfile, lines + '\n', append = True)
48
49
50 def removeFiles(filenames):
51     '''utility function: 'rm -f'
52         ignore errors when file does not exist, or is a directory.
53     '''
54     for file in filenames:
55         try:
56             os.remove(file)
57         except:
58             pass
59
60
61 def cmdOutput(cmd):
62     '''utility function: run a command and get its output as a string
63         cmd: command to run
64     '''
65     fout = os.popen(cmd)
66     output = fout.read()
67     fout.close()
68     return output.strip()
69
70
71 def setEnviron():
72     ''' I do not really know why this is useful, but we might as well keep it.
73         NLS nuisances.
74         Only set these to C if already set.  These must not be set unconditionally
75         because not all systems understand e.g. LANG=C (notably SCO).
76         Fixing LC_MESSAGES prevents Solaris sh from translating var values in `set'!
77         Non-C LC_CTYPE values break the ctype check.
78     '''
79     os.environ['LANG'] = os.getenv('LANG', 'C')
80     os.environ['LC'] = os.getenv('LC_ALL', 'C')
81     os.environ['LC_MESSAGE'] = os.getenv('LC_MESSAGE', 'C')
82     os.environ['LC_CTYPE'] = os.getenv('LC_CTYPE', 'C')
83
84
85 def createDirectories():
86     ''' Create the build directories if necessary '''
87     for dir in ['bind', 'clipart', 'doc', 'examples', 'images', 'kbd', \
88         'layouts', 'scripts', 'templates', 'ui' ]:
89         if not os.path.isdir( dir ):
90             try:
91                 os.mkdir( dir)
92             except:
93                 print "Failed to create directory ", dir
94                 sys.exit(1)
95
96
97 def checkTeXPaths():
98     ''' Determine the path-style needed by the TeX engine on Win32 (Cygwin) '''
99     windows_style_tex_paths = ''
100     if os.name == 'nt' or sys.platform == 'cygwin':
101         from tempfile import mkstemp
102         fd, tmpfname = mkstemp(suffix='.ltx')
103         if os.name == 'nt':
104             inpname = tmpfname.replace('\\', '/')
105         else:
106             inpname = cmdOutput('cygpath -m ' + tmpfname)
107         logname = os.path.basename(inpname.replace('.ltx', '.log'))
108         inpname = inpname.replace('~', '\\string~')
109         os.write(fd, r'\relax')
110         os.close(fd)
111         latex_out = cmdOutput(r'latex "\nonstopmode\input{%s}"' % inpname)
112         if 'Error' in latex_out:
113             print "configure: TeX engine needs posix-style paths in latex files"
114             windows_style_tex_paths = 'false'
115         else:
116             print "configure: TeX engine needs windows-style paths in latex files"
117             windows_style_tex_paths = 'true'
118         removeFiles([tmpfname, logname, 'texput.log'])
119     return windows_style_tex_paths
120
121
122 ## Searching some useful programs
123 def checkProg(description, progs, rc_entry = [], path = [], not_found = ''):
124     '''
125         This function will search a program in $PATH plus given path
126         If found, return directory and program name (not the options).
127
128         description: description of the program
129
130         progs: check programs, for each prog, the first word is used
131             for searching but the whole string is used to replace
132             %% for a rc_entry. So, feel free to add '$$i' etc for programs.
133
134         path: additional pathes
135
136         rc_entry: entry to outfile, can be
137             1. emtpy: no rc entry will be added
138             2. one pattern: %% will be replaced by the first found program,
139                 or '' if no program is found.
140             3. several patterns for each prog and not_found. This is used 
141                 when different programs have different usages. If you do not 
142                 want not_found entry to be added to the RC file, you can specify 
143                 an entry for each prog and use '' for the not_found entry.
144
145         not_found: the value that should be used instead of '' if no program
146             was found
147
148     '''
149     # one rc entry for each progs plus not_found entry
150     if len(rc_entry) > 1 and len(rc_entry) != len(progs) + 1:
151         print "rc entry should have one item or item for each prog and not_found."
152         sys.exit(2)
153     print 'checking for ' + description + '...'
154     ## print '(' + ','.join(progs) + ')',
155     for idx in range(len(progs)):
156         # ac_prog may have options, ac_word is the command name
157         ac_prog = progs[idx]
158         ac_word = ac_prog.split(' ')[0]
159         print '+checking for "' + ac_word + '"... ',
160         path = os.environ["PATH"].split(os.pathsep) + path
161         extlist = ['']
162         if os.environ.has_key("PATHEXT"):
163             extlist = extlist + os.environ["PATHEXT"].split(os.pathsep)
164         for ac_dir in path:
165             for ext in extlist:
166                 if os.path.isfile( os.path.join(ac_dir, ac_word + ext) ):
167                     print ' yes'
168                     # write rc entries for this command
169                     if len(rc_entry) == 1:
170                         addToRC(rc_entry[0].replace('%%', ac_prog))
171                     elif len(rc_entry) > 1:
172                         addToRC(rc_entry[idx].replace('%%', ac_prog))
173                     return [ac_dir, ac_word]
174         # if not successful
175         print ' no'
176     # write rc entries for 'not found'
177     if len(rc_entry) > 0:  # the last one.
178         addToRC(rc_entry[-1].replace('%%', not_found))
179     return ['', not_found]
180
181
182 def checkViewer(description, progs, rc_entry = [], path = []):
183     ''' The same as checkProg, but for viewers and editors '''
184     return checkProg(description, progs, rc_entry, path, not_found = 'auto')
185
186
187 def checkDTLtools():
188     ''' Check whether DTL tools are available (Windows only) '''
189     # Find programs! Returned path is not used now
190     if ((os.name == 'nt' or sys.platform == 'cygwin') and
191             checkProg('DVI to DTL converter', ['dv2dt']) != ['', ''] and
192             checkProg('DTL to DVI converter', ['dt2dv']) != ['', '']):
193         dtl_tools = True
194     else:
195         dtl_tools = False
196     return dtl_tools
197
198
199 def checkLatex(dtl_tools):
200     ''' Check latex, return lyx_check_config '''
201     path, LATEX = checkProg('a Latex2e program', ['latex $$i', 'platex $$i', 'latex2e $$i'])
202     path, PPLATEX = checkProg('a DVI postprocessing program', ['pplatex $$i'])
203     # use LATEX to convert from latex to dvi if PPLATEX is not available    
204     if PPLATEX == '':
205         PPLATEX = LATEX
206     if dtl_tools:
207         # Windows only: DraftDVI
208         addToRC(r'''\converter latex      dvi2       "%s"       "latex"
209 \converter dvi2       dvi        "python -tt $$s/scripts/clean_dvi.py $$i $$o"  ""''' % PPLATEX)
210     else:
211         addToRC(r'\converter latex      dvi        "%s" "latex"' % PPLATEX)
212     # no latex
213     if LATEX != '':
214         # Check if latex is usable
215         writeToFile('chklatex.ltx', '''
216 \\nonstopmode\\makeatletter
217 \\ifx\\undefined\\documentclass\\else
218   \\message{ThisIsLaTeX2e}
219 \\fi
220 \\@@end
221 ''')
222         # run latex on chklatex.ltx and check result
223         if cmdOutput(LATEX + ' chklatex.ltx').find('ThisIsLaTeX2e') != -1:
224             # valid latex2e
225             return LATEX
226         else:
227             print "Latex not usable (not LaTeX2e) "
228         # remove temporary files
229         removeFiles(['chklatex.ltx', 'chklatex.log'])
230     return ''
231
232
233 def checkFormatEntries(dtl_tools):  
234     ''' Check all formats (\Format entries) '''
235     checkViewer('a Tgif viewer and editor', ['tgif'],
236         rc_entry = [r'\Format tgif       obj     Tgif                   "" "%%" "%%"    "vector"'])
237     #
238     checkViewer('a FIG viewer and editor', ['xfig', 'jfig3-itext.jar', 'jfig3.jar'],
239         rc_entry = [r'\Format fig        fig     FIG                    "" "%%" "%%"    "vector"'])
240     #
241     checkViewer('a Grace viewer and editor', ['xmgrace'],
242         rc_entry = [r'\Format agr        agr     Grace                  "" "%%" "%%"    "vector"'])
243     #
244     checkViewer('a FEN viewer and editor', ['xboard -lpf $$i -mode EditPosition'],
245         rc_entry = [r'\Format fen        fen     FEN                    "" "%%" "%%"    ""'])
246     #
247     path, iv = checkViewer('a raster image viewer', ['xv', 'kview', 'gimp-remote', 'gimp'])
248     path, ie = checkViewer('a raster image editor', ['gimp-remote', 'gimp'])
249     addToRC(r'''\Format bmp        bmp     BMP                    "" "%s"       "%s"    ""
250 \Format gif        gif     GIF                    "" "%s"       "%s"    ""
251 \Format jpg        jpg     JPEG                   "" "%s"       "%s"    ""
252 \Format pbm        pbm     PBM                    "" "%s"       "%s"    ""
253 \Format pgm        pgm     PGM                    "" "%s"       "%s"    ""
254 \Format png        png     PNG                    "" "%s"       "%s"    ""
255 \Format ppm        ppm     PPM                    "" "%s"       "%s"    ""
256 \Format tiff       tif     TIFF                   "" "%s"       "%s"    ""
257 \Format xbm        xbm     XBM                    "" "%s"       "%s"    ""
258 \Format xpm        xpm     XPM                    "" "%s"       "%s"    ""''' % \
259         (iv, ie, iv, ie, iv, ie, iv, ie, iv, ie, iv, ie, iv, ie, iv, ie, iv, ie, iv, ie) )
260     #
261     checkViewer('a text editor', ['sensible-editor', 'xemacs', 'gvim', 'kedit', 'kwrite', 'kate', \
262         'nedit', 'gedit', 'notepad'],
263         rc_entry = [r'''\Format asciichess asc    "Plain text (chess output)"  "" ""    "%%"    ""
264 \Format asciiimage asc    "Plain text (image)"         "" ""    "%%"    ""
265 \Format asciixfig  asc    "Plain text (Xfig output)"   "" ""    "%%"    ""
266 \Format dateout    tmp    "date (output)"         "" "" "%%"    ""
267 \Format docbook    sgml    DocBook                B  "" "%%"    "document"
268 \Format docbook-xml xml   "Docbook (XML)"         "" "" "%%"    "document"
269 \Format dot        dot    "Graphviz Dot"          "" "" "%%"    "vector"
270 \Format literate   nw      NoWeb                  N  "" "%%"    "document"
271 \Format lilypond   ly     "LilyPond music"        "" "" "%%"    "vector"
272 \Format latex      tex    "LaTeX (plain)"         L  "" "%%"    "document"
273 \Format linuxdoc   sgml    LinuxDoc               x  "" "%%"    "document"
274 \Format pdflatex   tex    "LaTeX (pdflatex)"      "" "" "%%"    "document"
275 \Format text       txt    "Plain text"            a  "" "%%"    "document"
276 \Format text2      txt    "Plain text (pstotext)" "" "" "%%"    "document"
277 \Format text3      txt    "Plain text (ps2ascii)" "" "" "%%"    "document"
278 \Format text4      txt    "Plain text (catdvi)"   "" "" "%%"    "document"
279 \Format textparagraph txt "Plain Text, Join Lines" "" ""        "%%"    "document"''' ])
280  #
281     checkViewer('a BibTeX editor', ['sensible-editor', 'jabref', 'JabRef', \
282         'pybliographic', 'bibdesk', 'gbib', 'kbib', \
283         'kbibtex', 'sixpack', 'bibedit', 'tkbibtex' \
284         'xemacs', 'gvim', 'kedit', 'kwrite', 'kate', \
285         'nedit', 'gedit', 'notepad'],
286         rc_entry = [r'''\Format bibtex bib    "BibTeX"         "" ""    "%%"    ""''' ])
287     #
288     #checkProg('a Postscript interpreter', ['gs'],
289     #  rc_entry = [ r'\ps_command "%%"' ])
290     checkViewer('a Postscript previewer', ['kghostview', 'evince', 'gv', 'ghostview -swap'],
291         rc_entry = [r'''\Format eps        eps     EPS                    "" "%%"       ""      "vector"
292 \Format ps         ps      Postscript             t  "%%"       ""      "document,vector"'''])
293     #
294     checkViewer('a PDF previewer', ['kpdf', 'evince', 'kghostview', 'xpdf', 'acrobat', 'acroread', \
295                     'gv', 'ghostview'],
296         rc_entry = [r'''\Format pdf        pdf    "PDF (ps2pdf)"          P  "%%"       ""      "document,vector"
297 \Format pdf2       pdf    "PDF (pdflatex)"        F  "%%"       ""      "document,vector"
298 \Format pdf3       pdf    "PDF (dvipdfm)"         m  "%%"       ""      "document,vector"'''])
299     #
300     checkViewer('a DVI previewer', ['xdvi', 'kdvi'],
301         rc_entry = [r'\Format dvi        dvi     DVI                    D  "%%" ""      "document,vector"'])
302     if dtl_tools:
303         # Windows only: DraftDVI
304         addToRC(r'\Format dvi2       dvi     DraftDVI               ""  ""      ""      "vector"')
305     #
306     checkViewer('an HTML previewer', ['firefox', 'mozilla file://$$p$$i', 'netscape'],
307         rc_entry = [r'\Format html       html    HTML                   H  "%%" ""      "document"'])
308     #
309     checkViewer('Noteedit', ['noteedit'],
310         rc_entry = [r'\Format noteedit   not     Noteedit               "" "%%" "%%"    "vector"'])
311     #
312     checkViewer('an OpenDocument viewer', ['oowriter'],
313         rc_entry = [r'\Format odt        odt     OpenDocument           "" "%%" "%%"    "document,vector"'])
314     #
315     # entried that do not need checkProg
316     addToRC(r'''\Format date       ""     "date command"          "" "" ""      ""
317 \Format csv        csv    "Table (CSV)"  "" ""  ""      "document"
318 \Format fax        ""      Fax                    "" "" ""      "document"
319 \Format lyx        lyx     LyX                    "" "" ""      ""
320 \Format lyx13x     lyx13  "LyX 1.3.x"             "" "" ""      "document"
321 \Format lyx14x     lyx14  "LyX 1.4.x"             "" "" ""      "document"
322 \Format lyx15x     lyx15  "LyX 1.5.x"             "" "" ""      "document"
323 \Format clyx       cjklyx "CJK LyX 1.4.x (big5)"  "" "" ""      "document"
324 \Format jlyx       cjklyx "CJK LyX 1.4.x (euc-jp)" "" ""        ""      "document"
325 \Format klyx       cjklyx "CJK LyX 1.4.x (euc-kr)" "" ""        ""      "document"
326 \Format lyxpreview lyxpreview "LyX Preview"       "" "" ""      ""
327 \Format pdftex     pdftex_t PDFTEX                "" "" ""      ""
328 \Format program    ""      Program                "" "" ""      ""
329 \Format pstex      pstex_t PSTEX                  "" "" ""      ""
330 \Format rtf        rtf    "Rich Text Format"      "" "" ""      "document,vector"
331 \Format sxw        sxw    "OpenOffice.Org (sxw)"  ""  ""        ""      "document,vector"
332 \Format wmf        wmf    "Windows Metafile"      "" "" ""      "vector"
333 \Format emf        emf    "Enhanced Metafile"     "" "" ""      "vector"
334 \Format word       doc    "MS Word"               W  "" ""      "document,vector"
335 \Format wordhtml   html   "HTML (MS Word)"        "" ""        ""       "document"
336 ''')
337
338
339 def checkConverterEntries():
340     ''' Check all converters (\converter entries) '''
341     checkProg('the pdflatex program', ['pdflatex $$i'],
342         rc_entry = [ r'\converter pdflatex   pdf2       "%%"    "latex"' ])
343     
344     ''' If we're running LyX in-place then tex2lyx will be found in
345             ../src/tex2lyx. Add this directory to the PATH temporarily and
346             search for tex2lyx.
347             Use PATH to avoid any problems with paths-with-spaces.
348     '''
349     path_orig = os.environ["PATH"]
350     os.environ["PATH"] = os.path.join('..', 'src', 'tex2lyx') + \
351         os.pathsep + path_orig
352
353     checkProg('a LaTeX/Noweb -> LyX converter', ['tex2lyx', 'tex2lyx' + version_suffix],
354         rc_entry = [r'''\converter latex      lyx        "%% -f $$i $$o"        ""
355 \converter literate   lyx        "%% -n -f $$i $$o"     ""'''])
356
357     os.environ["PATH"] = path_orig
358
359     #
360     checkProg('a Noweb -> LaTeX converter', ['noweave -delay -index $$i > $$o'],
361         rc_entry = [ r'\converter literate   latex      "%%"    ""' ])
362     #
363     checkProg('an HTML -> LaTeX converter', ['html2latex $$i', 'gnuhtml2latex $$i', \
364         'htmltolatex -input $$i -output $$o', 'java -jar htmltolatex.jar -input $$i -output $$o'],
365         rc_entry = [ r'\converter html       latex      "%%"    ""' ])
366     #
367     checkProg('an MS Word -> LaTeX converter', ['wvCleanLatex $$i $$o'],
368         rc_entry = [ r'\converter word       latex      "%%"    ""' ])
369     # On SuSE the scripts have a .sh suffix, and on debian they are in /usr/share/tex4ht/
370     path, htmlconv = checkProg('a LaTeX -> HTML converter', ['htlatex $$i', 'htlatex.sh $$i', \
371         '/usr/share/tex4ht/htlatex $$i', 'tth  -t -e2 -L$$b < $$i > $$o', \
372         'latex2html -no_subdir -split 0 -show_section_numbers $$i', 'hevea -s $$i'],
373         rc_entry = [ r'\converter latex      html       "%%"    "needaux"' ])
374     if htmlconv.find('htlatex') >= 0 or htmlconv == 'latex2html':
375       addToRC(r'''\copier    html       "python -tt $$s/scripts/ext_copy.py -e html,png,css $$i $$o"''')
376     else:
377       addToRC(r'''\copier    html       "python -tt $$s/scripts/ext_copy.py $$i $$o"''')
378
379     # On SuSE the scripts have a .sh suffix, and on debian they are in /usr/share/tex4ht/
380     path, htmlconv = checkProg('a LaTeX -> MS Word converter', ["htlatex $$i 'html,word' 'symbol/!' '-cvalidate'", \
381         "htlatex.sh $$i 'html,word' 'symbol/!' '-cvalidate'", \
382         "/usr/share/tex4ht/htlatex $$i 'html,word' 'symbol/!' '-cvalidate'"],
383         rc_entry = [ r'\converter latex      wordhtml   "%%"    "needaux"' ])
384     if htmlconv.find('htlatex') >= 0:
385       addToRC(r'''\copier    wordhtml       "python -tt $$s/scripts/ext_copy.py -e html,png,css $$i $$o"''')
386     #
387     checkProg('an OpenOffice.org -> LaTeX converter', ['w2l -clean $$i'],
388         rc_entry = [ r'\converter sxw        latex      "%%"    ""' ])
389     #
390     checkProg('an OpenDocument -> LaTeX converter', ['w2l -clean $$i'],
391         rc_entry = [ r'\converter odt        latex      "%%"    ""' ])
392     # On SuSE the scripts have a .sh suffix, and on debian they are in /usr/share/tex4ht/
393     # Both SuSE and debian have oolatex
394     checkProg('a LaTeX -> Open Document converter', [
395         'htlatex $$i \'xhtml,ooffice\' \'ooffice/! -cmozhtf\' \'-coo\' \'-cvalidate\'', \
396         'oolatex $$i', 'oolatex.sh $$i', \
397         '/usr/share/tex4ht/oolatex $$i'],
398         rc_entry = [ r'\converter latex      odt        "%%"    "needaux"' ])
399     # On windows it is called latex2rt.exe
400     checkProg('a LaTeX -> RTF converter', ['latex2rtf -p -S -o $$o $$i', 'latex2rt -p -S -o $$o $$i'],
401         rc_entry = [ r'\converter latex      rtf        "%%"    "needaux"' ])
402     #
403     checkProg('a RTF -> HTML converter', ['unrtf --html  $$i > $$o'],
404         rc_entry = [ r'\converter rtf      html        "%%"     ""' ])
405     #
406     checkProg('a PS to PDF converter', ['ps2pdf13 $$i $$o'],
407         rc_entry = [ r'\converter ps         pdf        "%%"    ""' ])
408     #
409     checkProg('a PS to TXT converter', ['pstotext $$i > $$o'],
410         rc_entry = [ r'\converter ps         text2      "%%"    ""' ])
411     #
412     checkProg('a PS to TXT converter', ['ps2ascii $$i $$o'],
413         rc_entry = [ r'\converter ps         text3      "%%"    ""' ])
414     #
415     checkProg('a PS to EPS converter', ['ps2eps $$i'],
416         rc_entry = [ r'\converter ps         eps      "%%"      ""' ])
417     #
418     checkProg('a PDF to PS converter', ['pdf2ps $$i $$o', 'pdftops $$i $$o'],
419         rc_entry = [ r'\converter pdf         ps        "%%"    ""' ])
420     #
421     checkProg('a PDF to EPS converter', ['pdftops -eps -f 1 -l 1 $$i $$o'],
422         rc_entry = [ r'\converter pdf         eps        "%%"   ""' ])
423     #
424     checkProg('a DVI to TXT converter', ['catdvi $$i > $$o'],
425         rc_entry = [ r'\converter dvi        text4      "%%"    ""' ])
426     #
427     checkProg('a DVI to PS converter', ['dvips -o $$o $$i'],
428         rc_entry = [ r'\converter dvi        ps         "%%"    ""' ])
429     #
430     checkProg('a DVI to PDF converter', ['dvipdfmx -o $$o $$i', 'dvipdfm -o $$o $$i'],
431         rc_entry = [ r'\converter dvi        pdf3       "%%"    ""' ])
432     #
433     path, dvipng = checkProg('dvipng', ['dvipng'])
434     if dvipng == "dvipng":
435         addToRC(r'\converter lyxpreview png        "python -tt $$s/scripts/lyxpreview2bitmap.py"        ""')
436     else:
437         addToRC(r'\converter lyxpreview png        ""   ""')
438     #  
439     checkProg('a fax program', ['kdeprintfax $$i', 'ksendfax $$i'],
440         rc_entry = [ r'\converter ps         fax        "%%"    ""'])
441     #
442     checkProg('a FIG -> EPS/PPM converter', ['fig2dev'],
443         rc_entry = [
444             r'''\converter fig        eps        "fig2dev -L eps $$i $$o"       ""
445 \converter fig        ppm        "fig2dev -L ppm $$i $$o"       ""
446 \converter fig        png        "fig2dev -L png $$i $$o"       ""''',
447             ''])
448     #
449     checkProg('a TIFF -> PS converter', ['tiff2ps $$i > $$o'],
450         rc_entry = [ r'\converter tiff       eps        "%%"    ""', ''])
451     #
452     checkProg('a TGIF -> EPS/PPM converter', ['tgif'],
453         rc_entry = [
454             r'''\converter tgif       eps        "tgif -stdout -print -color -eps $$i > $$o"    ""
455 \converter tgif       ppm        "tgif -stdout -print -color -ppm $$i > $$o"    ""
456 \converter tgif       png        "tgif -stdout -print -color -png $$i > $$o"    ""
457 \converter tgif       pdf        "tgif -stdout -print -color -pdf $$i > $$o"    ""''',
458             ''])
459     #
460     checkProg('a WMF -> EPS converter', ['metafile2eps $$i $$o', 'wmf2eps -o $$o $$i'],
461         rc_entry = [ r'\converter wmf        eps        "%%"    ""'])
462     #
463     checkProg('an EMF -> EPS converter', ['metafile2eps $$i $$o', 'wmf2eps -o $$o $$i'],
464         rc_entry = [ r'\converter emf        eps        "%%"    ""'])
465     #
466     checkProg('an EPS -> PDF converter', ['epstopdf'],
467         rc_entry = [ r'\converter eps        pdf        "epstopdf --outfile=$$o $$i"    ""', ''])
468     #
469     # no agr -> pdf converter, since the pdf library used by gracebat is not
470     # free software and therefore not compiled in in many installations.
471     # Fortunately, this is not a big problem, because we will use epstopdf to
472     # convert from agr to pdf via eps without loss of quality.
473     checkProg('a Grace -> Image converter', ['gracebat'],
474         rc_entry = [
475             r'''\converter agr        eps        "gracebat -hardcopy -printfile $$o -hdevice EPS $$i 2>/dev/null"       ""
476 \converter agr        png        "gracebat -hardcopy -printfile $$o -hdevice PNG $$i 2>/dev/null"       ""
477 \converter agr        jpg        "gracebat -hardcopy -printfile $$o -hdevice JPEG $$i 2>/dev/null"      ""
478 \converter agr        ppm        "gracebat -hardcopy -printfile $$o -hdevice PNM $$i 2>/dev/null"       ""''',
479             ''])
480     #
481     checkProg('a Dot -> PDF converter', ['dot -Tpdf $$i -o $$o'],
482         rc_entry = [ r'\converter dot        pdf        "%%"    ""'])
483     #
484     #
485     path, lilypond = checkProg('a LilyPond -> EPS/PDF/PNG converter', ['lilypond'])
486     if (lilypond != ''):
487         version_string = cmdOutput("lilypond --version")
488         match = re.match('GNU LilyPond (\S+)', version_string)
489         if match:
490             version_number = match.groups()[0]
491             version = version_number.split('.')
492             if int(version[0]) > 2 or (len(version) > 1 and int(version[0]) == 2 and int(version[1]) >= 6):
493                 addToRC(r'''\converter lilypond   eps        "lilypond -b eps --ps $$i" ""
494 \converter lilypond   png        "lilypond -b eps --png $$i"    ""''')
495                 if int(version[0]) > 2 or (len(version) > 1 and int(version[0]) == 2 and int(version[1]) >= 9):
496                     addToRC(r'\converter lilypond   pdf        "lilypond -b eps --pdf $$i"      ""')
497                 print '+  found LilyPond version %s.' % version_number
498             else:
499                 print '+  found LilyPond, but version %s is too old.' % version_number
500         else:
501             print '+  found LilyPond, but could not extract version number.'
502     #
503     checkProg('a Noteedit -> LilyPond converter', ['noteedit --export-lilypond $$i'],
504         rc_entry = [ r'\converter noteedit   lilypond   "%%"    ""', ''])
505     #
506     # FIXME: no rc_entry? comment it out
507     # checkProg('Image converter', ['convert $$i $$o'])
508     #
509     # Entries that do not need checkProg
510     addToRC(r'''\converter lyxpreview ppm        "python -tt $$s/scripts/lyxpreview2bitmap.py"  ""
511 \converter csv        lyx        "python -tt $$s/scripts/csv2lyx.py $$i $$o"    ""
512 \converter date       dateout    "python -tt $$s/scripts/date.py %d-%m-%Y > $$o"        ""
513 \converter docbook    docbook-xml "cp $$i $$o"  "xml"
514 \converter fen        asciichess "python -tt $$s/scripts/fen2ascii.py $$i $$o"  ""
515 \converter fig        pdftex     "python -tt $$s/scripts/fig2pdftex.py $$i $$o" ""
516 \converter fig        pstex      "python -tt $$s/scripts/fig2pstex.py $$i $$o"  ""
517 \converter lyx        lyx13x     "python -tt $$s/lyx2lyx/lyx2lyx -t 221 $$i > $$o"      ""
518 \converter lyx        lyx14x     "python -tt $$s/lyx2lyx/lyx2lyx -t 245 $$i > $$o"      ""
519 \converter lyx        lyx15x     "python -tt $$s/lyx2lyx/lyx2lyx -t 276 $$i > $$o"      ""
520 \converter lyx        clyx       "python -tt $$s/lyx2lyx/lyx2lyx -c big5 -t 245 $$i > $$o"      ""
521 \converter lyx        jlyx       "python -tt $$s/lyx2lyx/lyx2lyx -c euc_jp -t 245 $$i > $$o"    ""
522 \converter lyx        klyx       "python -tt $$s/lyx2lyx/lyx2lyx -c euc_kr -t 245 $$i > $$o"    ""
523 \converter clyx       lyx        "python -tt $$s/lyx2lyx/lyx2lyx -c big5 $$i > $$o"     ""
524 \converter jlyx       lyx        "python -tt $$s/lyx2lyx/lyx2lyx -c euc_jp $$i > $$o"   ""
525 \converter klyx       lyx        "python -tt $$s/lyx2lyx/lyx2lyx -c euc_kr $$i > $$o"   ""
526 ''')
527
528
529 def checkLinuxDoc():
530     ''' Check linuxdoc '''
531     #
532     path, LINUXDOC = checkProg('SGML-tools 1.x (LinuxDoc)', ['sgml2lyx'],
533         rc_entry = [
534         r'''\converter linuxdoc   lyx        "sgml2lyx $$i"     ""
535 \converter linuxdoc   latex      "sgml2latex $$i"       ""
536 \converter linuxdoc   dvi        "sgml2latex -o dvi $$i"        ""
537 \converter linuxdoc   html       "sgml2html $$i"        ""''',
538         r'''\converter linuxdoc   lyx        "" ""
539 \converter linuxdoc   latex      ""     ""
540 \converter linuxdoc   dvi        ""     ""
541 \converter linuxdoc   html       ""     ""''' ])
542     if LINUXDOC != '':
543         return ('yes', 'true', '\\def\\haslinuxdoc{yes}')
544     else:
545         return ('no', 'false', '')
546
547
548 def checkDocBook():
549     ''' Check docbook '''
550     path, DOCBOOK = checkProg('SGML-tools 2.x (DocBook) or db2x scripts', ['sgmltools', 'db2dvi'],
551         rc_entry = [
552             r'''\converter docbook    dvi        "sgmltools -b dvi $$i" ""
553 \converter docbook    html       "sgmltools -b html $$i"        ""''',
554             r'''\converter docbook    dvi        "db2dvi $$i"   ""
555 \converter docbook    html       "db2html $$i"  ""''',
556             r'''\converter docbook    dvi        ""     ""
557 \converter docbook    html       ""     ""'''])
558     #
559     if DOCBOOK != '':
560         return ('yes', 'true', '\\def\\hasdocbook{yes}')
561     else:
562         return ('no', 'false', '')
563
564
565 def checkOtherEntries():
566     ''' entries other than Format and Converter '''
567     checkProg('a *roff formatter', ['groff', 'nroff'],
568         rc_entry = [
569             r'\plaintext_roff_command "groff -t -Tlatin1 $$FName"',
570             r'\plaintext_roff_command "tbl $$FName | nroff"',
571             r'\plaintext_roff_command ""' ])
572     checkProg('ChkTeX', ['chktex -n1 -n3 -n6 -n9 -n22 -n25 -n30 -n38'],
573         rc_entry = [ r'\chktex_command "%%"' ])
574     checkProg('BibTeX', ['jbibtex', 'bibtex'],
575         rc_entry = [ r'\bibtex_command "%%"' ])
576     checkProg('an index processor', ['texindy', 'makeindex -c -q'],
577         rc_entry = [ r'\index_command "%%"' ])
578     checkProg('a spellchecker', ['ispell'],
579         rc_entry = [ r'\spell_command "%%"' ])
580     ## FIXME: OCTAVE is not used anywhere
581     # path, OCTAVE = checkProg('Octave', ['octave'])
582     ## FIXME: MAPLE is not used anywhere
583     # path, MAPLE = checkProg('Maple', ['maple'])
584     checkProg('a spool command', ['lp', 'lpr'],
585         rc_entry = [
586             r'''\print_spool_printerprefix "-d "
587 \print_spool_command "lp"''',
588             r'''\print_spool_printerprefix "-P",
589 \print_spool_command "lpr"''',
590             ''])
591     # Add the rest of the entries (no checkProg is required)
592     addToRC(r'''\copier    fig        "python -tt $$s/scripts/fig_copy.py $$i $$o"
593 \copier    pstex      "python -tt $$s/scripts/tex_copy.py $$i $$o $$l"
594 \copier    pdftex     "python -tt $$s/scripts/tex_copy.py $$i $$o $$l"
595 \copier    program    "python -tt $$s/scripts/ext_copy.py $$i $$o"
596 ''')
597
598
599 def processLayoutFile(file, bool_docbook, bool_linuxdoc):
600     ''' process layout file and get a line of result
601         
602         Declare lines look like this: (article.layout, scrbook.layout, svjog.layout)
603         
604         \DeclareLaTeXClass{article}
605         \DeclareLaTeXClass[scrbook]{book (koma-script)}
606         \DeclareLaTeXClass[svjour,svjog.clo]{article (Springer - svjour/jog)}
607
608         we expect output:
609         
610         "article" "article" "article" "false"
611         "scrbook" "scrbook" "book (koma-script)" "false"
612         "svjog" "svjour" "article (Springer - svjour/jog)" "false"
613     '''
614     classname = file.split(os.sep)[-1].split('.')[0]
615     # return ('LaTeX', '[a,b]', 'a', ',b,c', 'article') for \DeclareLaTeXClass[a,b,c]{article}
616     p = re.compile(r'\Declare(LaTeX|DocBook|LinuxDoc)Class\s*(\[([^,]*)(,.*)*\])*\s*{(.*)}')
617     for line in open(file).readlines():
618         res = p.search(line)
619         if res != None:
620             (classtype, optAll, opt, opt1, desc) = res.groups()
621             avai = {'LaTeX':'false', 'DocBook':bool_docbook, 'LinuxDoc':bool_linuxdoc}[classtype]
622             if opt == None:
623                 opt = classname
624             return '"%s" "%s" "%s" "%s"\n' % (classname, opt, desc, avai)
625     print "Layout file without \DeclareXXClass line. "
626     sys.exit(2)
627
628     
629 def checkLatexConfig(check_config, bool_docbook, bool_linuxdoc):
630     ''' Explore the LaTeX configuration 
631         Return None (will be passed to sys.exit()) for success.
632     '''
633     print 'checking LaTeX configuration... ',
634     # if --without-latex-config is forced, or if there is no previous 
635     # version of textclass.lst, re-generate a default file.
636     if not os.path.isfile('textclass.lst') or not check_config:
637         # remove the files only if we want to regenerate
638         removeFiles(['textclass.lst', 'packages.lst'])
639         #
640         # Then, generate a default textclass.lst. In case configure.py
641         # fails, we still have something to start lyx.
642         print ' default values'
643         print '+checking list of textclasses... '
644         tx = open('textclass.lst', 'w')
645         tx.write('''
646 # This file declares layouts and their associated definition files
647 # (include dir. relative to the place where this file is).
648 # It contains only default values, since chkconfig.ltx could not be run
649 # for some reason. Run ./configure.py if you need to update it after a
650 # configuration change.
651 ''')
652         # build the list of available layout files and convert it to commands
653         # for chkconfig.ltx
654         foundClasses = []
655         for file in glob.glob( os.path.join('layouts', '*.layout') ) + \
656             glob.glob( os.path.join(srcdir, 'layouts', '*.layout' ) ) :
657             # valid file?
658             if not os.path.isfile(file): 
659                 continue
660             # get stuff between /xxxx.layout .
661             classname = file.split(os.sep)[-1].split('.')[0]
662             #  tr ' -' '__'`
663             cleanclass = classname.replace(' ', '_')
664             cleanclass = cleanclass.replace('-', '_')
665             # make sure the same class is not considered twice
666             if foundClasses.count(cleanclass) == 0: # not found before
667                 foundClasses.append(cleanclass)
668                 tx.write(processLayoutFile(file, bool_docbook, bool_linuxdoc))
669         tx.close()
670         print '\tdone'
671     if not check_config:
672         return None
673     # the following will generate textclass.lst.tmp, and packages.lst.tmp
674     else:
675         print '\tauto'
676         removeFiles(['wrap_chkconfig.ltx', 'chkconfig.vars', \
677             'chkconfig.classes', 'chklayouts.tex'])
678         rmcopy = False
679         if not os.path.isfile( 'chkconfig.ltx' ):
680             shutil.copyfile( os.path.join(srcdir, 'chkconfig.ltx'), 'chkconfig.ltx' )
681             rmcopy = True
682         writeToFile('wrap_chkconfig.ltx', '%s\n%s\n\\input{chkconfig.ltx}\n' \
683             % (linuxdoc_cmd, docbook_cmd) )
684         # Construct the list of classes to test for.
685         # build the list of available layout files and convert it to commands
686         # for chkconfig.ltx
687         p1 = re.compile(r'\Declare(LaTeX|DocBook)Class')
688         testclasses = list()
689         for file in glob.glob( os.path.join('layouts', '*.layout') ) + \
690             glob.glob( os.path.join(srcdir, 'layouts', '*.layout' ) ) :
691             if not os.path.isfile(file):
692                 continue
693             classname = file.split(os.sep)[-1].split('.')[0]
694             for line in open(file).readlines():
695                 if p1.search(line) == None:
696                     continue
697                 if line[0] != '#':
698                     print "Wrong input layout file with line '" + line
699                     sys.exit(3)
700                 testclasses.append("\\TestDocClass{%s}{%s}" % (classname, line[1:].strip()))
701                 break
702         testclasses.sort()
703         cl = open('chklayouts.tex', 'w')
704         for line in testclasses:
705             cl.write(line + '\n')
706         cl.close()
707         #
708         # we have chklayouts.tex, then process it
709         fout = os.popen(LATEX + ' wrap_chkconfig.ltx')
710         while True:
711             line = fout.readline()
712             if not line:
713                 break;
714             if re.match('^\+', line):
715                 print line,
716         # if the command succeeds, None will be returned
717         ret = fout.close()
718         #
719         # currently, values in chhkconfig are only used to set
720         # \font_encoding
721         values = {}
722         for line in open('chkconfig.vars').readlines():
723             key, val = re.sub('-', '_', line).split('=')
724             val = val.strip()
725             values[key] = val.strip("'")
726         # chk_fontenc may not exist 
727         try:
728             addToRC(r'\font_encoding "%s"' % values["chk_fontenc"])
729         except:
730             pass
731         if rmcopy:   # remove the copied file
732             removeFiles( [ 'chkconfig.ltx' ] )
733         # if configure successed, move textclass.lst.tmp to textclass.lst
734         # and packages.lst.tmp to packages.lst
735         if os.path.isfile('textclass.lst.tmp') and len(open('textclass.lst.tmp').read()) > 0 \
736             and os.path.isfile('packages.lst.tmp') and len(open('packages.lst.tmp').read()) > 0:
737             shutil.move('textclass.lst.tmp', 'textclass.lst')
738             shutil.move('packages.lst.tmp', 'packages.lst')
739         return ret
740
741
742 def checkModulesConfig():
743   removeFiles(['lyxmodules.lst'])
744
745   print '+checking list of modules... '
746   tx = open('lyxmodules.lst', 'w')
747   tx.write('''## This file declares modules and their associated definition files.
748 ## It has been automatically generated by configure
749 ## Use "Options/Reconfigure" if you need to update it after a
750 ## configuration change. 
751 ''')
752   # build the list of available modules
753   foundClasses = []
754   for file in glob.glob( os.path.join('layouts', '*.module') ) + \
755       glob.glob( os.path.join(srcdir, 'layouts', '*.module' ) ) :
756       # valid file?
757       print file
758       if not os.path.isfile(file): 
759           continue
760       tx.write(processModuleFile(file, bool_docbook, bool_linuxdoc))
761   tx.close()
762   print '\tdone'
763
764
765 def processModuleFile(file, bool_docbook, bool_linuxdoc):
766     ''' process module file and get a line of result
767
768         The top of a module file should look like this:
769           #\DeclareLyXModule[LaTeX Packages]{ModuleName}
770           #BeginDescription
771           #...body of description...
772           #EndDescription
773           #Requires: [list of required modules]
774           #Excludes: [list of excluded modules]
775         The last two lines are optional
776         We expect output:
777           "ModuleName" "filename" "Description" "Packages" "Requires" "Excludes"
778     '''
779     p = re.compile(r'\DeclareLyXModule\s*(?:\[([^]]*?)\])?{(.*)}')
780     r = re.compile(r'#+\s*Requires: (.*)')
781     x = re.compile(r'#+\s*Excludes: (.*)')
782     b = re.compile(r'#+\s*DescriptionBegin\s*$')
783     e = re.compile(r'#+\s*DescriptionEnd\s*$')
784
785     modname = desc = pkgs = req = excl = ""
786     readingDescription = False
787     descLines = []
788     filename = file.split(os.sep)[-1]
789     filename = filename[:-7]
790
791     for line in open(file).readlines():
792       if readingDescription:
793         res = e.search(line)
794         if res != None:
795           readingDescription = False
796           desc = " ".join(descLines)
797           continue
798         descLines.append(line[1:].strip())
799         continue
800       res = b.search(line)
801       if res != None:
802         readingDescription = True
803         continue
804       res = p.search(line)
805       if res != None:
806           (pkgs, modname) = res.groups()
807           if pkgs == None:
808             pkgs = ""
809           else:
810             tmp = [s.strip() for s in pkgs.split(",")]
811             pkgs = ",".join(tmp)
812           continue
813       res = r.search(line)
814       if res != None:
815         req = res.group(1)
816         tmp = [s.strip() for s in req.split("|")]
817         req = "|".join(tmp)
818         continue
819       res = x.search(line)
820       if res != None:
821         excl = res.group(1)
822         tmp = [s.strip() for s in excl.split("|")]
823         excl = "|".join(tmp)
824         continue
825     if modname != "":
826         return '"%s" "%s" "%s" "%s" "%s" "%s"\n' % (modname, filename, desc, pkgs, req, excl)
827     print "Module file without \DeclareLyXModule line. "
828     sys.exit(2)
829
830
831 def checkTeXAllowSpaces():
832     ''' Let's check whether spaces are allowed in TeX file names '''
833     tex_allows_spaces = 'false'
834     if lyx_check_config:
835         print "Checking whether TeX allows spaces in file names... ",
836         writeToFile('a b.tex', r'\message{working^^J}' )
837         if os.name == 'nt':
838             latex_out = cmdOutput(LATEX + r""" "\nonstopmode\input{\"a b\"}" """)
839         else:
840             latex_out = cmdOutput(LATEX + r""" '\nonstopmode\input{"a b"}' """)
841         if 'working' in latex_out:
842             print 'yes'
843             tex_allows_spaces = 'true'
844         else:
845             print 'no'
846             tex_allows_spaces = 'false'
847         addToRC(r'\tex_allows_spaces ' + tex_allows_spaces)
848         removeFiles( [ 'a b.tex', 'a b.log', 'texput.log' ])
849
850
851 def removeTempFiles():
852     # Final clean-up
853     if not lyx_keep_temps:
854         removeFiles(['chkconfig.vars',  \
855             'wrap_chkconfig.ltx', 'wrap_chkconfig.log', \
856             'chklayouts.tex', 'missfont.log', 
857             'chklatex.ltx', 'chklatex.log'])
858
859
860 if __name__ == '__main__':
861     lyx_check_config = True
862     outfile = 'lyxrc.defaults'
863     rc_entries = ''
864     lyx_keep_temps = False
865     version_suffix = ''
866     logfile = 'configure.log'
867     ## Parse the command line
868     for op in sys.argv[1:]:   # default shell/for list is $*, the options
869         if op in [ '-help', '--help', '-h' ]:
870             print '''Usage: configure [options]
871 Options:
872     --help                   show this help lines
873     --keep-temps             keep temporary files (for debug. purposes)
874     --without-latex-config   do not run LaTeX to determine configuration
875     --with-version-suffix=suffix suffix of binary installed files
876 '''
877             sys.exit(0)
878         elif op == '--without-latex-config':
879             lyx_check_config = False
880         elif op == '--keep-temps':
881             lyx_keep_temps = True
882         elif op[0:22] == '--with-version-suffix=':  # never mind if op is not long enough
883             version_suffix = op[22:]
884         else:
885             print "Unknown option", op
886             sys.exit(1)
887     #
888     # set up log file for stdout and stderr
889     log = open(logfile, 'w')
890     sys.stdout = Tee(sys.stdout, log)
891     sys.stderr = Tee(sys.stderr, log)
892     # check if we run from the right directory
893     srcdir = os.path.dirname(sys.argv[0])
894     if srcdir == '':
895         srcdir = '.'
896     if not os.path.isfile( os.path.join(srcdir, 'chkconfig.ltx') ):
897         print "configure: error: cannot find chkconfig.ltx script"
898         sys.exit(1)
899     setEnviron()
900     createDirectories()
901     windows_style_tex_paths = checkTeXPaths()
902     dtl_tools = checkDTLtools()
903     ## Write the first part of outfile
904     writeToFile(outfile, '''# This file has been automatically generated by LyX' lib/configure.py
905 # script. It contains default settings that have been determined by
906 # examining your system. PLEASE DO NOT MODIFY ANYTHING HERE! If you
907 # want to customize LyX, use LyX' Preferences dialog or modify directly 
908 # the "preferences" file instead. Any setting in that file will
909 # override the values given here.
910 ''')
911     # check latex
912     LATEX = checkLatex(dtl_tools)
913     checkFormatEntries(dtl_tools)
914     checkConverterEntries()
915     (chk_linuxdoc, bool_linuxdoc, linuxdoc_cmd) = checkLinuxDoc()
916     (chk_docbook, bool_docbook, docbook_cmd) = checkDocBook()
917     checkTeXAllowSpaces()
918     if windows_style_tex_paths != '':
919         addToRC(r'\tex_expects_windows_paths %s' % windows_style_tex_paths)
920     checkOtherEntries()
921     # --without-latex-config can disable lyx_check_config
922     ret = checkLatexConfig(lyx_check_config and LATEX != '',
923         bool_docbook, bool_linuxdoc)
924     checkModulesConfig() #lyx_check_config and LATEX != '')
925     removeTempFiles()
926     # The return error code can be 256. Because most systems expect an error code
927     # in the range 0-127, 256 can be interpretted as 'success'. Because we expect
928     # a None for success, 'ret is not None' is used to exit.
929     sys.exit(ret is not None)